Merge branch 'worktree-agent-a3a33bf5'

This commit is contained in:
SnapOtter
2026-05-01 04:13:56 +08:00
2 changed files with 175 additions and 1 deletions
+54
View File
@@ -11,6 +11,8 @@ function getFixturePath(name: string): string {
const FIXTURE_JPG = getFixturePath("test-100x100.jpg");
const FIXTURE_PNG = getFixturePath("test-200x150.png");
const FIXTURE_WEBP = getFixturePath("test-50x50.webp");
const FIXTURE_HEIC = getFixturePath("test-200x150.heic");
const FIXTURE_PORTRAIT_JPG = getFixturePath("test-portrait.jpg");
// ---------------------------------------------------------------------------
// Multi-file upload tests
@@ -173,6 +175,33 @@ test.describe("Multi-file upload", () => {
// Should display "1 / 2" counter badge
await expect(page.getByText("1 / 2")).toBeVisible();
});
test("upload 5 files and all are listed", async ({ loggedInPage: page }) => {
await page.goto("/resize");
// Upload initial 3 files
const fileChooserPromise = page.waitForEvent("filechooser");
const dropzone = page.locator("[class*='border-dashed']").first();
await dropzone.click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles([FIXTURE_JPG, FIXTURE_PNG, FIXTURE_WEBP]);
await page.waitForTimeout(500);
await expect(page.getByText("Files (3)")).toBeVisible();
// Add 2 more via "+ Add more"
const addMorePromise = page.waitForEvent("filechooser");
await page.getByText("+ Add more").click();
const addMoreChooser = await addMorePromise;
await addMoreChooser.setFiles([FIXTURE_HEIC, FIXTURE_PORTRAIT_JPG]);
await page.waitForTimeout(500);
// All 5 files should be registered
await expect(page.getByText("Files (5)")).toBeVisible();
// Counter badge should show "1 / 5"
await expect(page.getByText("1 / 5")).toBeVisible();
});
});
// ---------------------------------------------------------------------------
@@ -381,4 +410,29 @@ test.describe("Mixed formats", () => {
await expect(page.locator("button[title='test-200x150.png']")).toBeVisible();
await expect(page.locator("button[title='test-50x50.webp']")).toBeVisible();
});
test("upload JPEG + PNG + WebP + HEIC mixed batch and all are accepted", async ({
loggedInPage: page,
}) => {
await page.goto("/resize");
const fileChooserPromise = page.waitForEvent("filechooser");
const dropzone = page.locator("[class*='border-dashed']").first();
await dropzone.click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles([FIXTURE_JPG, FIXTURE_PNG, FIXTURE_WEBP, FIXTURE_HEIC]);
await page.waitForTimeout(1000);
// All 4 files should be registered
await expect(page.getByText("Files (4)")).toBeVisible();
// Counter badge should show "1 / 4"
await expect(page.getByText("1 / 4")).toBeVisible();
// All 4 thumbnails should be visible in the thumbnail strip
await expect(page.locator("button[title='test-100x100.jpg']")).toBeVisible();
await expect(page.locator("button[title='test-200x150.png']")).toBeVisible();
await expect(page.locator("button[title='test-50x50.webp']")).toBeVisible();
await expect(page.locator("button[title='test-200x150.heic']")).toBeVisible();
});
});
+121 -1
View File
@@ -1,4 +1,16 @@
import { expect, getTestImagePath, test } from "./helpers";
import path from "node:path";
import { expect, getTestImagePath, test, waitForProcessing } from "./helpers";
// ---------------------------------------------------------------------------
// Helper: resolve fixture image paths
// ---------------------------------------------------------------------------
function getFixturePath(name: string): string {
return path.join(process.cwd(), "tests", "fixtures", name);
}
const FIXTURE_JPG = getFixturePath("test-100x100.jpg");
const FIXTURE_PNG = getFixturePath("test-200x150.png");
const FIXTURE_WEBP = getFixturePath("test-50x50.webp");
// ---------------------------------------------------------------------------
// Helper: navigate to /automate with retry logic for blank-page flakes
@@ -192,6 +204,17 @@ test.describe("Pipeline Builder - Step management", () => {
await compressRow.click();
await expect(page.locator(".border-primary").first()).toBeVisible({ timeout: 3_000 });
});
test("drag handles are visible for reordering steps", async ({ loggedInPage: page }) => {
await gotoAutomate(page);
await addToolStep(page, "Resize", 1);
await addToolStep(page, "Compress", 2);
// Each step has a GripVertical drag handle (rendered as a <span> with cursor-grab)
const dragHandles = page.locator(".cursor-grab");
await expect(dragHandles).toHaveCount(2);
});
});
// ---------------------------------------------------------------------------
@@ -253,6 +276,103 @@ test.describe("Pipeline Builder - File upload and processing", () => {
// File info should be visible in the preview area
await expect(page.getByText("test-image.png").first()).toBeVisible();
});
test("processing completes and shows before/after result", async ({ loggedInPage: page }) => {
await gotoAutomate(page);
await addToolStep(page, "Remove Metadata", 1);
await addToolStep(page, "Compress", 2);
await uploadTestFile(page);
// Click Process
await page.getByRole("button", { name: "Process", exact: true }).click();
// Wait for processing to complete (may be instant for small images)
await waitForProcessing(page, 30_000);
// After completion, the before/after slider should appear
const slider = page.locator("[aria-label='Before/after comparison slider']");
await expect(slider).toBeVisible({ timeout: 15_000 });
// Original and Processed labels should be shown
await expect(page.getByText("Original").first()).toBeVisible();
await expect(page.getByText("Processed").first()).toBeVisible();
});
test("process button disabled without file even when steps present", async ({
loggedInPage: page,
}) => {
await gotoAutomate(page);
await addToolStep(page, "Compress", 1);
// Process should be disabled without a file
const processBtn = page.getByRole("button", { name: "Process", exact: true });
await expect(processBtn).toBeDisabled();
});
});
// ---------------------------------------------------------------------------
// Batch pipeline tests
// ---------------------------------------------------------------------------
test.describe("Pipeline Builder - Batch processing", () => {
/** Upload multiple fixture images via the dropzone file chooser. */
async function uploadMultipleFiles(page: import("@playwright/test").Page) {
const fileChooserPromise = page.waitForEvent("filechooser");
await page.getByRole("button", { name: /upload from computer/i }).click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles([FIXTURE_JPG, FIXTURE_PNG, FIXTURE_WEBP]);
await page.waitForTimeout(500);
}
test("batch pipeline: 3 images through 2 steps, all processed", async ({
loggedInPage: page,
}) => {
await gotoAutomate(page);
// Add 2 pipeline steps
await addToolStep(page, "Remove Metadata", 1);
await addToolStep(page, "Compress", 2);
// Upload 3 images
await uploadMultipleFiles(page);
// File badge should show "3 files"
await expect(page.getByText("3 files").first()).toBeVisible();
// Process button should show batch count
const processBtn = page.getByRole("button", { name: /process all.*3/i });
await expect(processBtn).toBeEnabled();
await processBtn.click();
// Wait for processing to complete (may be instant for small images)
await waitForProcessing(page, 45_000);
// Counter badge should appear (N / M format)
await expect(page.getByText(/1 \/ 3/).first()).toBeVisible({ timeout: 15_000 });
});
test("batch pipeline: Download ZIP button appears after batch processing", async ({
loggedInPage: page,
}) => {
await gotoAutomate(page);
await addToolStep(page, "Compress", 1);
await uploadMultipleFiles(page);
// Process batch
const processBtn = page.getByRole("button", { name: /process all.*3/i });
await processBtn.click();
// Wait for processing to complete
await waitForProcessing(page, 45_000);
// Counter badge should appear to confirm results are ready
await expect(page.getByText(/1 \/ 3/).first()).toBeVisible({ timeout: 15_000 });
// Download ZIP button should be visible
await expect(page.getByRole("button", { name: /download zip/i })).toBeVisible();
});
});
// ---------------------------------------------------------------------------