mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
Add ~500 new E2E tests and ~300 new integration tests covering: - 24 new GUI E2E specs: navigation, responsive layout, keyboard shortcuts, tool UI for all 35 non-AI tools, batch/pipeline workflows, settings/RBAC, visual regression, accessibility, and performance budgets - 3 new E2E-Docker specs: batch workflows, advanced pipelines, cross-format - 1 new adversarial integration test: memory pressure, corrupted files, unicode filenames, extreme dimensions, pipeline/batch edge cases - 29 expanded integration test files: HEIC/HEIF input, large files, parameter boundaries, batch processing, format edge cases across all tools - Cross-format matrix expanded: 641 tests covering every tool x 18 formats - AI bridge unit tests expanded: lifecycle, tool modules, error propagation - Unit test gaps filled: analytics, tool-registry, web stores Also fixes: - vitest.config.ts: exclude e2e-docs and e2e-landing from Vitest runner - AI E2E specs: add sidecar health check to skip gracefully when Python AI backend is not running instead of timing out
916 lines
32 KiB
TypeScript
916 lines
32 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { expect, test } from "@playwright/test";
|
|
|
|
// ─── Cross-Format Docker Tests ────────────────────────────────────
|
|
// Tests key tools against multiple input formats through the real
|
|
// Docker container with real Sharp processing. Verifies actual
|
|
// output dimensions, format, and file size.
|
|
|
|
const FIXTURES = join(process.cwd(), "tests", "fixtures");
|
|
const FORMATS = join(FIXTURES, "formats");
|
|
|
|
let token: string;
|
|
|
|
test.beforeAll(async ({ request }) => {
|
|
const res = await request.post("/api/auth/login", {
|
|
data: { username: "admin", password: "admin" },
|
|
});
|
|
const body = await res.json();
|
|
token = body.token;
|
|
});
|
|
|
|
function fixture(name: string): Buffer {
|
|
return readFileSync(join(FIXTURES, name));
|
|
}
|
|
|
|
function formatFixture(name: string): Buffer {
|
|
return readFileSync(join(FORMATS, name));
|
|
}
|
|
|
|
const PNG_200x150 = fixture("test-200x150.png");
|
|
const JPG_100x100 = fixture("test-100x100.jpg");
|
|
const WEBP_50x50 = fixture("test-50x50.webp");
|
|
const HEIC_200x150 = fixture("test-200x150.heic");
|
|
|
|
// ─── Resize: Format Matrix ───────────────────────────────────────
|
|
|
|
test.describe("Resize: JPEG", () => {
|
|
test("resize JPEG to 50px wide", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/resize", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
|
|
settings: JSON.stringify({ width: 50, fit: "contain" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Resize: PNG", () => {
|
|
test("resize PNG to 80px wide", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/resize", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
|
|
settings: JSON.stringify({ width: 80, fit: "contain" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Resize: WebP", () => {
|
|
test("resize WebP to 30px wide", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/resize", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.webp", mimeType: "image/webp", buffer: WEBP_50x50 },
|
|
settings: JSON.stringify({ width: 30, fit: "contain" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Resize: AVIF", () => {
|
|
test("resize AVIF to 60px wide", async ({ request }) => {
|
|
const avif = formatFixture("sample.avif");
|
|
const res = await request.post("/api/v1/tools/resize", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.avif", mimeType: "image/avif", buffer: avif },
|
|
settings: JSON.stringify({ width: 60, fit: "contain" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Resize: HEIC", () => {
|
|
test("resize HEIC to 80px wide", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/resize", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.heic", mimeType: "image/heic", buffer: HEIC_200x150 },
|
|
settings: JSON.stringify({ width: 80, fit: "contain" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Resize: TIFF", () => {
|
|
test("resize TIFF to 60px wide", async ({ request }) => {
|
|
const tiff = formatFixture("sample.tiff");
|
|
const res = await request.post("/api/v1/tools/resize", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.tiff", mimeType: "image/tiff", buffer: tiff },
|
|
settings: JSON.stringify({ width: 60, fit: "contain" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Resize: BMP", () => {
|
|
test("resize BMP to 40px wide", async ({ request }) => {
|
|
const bmp = formatFixture("sample.bmp");
|
|
const res = await request.post("/api/v1/tools/resize", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.bmp", mimeType: "image/bmp", buffer: bmp },
|
|
settings: JSON.stringify({ width: 40, fit: "contain" }),
|
|
},
|
|
});
|
|
// BMP may not be supported by Sharp natively
|
|
if (res.ok()) {
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
} else {
|
|
expect([400, 422]).toContain(res.status());
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe("Resize: GIF", () => {
|
|
test("resize GIF to 40px wide", async ({ request }) => {
|
|
const gif = formatFixture("sample.gif");
|
|
const res = await request.post("/api/v1/tools/resize", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.gif", mimeType: "image/gif", buffer: gif },
|
|
settings: JSON.stringify({ width: 40, fit: "contain" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
// ─── Convert: From Each Format to PNG ─────────────────────────────
|
|
|
|
test.describe("Convert to PNG: from JPEG", () => {
|
|
test("JPEG to PNG", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
|
|
settings: JSON.stringify({ format: "png" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".png");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Convert to PNG: from WebP", () => {
|
|
test("WebP to PNG", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.webp", mimeType: "image/webp", buffer: WEBP_50x50 },
|
|
settings: JSON.stringify({ format: "png" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".png");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Convert to PNG: from AVIF", () => {
|
|
test("AVIF to PNG", async ({ request }) => {
|
|
const avif = formatFixture("sample.avif");
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.avif", mimeType: "image/avif", buffer: avif },
|
|
settings: JSON.stringify({ format: "png" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".png");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Convert to PNG: from HEIC", () => {
|
|
test("HEIC to PNG", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.heic", mimeType: "image/heic", buffer: HEIC_200x150 },
|
|
settings: JSON.stringify({ format: "png" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".png");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Convert to PNG: from TIFF", () => {
|
|
test("TIFF to PNG", async ({ request }) => {
|
|
const tiff = formatFixture("sample.tiff");
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.tiff", mimeType: "image/tiff", buffer: tiff },
|
|
settings: JSON.stringify({ format: "png" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".png");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Convert to PNG: from GIF", () => {
|
|
test("GIF to PNG", async ({ request }) => {
|
|
const gif = formatFixture("sample.gif");
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.gif", mimeType: "image/gif", buffer: gif },
|
|
settings: JSON.stringify({ format: "png" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".png");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Convert to PNG: from HEIF", () => {
|
|
test("HEIF to PNG", async ({ request }) => {
|
|
const heif = formatFixture("sample.heif");
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.heif", mimeType: "image/heif", buffer: heif },
|
|
settings: JSON.stringify({ format: "png" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".png");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Convert to PNG: from BMP", () => {
|
|
test("BMP to PNG", async ({ request }) => {
|
|
const bmp = formatFixture("sample.bmp");
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.bmp", mimeType: "image/bmp", buffer: bmp },
|
|
settings: JSON.stringify({ format: "png" }),
|
|
},
|
|
});
|
|
// BMP decode may not be supported
|
|
if (res.ok()) {
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".png");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
} else {
|
|
expect([400, 422]).toContain(res.status());
|
|
}
|
|
});
|
|
});
|
|
|
|
// ─── Compress: Format Matrix ──────────────────────────────────────
|
|
|
|
test.describe("Compress: JPEG", () => {
|
|
test("compress JPEG with quality 30", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/compress", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.jpg", mimeType: "image/jpeg", buffer: formatFixture("sample.jpg") },
|
|
settings: JSON.stringify({ quality: 30 }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
expect(body.processedSize).toBeLessThan(body.originalSize);
|
|
});
|
|
});
|
|
|
|
test.describe("Compress: PNG", () => {
|
|
test("compress PNG with quality 40", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/compress", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
|
|
settings: JSON.stringify({ quality: 40 }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Compress: WebP", () => {
|
|
test("compress WebP with quality 25", async ({ request }) => {
|
|
const webp = formatFixture("sample.webp");
|
|
const res = await request.post("/api/v1/tools/compress", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.webp", mimeType: "image/webp", buffer: webp },
|
|
settings: JSON.stringify({ quality: 25 }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Compress: AVIF", () => {
|
|
test("compress AVIF with quality 40", async ({ request }) => {
|
|
const avif = formatFixture("sample.avif");
|
|
const res = await request.post("/api/v1/tools/compress", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.avif", mimeType: "image/avif", buffer: avif },
|
|
settings: JSON.stringify({ quality: 40 }),
|
|
},
|
|
});
|
|
// AVIF compression may not be supported
|
|
if (res.ok()) {
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
} else {
|
|
expect([400, 422]).toContain(res.status());
|
|
}
|
|
});
|
|
});
|
|
|
|
test.describe("Compress: HEIC", () => {
|
|
test("compress HEIC with quality 50", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/compress", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.heic", mimeType: "image/heic", buffer: HEIC_200x150 },
|
|
settings: JSON.stringify({ quality: 50 }),
|
|
},
|
|
});
|
|
// HEIC compression may not be directly supported
|
|
if (res.ok()) {
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toBeTruthy();
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
} else {
|
|
expect([400, 422]).toContain(res.status());
|
|
}
|
|
});
|
|
});
|
|
|
|
// ─── Info: Every Format ───────────────────────────────────────────
|
|
|
|
test.describe("Info: JPEG", () => {
|
|
test("get info for JPEG returns correct dimensions", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.width).toBe(100);
|
|
expect(body.height).toBe(100);
|
|
expect(body.format).toBe("jpeg");
|
|
expect(body.fileSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Info: PNG", () => {
|
|
test("get info for PNG returns correct dimensions", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.width).toBe(200);
|
|
expect(body.height).toBe(150);
|
|
expect(body.format).toBe("png");
|
|
expect(body.fileSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Info: WebP", () => {
|
|
test("get info for WebP returns correct dimensions", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.webp", mimeType: "image/webp", buffer: WEBP_50x50 },
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.width).toBe(50);
|
|
expect(body.height).toBe(50);
|
|
expect(body.format).toBe("webp");
|
|
expect(body.fileSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Info: AVIF", () => {
|
|
test("get info for AVIF returns dimensions and format", async ({ request }) => {
|
|
const avif = formatFixture("sample.avif");
|
|
const res = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.avif", mimeType: "image/avif", buffer: avif },
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.width).toBeGreaterThan(0);
|
|
expect(body.height).toBeGreaterThan(0);
|
|
expect(body.fileSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Info: HEIC", () => {
|
|
test("get info for HEIC returns dimensions", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.heic", mimeType: "image/heic", buffer: HEIC_200x150 },
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.width).toBeGreaterThan(0);
|
|
expect(body.height).toBeGreaterThan(0);
|
|
expect(body.fileSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Info: TIFF", () => {
|
|
test("get info for TIFF returns dimensions and format", async ({ request }) => {
|
|
const tiff = formatFixture("sample.tiff");
|
|
const res = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.tiff", mimeType: "image/tiff", buffer: tiff },
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.width).toBeGreaterThan(0);
|
|
expect(body.height).toBeGreaterThan(0);
|
|
expect(body.fileSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Info: GIF", () => {
|
|
test("get info for GIF returns dimensions and format", async ({ request }) => {
|
|
const gif = formatFixture("sample.gif");
|
|
const res = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.gif", mimeType: "image/gif", buffer: gif },
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.width).toBeGreaterThan(0);
|
|
expect(body.height).toBeGreaterThan(0);
|
|
expect(body.format).toBe("gif");
|
|
expect(body.fileSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Info: HEIF", () => {
|
|
test("get info for HEIF returns dimensions", async ({ request }) => {
|
|
const heif = formatFixture("sample.heif");
|
|
const res = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.heif", mimeType: "image/heif", buffer: heif },
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.width).toBeGreaterThan(0);
|
|
expect(body.height).toBeGreaterThan(0);
|
|
expect(body.fileSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Info: BMP", () => {
|
|
test("get info for BMP returns dimensions if supported", async ({ request }) => {
|
|
const bmp = formatFixture("sample.bmp");
|
|
const res = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "sample.bmp", mimeType: "image/bmp", buffer: bmp },
|
|
},
|
|
});
|
|
if (res.ok()) {
|
|
const body = await res.json();
|
|
expect(body.width).toBeGreaterThan(0);
|
|
expect(body.height).toBeGreaterThan(0);
|
|
expect(body.fileSize).toBeGreaterThan(0);
|
|
} else {
|
|
expect([400, 422]).toContain(res.status());
|
|
}
|
|
});
|
|
});
|
|
|
|
// ─── Cross-Format Conversions: Full Matrix ────────────────────────
|
|
|
|
test.describe("Cross-format: JPEG to all targets", () => {
|
|
test("JPEG to WebP", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
|
|
settings: JSON.stringify({ format: "webp" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".webp");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("JPEG to AVIF", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
|
|
settings: JSON.stringify({ format: "avif" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".avif");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("JPEG to TIFF", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
|
|
settings: JSON.stringify({ format: "tiff" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".tiff");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("JPEG to GIF", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
|
|
settings: JSON.stringify({ format: "gif" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".gif");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("JPEG to HEIC", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
|
|
settings: JSON.stringify({ format: "heic" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".heic");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Cross-format: PNG to all targets", () => {
|
|
test("PNG to JPEG", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
|
|
settings: JSON.stringify({ format: "jpg", quality: 85 }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".jpg");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("PNG to WebP", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
|
|
settings: JSON.stringify({ format: "webp" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".webp");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("PNG to AVIF", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
|
|
settings: JSON.stringify({ format: "avif" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".avif");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Cross-format: WebP to all targets", () => {
|
|
test("WebP to JPEG", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.webp", mimeType: "image/webp", buffer: WEBP_50x50 },
|
|
settings: JSON.stringify({ format: "jpg" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".jpg");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("WebP to AVIF", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.webp", mimeType: "image/webp", buffer: WEBP_50x50 },
|
|
settings: JSON.stringify({ format: "avif" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".avif");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("WebP to TIFF", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.webp", mimeType: "image/webp", buffer: WEBP_50x50 },
|
|
settings: JSON.stringify({ format: "tiff" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".tiff");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
test.describe("Cross-format: HEIC to all targets", () => {
|
|
test("HEIC to JPEG", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.heic", mimeType: "image/heic", buffer: HEIC_200x150 },
|
|
settings: JSON.stringify({ format: "jpg" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".jpg");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("HEIC to WebP", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.heic", mimeType: "image/heic", buffer: HEIC_200x150 },
|
|
settings: JSON.stringify({ format: "webp" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".webp");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("HEIC to AVIF", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.heic", mimeType: "image/heic", buffer: HEIC_200x150 },
|
|
settings: JSON.stringify({ format: "avif" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".avif");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("HEIC to TIFF", async ({ request }) => {
|
|
const res = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.heic", mimeType: "image/heic", buffer: HEIC_200x150 },
|
|
settings: JSON.stringify({ format: "tiff" }),
|
|
},
|
|
});
|
|
expect(res.ok()).toBe(true);
|
|
const body = await res.json();
|
|
expect(body.downloadUrl).toContain(".tiff");
|
|
expect(body.processedSize).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
// ─── Resize + Info Verification ───────────────────────────────────
|
|
|
|
test.describe("Resize + info verification: JPEG", () => {
|
|
test("resize JPEG then verify dimensions via info", async ({ request }) => {
|
|
// Step 1: Resize
|
|
const resizeRes = await request.post("/api/v1/tools/resize", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
|
|
settings: JSON.stringify({ width: 50, height: 50, fit: "fill" }),
|
|
},
|
|
});
|
|
expect(resizeRes.ok()).toBe(true);
|
|
const resizeBody = await resizeRes.json();
|
|
expect(resizeBody.downloadUrl).toBeTruthy();
|
|
|
|
// Step 2: Download resized image
|
|
const dlRes = await request.get(resizeBody.downloadUrl, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
expect(dlRes.ok()).toBe(true);
|
|
const resizedBuffer = Buffer.from(await dlRes.body());
|
|
|
|
// Step 3: Verify dimensions via info
|
|
const infoRes = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "resized.jpg", mimeType: "image/jpeg", buffer: resizedBuffer },
|
|
},
|
|
});
|
|
expect(infoRes.ok()).toBe(true);
|
|
const infoBody = await infoRes.json();
|
|
expect(infoBody.width).toBe(50);
|
|
expect(infoBody.height).toBe(50);
|
|
});
|
|
});
|
|
|
|
test.describe("Resize + info verification: PNG", () => {
|
|
test("resize PNG then verify dimensions via info", async ({ request }) => {
|
|
const resizeRes = await request.post("/api/v1/tools/resize", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
|
|
settings: JSON.stringify({ width: 100, fit: "contain" }),
|
|
},
|
|
});
|
|
expect(resizeRes.ok()).toBe(true);
|
|
const resizeBody = await resizeRes.json();
|
|
|
|
const dlRes = await request.get(resizeBody.downloadUrl, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
expect(dlRes.ok()).toBe(true);
|
|
const resizedBuffer = Buffer.from(await dlRes.body());
|
|
|
|
const infoRes = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "resized.png", mimeType: "image/png", buffer: resizedBuffer },
|
|
},
|
|
});
|
|
expect(infoRes.ok()).toBe(true);
|
|
const infoBody = await infoRes.json();
|
|
expect(infoBody.width).toBe(100);
|
|
// Aspect ratio preserved: 200x150 -> 100x75
|
|
expect(infoBody.height).toBe(75);
|
|
expect(infoBody.format).toBe("png");
|
|
});
|
|
});
|
|
|
|
// ─── Convert + Info Verification ──────────────────────────────────
|
|
|
|
test.describe("Convert + info verification", () => {
|
|
test("convert JPEG to WebP then verify format via info", async ({ request }) => {
|
|
const convertRes = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
|
|
settings: JSON.stringify({ format: "webp" }),
|
|
},
|
|
});
|
|
expect(convertRes.ok()).toBe(true);
|
|
const convertBody = await convertRes.json();
|
|
|
|
const dlRes = await request.get(convertBody.downloadUrl, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
expect(dlRes.ok()).toBe(true);
|
|
const convertedBuffer = Buffer.from(await dlRes.body());
|
|
|
|
const infoRes = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "converted.webp", mimeType: "image/webp", buffer: convertedBuffer },
|
|
},
|
|
});
|
|
expect(infoRes.ok()).toBe(true);
|
|
const infoBody = await infoRes.json();
|
|
expect(infoBody.format).toBe("webp");
|
|
expect(infoBody.width).toBe(100);
|
|
expect(infoBody.height).toBe(100);
|
|
});
|
|
|
|
test("convert PNG to AVIF then verify format via info", async ({ request }) => {
|
|
const convertRes = await request.post("/api/v1/tools/convert", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
|
|
settings: JSON.stringify({ format: "avif" }),
|
|
},
|
|
});
|
|
expect(convertRes.ok()).toBe(true);
|
|
const convertBody = await convertRes.json();
|
|
|
|
const dlRes = await request.get(convertBody.downloadUrl, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
expect(dlRes.ok()).toBe(true);
|
|
const convertedBuffer = Buffer.from(await dlRes.body());
|
|
|
|
const infoRes = await request.post("/api/v1/tools/info", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
multipart: {
|
|
file: { name: "converted.avif", mimeType: "image/avif", buffer: convertedBuffer },
|
|
},
|
|
});
|
|
expect(infoRes.ok()).toBe(true);
|
|
const infoBody = await infoRes.json();
|
|
expect(infoBody.width).toBe(200);
|
|
expect(infoBody.height).toBe(150);
|
|
});
|
|
});
|