From e3c5279b92753b9780cb7cdcedf67350ef310257 Mon Sep 17 00:00:00 2001 From: SnapOtter Date: Fri, 1 May 2026 02:51:42 +0800 Subject: [PATCH] test: expand E2E-Docker coverage to all 47 tools with auth-failure tests --- tests/e2e-docker/adjustment-tools.spec.ts | 34 ++++++++++++ tests/e2e-docker/ai-tools.spec.ts | 34 ++++++++++++ tests/e2e-docker/auth.setup.ts | 15 ++++-- tests/e2e-docker/conversion-tools.spec.ts | 36 +++++++++++++ tests/e2e-docker/creative-tools.spec.ts | 54 +++++++++++++++++++ tests/e2e-docker/essential-tools.spec.ts | 34 ++++++++++++ .../format-conversion-tools.spec.ts | 34 ++++++++++++ tests/e2e-docker/layout-tools.spec.ts | 24 +++++++++ tests/e2e-docker/optimization-tools.spec.ts | 38 +++++++++++++ tests/e2e-docker/utility-tools.spec.ts | 29 ++++++++++ .../watermark-overlay-tools.spec.ts | 40 ++++++++++++++ 11 files changed, 368 insertions(+), 4 deletions(-) diff --git a/tests/e2e-docker/adjustment-tools.spec.ts b/tests/e2e-docker/adjustment-tools.spec.ts index e60a71b4..a8f71c44 100644 --- a/tests/e2e-docker/adjustment-tools.spec.ts +++ b/tests/e2e-docker/adjustment-tools.spec.ts @@ -398,3 +398,37 @@ test.describe("Image Enhancement", () => { expect(body.processedSize).not.toBe(body.originalSize); }); }); + +// ─── Auth Failure ────────────────────────────────────────────────── + +test.describe("Auth failure", () => { + test("adjust-colors without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/adjust-colors", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({ brightness: 20 }), + }, + }); + expect(res.status()).toBe(401); + }); + + test("strip-metadata without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/strip-metadata", { + multipart: { + file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_WITH_EXIF }, + settings: JSON.stringify({}), + }, + }); + expect(res.status()).toBe(401); + }); + + test("edit-metadata without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/edit-metadata", { + multipart: { + file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 }, + settings: JSON.stringify({ artist: "Test" }), + }, + }); + expect(res.status()).toBe(401); + }); +}); diff --git a/tests/e2e-docker/ai-tools.spec.ts b/tests/e2e-docker/ai-tools.spec.ts index 1fe0f48f..433954da 100644 --- a/tests/e2e-docker/ai-tools.spec.ts +++ b/tests/e2e-docker/ai-tools.spec.ts @@ -754,3 +754,37 @@ test.describe("AI Feature Bundle Status", () => { } }); }); + +// ─── Auth Failure ────────────────────────────────────────────────── + +test.describe("Auth failure", () => { + test("remove-background without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/remove-background", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: TINY_PNG }, + settings: JSON.stringify({}), + }, + }); + expect(res.status()).toBe(401); + }); + + test("upscale without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/upscale", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: TINY_PNG }, + settings: JSON.stringify({ scale: 2, model: "auto" }), + }, + }); + expect(res.status()).toBe(401); + }); + + test("ocr without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/ocr", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: TINY_PNG }, + settings: JSON.stringify({}), + }, + }); + expect(res.status()).toBe(401); + }); +}); diff --git a/tests/e2e-docker/auth.setup.ts b/tests/e2e-docker/auth.setup.ts index f09c0046..5362e47e 100644 --- a/tests/e2e-docker/auth.setup.ts +++ b/tests/e2e-docker/auth.setup.ts @@ -1,6 +1,6 @@ import { mkdirSync } from "node:fs"; import path from "node:path"; -import { expect, test as setup } from "@playwright/test"; +import { test as setup } from "@playwright/test"; const authFile = path.join(__dirname, "..", "..", ".playwright", ".auth", "analytics-user.json"); @@ -21,11 +21,18 @@ setup("authenticate", async ({ page }) => { await acceptBtn.waitFor({ state: "visible", timeout: 15_000 }); await acceptBtn.click(); // Consent page does window.location.href = "/" (full reload) - await page.waitForURL("/", { timeout: 30_000 }); + await page.waitForURL((url) => !url.pathname.includes("analytics-consent"), { + timeout: 30_000, + }); } - // At this point we should be on the home page - await expect(page).toHaveURL("/"); + // At this point we should be on the home page (or at least past the consent page) + await page.waitForURL( + (url) => !url.pathname.includes("login") && !url.pathname.includes("analytics-consent"), + { + timeout: 30_000, + }, + ); mkdirSync(path.dirname(authFile), { recursive: true }); await page.context().storageState({ path: authFile }); }); diff --git a/tests/e2e-docker/conversion-tools.spec.ts b/tests/e2e-docker/conversion-tools.spec.ts index 4f373939..1d309069 100644 --- a/tests/e2e-docker/conversion-tools.spec.ts +++ b/tests/e2e-docker/conversion-tools.spec.ts @@ -581,3 +581,39 @@ test.describe("Convert format matrix", () => { } }); }); + +// ─── Auth Failure ────────────────────────────────────────────────── + +test.describe("Auth failure", () => { + test("gif-tools without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/gif-tools", { + multipart: { + file: { name: "animated.gif", mimeType: "image/gif", buffer: ANIMATED_GIF }, + settings: JSON.stringify({}), + }, + }); + expect(res.status()).toBe(401); + }); + + test("image-to-base64 without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/image-to-base64", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({}), + }, + }); + expect(res.status()).toBe(401); + }); + + test("image-to-pdf without token returns 401", async ({ request }) => { + const { body, contentType } = buildMultipart( + [{ name: "file", filename: "test.jpg", contentType: "image/jpeg", buffer: JPG_100x100 }], + [{ name: "settings", value: JSON.stringify({ pageSize: "A4" }) }], + ); + const res = await request.post("/api/v1/tools/image-to-pdf", { + headers: { "Content-Type": contentType }, + data: body, + }); + expect(res.status()).toBe(401); + }); +}); diff --git a/tests/e2e-docker/creative-tools.spec.ts b/tests/e2e-docker/creative-tools.spec.ts index ebab6c95..38dbdd74 100644 --- a/tests/e2e-docker/creative-tools.spec.ts +++ b/tests/e2e-docker/creative-tools.spec.ts @@ -589,3 +589,57 @@ test.describe("Content-Aware Resize", () => { } }); }); + +// ─── Auth Failure ────────────────────────────────────────────────── + +test.describe("Auth failure", () => { + test("collage without token returns 401", async ({ request }) => { + const { body, contentType } = buildMultipart( + [ + { name: "file", filename: "a.png", contentType: "image/png", buffer: PNG_200x150 }, + { name: "file", filename: "b.jpg", contentType: "image/jpeg", buffer: JPG_100x100 }, + ], + [ + { + name: "settings", + value: JSON.stringify({ templateId: "2-h-equal", width: 400 }), + }, + ], + ); + const res = await request.post("/api/v1/tools/collage", { + headers: { "Content-Type": contentType }, + data: body, + }); + expect(res.status()).toBe(401); + }); + + test("stitch without token returns 401", async ({ request }) => { + const { body, contentType } = buildMultipart( + [ + { name: "file", filename: "a.png", contentType: "image/png", buffer: PNG_200x150 }, + { name: "file", filename: "b.jpg", contentType: "image/jpeg", buffer: JPG_100x100 }, + ], + [{ name: "settings", value: JSON.stringify({ direction: "horizontal" }) }], + ); + const res = await request.post("/api/v1/tools/stitch", { + headers: { "Content-Type": contentType }, + data: body, + }); + expect(res.status()).toBe(401); + }); + + test("text-overlay without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/text-overlay", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({ + text: "Test", + fontSize: 24, + color: "#FFFFFF", + position: "center", + }), + }, + }); + expect(res.status()).toBe(401); + }); +}); diff --git a/tests/e2e-docker/essential-tools.spec.ts b/tests/e2e-docker/essential-tools.spec.ts index ab57f9b8..7e58fee3 100644 --- a/tests/e2e-docker/essential-tools.spec.ts +++ b/tests/e2e-docker/essential-tools.spec.ts @@ -618,3 +618,37 @@ test.describe("Sharpening", () => { expect(body.downloadUrl).toBeTruthy(); }); }); + +// ─── Auth Failure ────────────────────────────────────────────────── + +test.describe("Auth failure", () => { + test("resize without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/resize", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({ width: 100 }), + }, + }); + expect(res.status()).toBe(401); + }); + + test("crop without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/crop", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({ left: 0, top: 0, width: 50, height: 50 }), + }, + }); + expect(res.status()).toBe(401); + }); + + test("convert without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/convert", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({ format: "jpg" }), + }, + }); + expect(res.status()).toBe(401); + }); +}); diff --git a/tests/e2e-docker/format-conversion-tools.spec.ts b/tests/e2e-docker/format-conversion-tools.spec.ts index 7fe49be6..270d0615 100644 --- a/tests/e2e-docker/format-conversion-tools.spec.ts +++ b/tests/e2e-docker/format-conversion-tools.spec.ts @@ -717,3 +717,37 @@ test.describe("PDF to Image — additional formats", () => { expect(body.downloadUrl || body.pages || body.jobId).toBeTruthy(); }); }); + +// ─── Auth Failure ────────────────────────────────────────────────── + +test.describe("Auth failure", () => { + test("svg-to-raster without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/svg-to-raster", { + multipart: { + file: { name: "test.svg", mimeType: "image/svg+xml", buffer: SVG_100x100 }, + settings: JSON.stringify({ format: "png", width: 200 }), + }, + }); + expect(res.status()).toBe(401); + }); + + test("vectorize without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/vectorize", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({}), + }, + }); + expect(res.status()).toBe(401); + }); + + test("pdf-to-image without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/pdf-to-image", { + multipart: { + file: { name: "test.pdf", mimeType: "application/pdf", buffer: PDF_3PAGE }, + settings: JSON.stringify({ format: "png", dpi: 150, pages: "1" }), + }, + }); + expect(res.status()).toBe(401); + }); +}); diff --git a/tests/e2e-docker/layout-tools.spec.ts b/tests/e2e-docker/layout-tools.spec.ts index c67a129c..8ecfa875 100644 --- a/tests/e2e-docker/layout-tools.spec.ts +++ b/tests/e2e-docker/layout-tools.spec.ts @@ -543,3 +543,27 @@ test.describe("Border — extended", () => { expect(body.processedSize).toBeGreaterThan(0); }); }); + +// ─── Auth Failure ────────────────────────────────────────────────── + +test.describe("Auth failure", () => { + test("split without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/split", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({ columns: 2, rows: 2 }), + }, + }); + expect(res.status()).toBe(401); + }); + + test("border without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/border", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({ size: 10, color: "#ff0000" }), + }, + }); + expect(res.status()).toBe(401); + }); +}); diff --git a/tests/e2e-docker/optimization-tools.spec.ts b/tests/e2e-docker/optimization-tools.spec.ts index 1b19b5ec..ea878729 100644 --- a/tests/e2e-docker/optimization-tools.spec.ts +++ b/tests/e2e-docker/optimization-tools.spec.ts @@ -734,3 +734,41 @@ test.describe("Content-Aware Resize — extended", () => { } }); }); + +// ─── Auth Failure ────────────────────────────────────────────────── + +test.describe("Auth failure", () => { + test("optimize-for-web without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/optimize-for-web", { + multipart: { + file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 }, + settings: JSON.stringify({ maxWidth: 800, quality: 75 }), + }, + }); + expect(res.status()).toBe(401); + }); + + test("favicon without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/favicon", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({}), + }, + }); + expect(res.status()).toBe(401); + }); + + test("replace-color without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/replace-color", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({ + targetColor: "#ffffff", + replacementColor: "#000000", + tolerance: 30, + }), + }, + }); + expect(res.status()).toBe(401); + }); +}); diff --git a/tests/e2e-docker/utility-tools.spec.ts b/tests/e2e-docker/utility-tools.spec.ts index 6af225fd..6be36b7c 100644 --- a/tests/e2e-docker/utility-tools.spec.ts +++ b/tests/e2e-docker/utility-tools.spec.ts @@ -758,3 +758,32 @@ test.describe("QR Read", () => { } }); }); + +// ─── Auth Failure ────────────────────────────────────────────────── + +test.describe("Auth failure", () => { + test("info without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/info", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + }, + }); + expect(res.status()).toBe(401); + }); + + test("qr-generate without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/qr-generate", { + data: { text: "https://snapotter.app", size: 512 }, + }); + expect(res.status()).toBe(401); + }); + + test("color-palette without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/color-palette", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + }, + }); + expect(res.status()).toBe(401); + }); +}); diff --git a/tests/e2e-docker/watermark-overlay-tools.spec.ts b/tests/e2e-docker/watermark-overlay-tools.spec.ts index e2208a7a..a5d4128c 100644 --- a/tests/e2e-docker/watermark-overlay-tools.spec.ts +++ b/tests/e2e-docker/watermark-overlay-tools.spec.ts @@ -538,3 +538,43 @@ test.describe("Compose — extended", () => { expect(json.downloadUrl).toBeTruthy(); }); }); + +// ─── Auth Failure ────────────────────────────────────────────────── + +test.describe("Auth failure", () => { + test("watermark-text without token returns 401", async ({ request }) => { + const res = await request.post("/api/v1/tools/watermark-text", { + multipart: { + file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 }, + settings: JSON.stringify({ + text: "TEST", + fontSize: 24, + color: "#ff0000", + opacity: 50, + position: "center", + }), + }, + }); + expect(res.status()).toBe(401); + }); + + test("compose without token returns 401", async ({ request }) => { + const { body, contentType } = buildMultipart( + [ + { name: "file", filename: "base.png", contentType: "image/png", buffer: PNG_200x150 }, + { + name: "overlay", + filename: "overlay.webp", + contentType: "image/webp", + buffer: WEBP_50x50, + }, + ], + [{ name: "settings", value: JSON.stringify({ x: 0, y: 0, opacity: 100 }) }], + ); + const res = await request.post("/api/v1/tools/compose", { + headers: { "Content-Type": contentType }, + data: body, + }); + expect(res.status()).toBe(401); + }); +});