mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
Renames 18 ambiguous or hard-to-search tool names so image tools self-qualify like the other modalities ("Compress" becomes "Compress Image"), and cleans up a few awkward names. Propagated across search (constants.ts), display (en.ts + 20 locales), the OpenAPI base spec + 20 locale specs, and the docs tool-page headings in 21 languages. Removes the duplicate "Normalize Audio" summary shared by the video and audio endpoints. Tool ids and routes are unchanged, so no API paths or bookmarks break.
322 lines
13 KiB
TypeScript
322 lines
13 KiB
TypeScript
import { expect, test, uploadTestImage, waitForProcessing } from "./helpers";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// GUI E2E: Metadata Tools (strip-metadata, edit-metadata, info)
|
|
// Covers settings UI, control interactions, processing flow, and download.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
test.describe("GUI Metadata Tools", () => {
|
|
// ========================================================================
|
|
// STRIP METADATA
|
|
// ========================================================================
|
|
test.describe("Strip Metadata", () => {
|
|
test("renders tool page with dropzone", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/strip-metadata");
|
|
await expect(page.getByText("Remove Image Metadata").first()).toBeVisible();
|
|
await expect(page.getByText("Upload from computer")).toBeVisible();
|
|
});
|
|
|
|
test("shows strip options after upload", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/strip-metadata");
|
|
await uploadTestImage(page);
|
|
|
|
// Remove All checkbox is checked by default
|
|
await expect(page.getByText("Remove All Metadata")).toBeVisible();
|
|
// Individual options
|
|
await expect(page.getByText("Strip EXIF")).toBeVisible();
|
|
await expect(page.getByText("Strip GPS")).toBeVisible();
|
|
await expect(page.getByText("Strip ICC")).toBeVisible();
|
|
await expect(page.getByText("Strip XMP")).toBeVisible();
|
|
});
|
|
|
|
test("individual strip options disabled when Remove All is checked", async ({
|
|
loggedInPage: page,
|
|
}) => {
|
|
await page.goto("/image/strip-metadata");
|
|
await uploadTestImage(page);
|
|
|
|
// With Remove All checked, individual checkboxes should be disabled
|
|
const exifCheckbox = page
|
|
.locator("label")
|
|
.filter({ hasText: "Strip EXIF" })
|
|
.locator("input[type='checkbox']");
|
|
await expect(exifCheckbox).toBeDisabled();
|
|
});
|
|
|
|
test("unchecking Remove All enables individual options", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/strip-metadata");
|
|
await uploadTestImage(page);
|
|
|
|
// Uncheck Remove All
|
|
const removeAllCheckbox = page
|
|
.locator("label")
|
|
.filter({ hasText: "Remove All Metadata" })
|
|
.locator("input[type='checkbox']");
|
|
await removeAllCheckbox.uncheck();
|
|
|
|
// Individual checkboxes should now be enabled
|
|
const exifCheckbox = page
|
|
.locator("label")
|
|
.filter({ hasText: "Strip EXIF" })
|
|
.locator("input[type='checkbox']");
|
|
await expect(exifCheckbox).toBeEnabled();
|
|
});
|
|
|
|
test("auto-inspects metadata on upload", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/strip-metadata");
|
|
await uploadTestImage(page);
|
|
|
|
// Should show "Current Metadata" heading or "Reading metadata..." or "No metadata found"
|
|
await expect(
|
|
page
|
|
.getByText("Current Metadata")
|
|
.or(page.getByText("Reading metadata..."))
|
|
.or(page.getByText("No metadata found"))
|
|
.first(),
|
|
).toBeVisible({ timeout: 10_000 });
|
|
});
|
|
|
|
test("processes strip and shows download", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/strip-metadata");
|
|
await uploadTestImage(page);
|
|
|
|
await page.getByTestId("strip-metadata-submit").click();
|
|
await waitForProcessing(page);
|
|
|
|
await expect(page.getByTestId("strip-metadata-download")).toBeVisible({ timeout: 15_000 });
|
|
});
|
|
});
|
|
|
|
// ========================================================================
|
|
// EDIT METADATA
|
|
// ========================================================================
|
|
test.describe("Edit Metadata", () => {
|
|
test("renders tool page with dropzone", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/edit-metadata");
|
|
await expect(page.getByText("Edit Image Metadata").first()).toBeVisible();
|
|
await expect(page.getByText("Upload from computer")).toBeVisible();
|
|
});
|
|
|
|
test("shows form fields after upload and inspection", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/edit-metadata");
|
|
await uploadTestImage(page);
|
|
|
|
// Wait for inspection to complete
|
|
await page.waitForSelector('[id="em-artist"]', { timeout: 10_000 });
|
|
|
|
// Basic Info section fields
|
|
await expect(page.locator("#em-artist")).toBeVisible();
|
|
await expect(page.locator("#em-copyright")).toBeVisible();
|
|
await expect(page.locator("#em-description")).toBeVisible();
|
|
await expect(page.locator("#em-software")).toBeVisible();
|
|
});
|
|
|
|
test("shows collapsible sections", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/edit-metadata");
|
|
await uploadTestImage(page);
|
|
await page.waitForSelector('[id="em-artist"]', { timeout: 10_000 });
|
|
|
|
await expect(page.getByText("Basic Info").first()).toBeVisible();
|
|
await expect(page.getByText("Date & Time").first()).toBeVisible();
|
|
await expect(page.getByText("Location (GPS)").first()).toBeVisible();
|
|
await expect(page.getByText("Keywords").first()).toBeVisible();
|
|
});
|
|
|
|
test("date mode toggle between Edit and Shift", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/edit-metadata");
|
|
await uploadTestImage(page);
|
|
await page.waitForSelector('[id="em-artist"]', { timeout: 10_000 });
|
|
|
|
// Open Date & Time section
|
|
await page.getByText("Date & Time").first().click();
|
|
await expect(page.getByRole("button", { name: "Edit Dates" })).toBeVisible();
|
|
await expect(page.getByRole("button", { name: "Shift All Dates" })).toBeVisible();
|
|
});
|
|
|
|
test("GPS section shows clear GPS checkbox", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/edit-metadata");
|
|
await uploadTestImage(page);
|
|
await page.waitForSelector('[id="em-artist"]', { timeout: 10_000 });
|
|
|
|
// Open GPS section
|
|
await page.getByText("Location (GPS)").first().click();
|
|
await expect(page.getByText("Remove all GPS data")).toBeVisible();
|
|
await expect(page.locator("#em-gps-lat")).toBeVisible();
|
|
await expect(page.locator("#em-gps-lon")).toBeVisible();
|
|
});
|
|
|
|
test("edits artist field and processes", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/edit-metadata");
|
|
await uploadTestImage(page);
|
|
await page.waitForSelector('[id="em-artist"]', { timeout: 10_000 });
|
|
|
|
await page.fill('[id="em-artist"]', "E2E Test Artist");
|
|
await page.getByTestId("edit-metadata-submit").click();
|
|
await waitForProcessing(page);
|
|
|
|
await expect(page.getByTestId("edit-metadata-download")).toBeVisible({ timeout: 15_000 });
|
|
});
|
|
|
|
test("shows changes summary when fields are modified", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/edit-metadata");
|
|
await uploadTestImage(page);
|
|
await page.waitForSelector('[id="em-artist"]', { timeout: 10_000 });
|
|
|
|
await page.fill('[id="em-artist"]', "Modified Artist");
|
|
await expect(page.getByText(/\d+ changes:/)).toBeVisible();
|
|
});
|
|
|
|
test("shows copyright and description fields", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/edit-metadata");
|
|
await uploadTestImage(page);
|
|
await page.waitForSelector('[id="em-artist"]', { timeout: 10_000 });
|
|
|
|
await expect(page.locator("#em-copyright")).toBeVisible();
|
|
await expect(page.locator("#em-description")).toBeVisible();
|
|
});
|
|
|
|
test("submit button uses data-testid", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/edit-metadata");
|
|
await uploadTestImage(page);
|
|
await page.waitForSelector('[id="em-artist"]', { timeout: 10_000 });
|
|
|
|
await expect(page.getByTestId("edit-metadata-submit")).toBeVisible();
|
|
});
|
|
});
|
|
|
|
// ========================================================================
|
|
// IMAGE INFO
|
|
// ========================================================================
|
|
test.describe("Image Info", () => {
|
|
test("renders tool page with dropzone", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/info");
|
|
await expect(page.getByText("Image Info").first()).toBeVisible();
|
|
await expect(page.getByText("Upload from computer")).toBeVisible();
|
|
});
|
|
|
|
test("shows Read Info button after upload", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/info");
|
|
await uploadTestImage(page);
|
|
|
|
await expect(page.getByTestId("info-submit")).toBeVisible();
|
|
await expect(page.getByTestId("info-submit")).toHaveText(/Read Info/);
|
|
});
|
|
|
|
test("displays image metadata after Read Info", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/info");
|
|
await uploadTestImage(page);
|
|
|
|
await page.getByTestId("info-submit").click();
|
|
await waitForProcessing(page);
|
|
|
|
// Verify metadata grid appears
|
|
await expect(page.getByText("Dimensions").first()).toBeVisible({ timeout: 15_000 });
|
|
await expect(page.getByText("Format").first()).toBeVisible();
|
|
await expect(page.getByText("File Size").first()).toBeVisible();
|
|
await expect(page.getByText("Channels").first()).toBeVisible();
|
|
await expect(page.getByText("Color Space").first()).toBeVisible();
|
|
});
|
|
|
|
test("shows channel stats histogram after Read Info", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/info");
|
|
await uploadTestImage(page);
|
|
|
|
await page.getByTestId("info-submit").click();
|
|
await waitForProcessing(page);
|
|
|
|
await expect(page.getByText("Channel Stats")).toBeVisible({ timeout: 15_000 });
|
|
});
|
|
|
|
test("no download link for info tool (read-only)", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/info");
|
|
await uploadTestImage(page);
|
|
|
|
await page.getByTestId("info-submit").click();
|
|
await waitForProcessing(page);
|
|
|
|
// Info tool does not produce a downloadable result
|
|
await expect(page.getByRole("link", { name: /download/i })).not.toBeVisible();
|
|
});
|
|
|
|
test("submit absent without file, enabled with file", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/info");
|
|
|
|
// The settings panel (and its submit button) only mounts after a file is
|
|
// added. Before upload the page shows just the dropzone.
|
|
await expect(page.getByTestId("info-submit")).toHaveCount(0);
|
|
|
|
await uploadTestImage(page);
|
|
await expect(page.getByTestId("info-submit")).toBeEnabled();
|
|
});
|
|
});
|
|
|
|
// ========================================================================
|
|
// UNDO / STATE RESET (Metadata tools)
|
|
// ========================================================================
|
|
test.describe("Undo and State Reset", () => {
|
|
test("strip-metadata: undo after processing returns to settings", async ({
|
|
loggedInPage: page,
|
|
}) => {
|
|
await page.goto("/image/strip-metadata");
|
|
await uploadTestImage(page);
|
|
|
|
await page.getByTestId("strip-metadata-submit").click();
|
|
await waitForProcessing(page);
|
|
|
|
await expect(page.getByTestId("strip-metadata-download")).toBeVisible({ timeout: 15_000 });
|
|
|
|
await page.getByRole("button", { name: /adjust settings/i }).click();
|
|
|
|
await expect(page.getByTestId("strip-metadata-submit")).toBeVisible({ timeout: 5_000 });
|
|
await expect(page.getByTestId("strip-metadata-download")).not.toBeVisible();
|
|
// Settings should still be visible
|
|
await expect(page.getByText("Remove All Metadata")).toBeVisible();
|
|
});
|
|
|
|
test("edit-metadata: undo after processing returns to form", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/edit-metadata");
|
|
await uploadTestImage(page);
|
|
await page.waitForSelector('[id="em-artist"]', { timeout: 10_000 });
|
|
|
|
await page.fill('[id="em-artist"]', "Undo Test Artist");
|
|
await page.getByTestId("edit-metadata-submit").click();
|
|
await waitForProcessing(page);
|
|
|
|
await expect(page.getByTestId("edit-metadata-download")).toBeVisible({ timeout: 15_000 });
|
|
|
|
await page.getByRole("button", { name: /adjust settings/i }).click();
|
|
|
|
await expect(page.getByTestId("edit-metadata-submit")).toBeVisible({ timeout: 5_000 });
|
|
await expect(page.getByTestId("edit-metadata-download")).not.toBeVisible();
|
|
});
|
|
|
|
test("strip-metadata: clear all returns to dropzone", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/strip-metadata");
|
|
await uploadTestImage(page);
|
|
|
|
await expect(page.getByText("Remove All Metadata")).toBeVisible();
|
|
|
|
await page.getByText("Clear all").click();
|
|
|
|
await expect(page.getByText("Upload from computer")).toBeVisible({ timeout: 5_000 });
|
|
});
|
|
|
|
test("navigate away from info tool resets state", async ({ loggedInPage: page }) => {
|
|
await page.goto("/image/info");
|
|
await uploadTestImage(page);
|
|
|
|
await page.getByTestId("info-submit").click();
|
|
await waitForProcessing(page);
|
|
|
|
await expect(page.getByText("Dimensions").first()).toBeVisible({ timeout: 15_000 });
|
|
|
|
// Navigate away and back
|
|
await page.goto("/image/resize");
|
|
await page.goto("/image/info");
|
|
|
|
// Should be back at dropzone
|
|
await expect(page.getByText("Upload from computer")).toBeVisible();
|
|
});
|
|
});
|
|
});
|