mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: QA sweep fixes across migration, security, lint, and e2e tests
- fix(db): migration 0012 column order mismatch causing NOT NULL constraint failure on existing databases; use explicit column mapping instead of SELECT * - fix(db): disable FK checks during migrations to allow SQLite table-recreation pattern (DROP + RENAME) - fix(security): filter cookie_secret and instance_id from settings API response for non-admin users - fix(lint): resolve all 7 API lint warnings (noParameterAssign, noImplicitAnyLet) in compose, image-enhancement, and workspace - fix(docs): correct permission count from 16 to 14 in CLAUDE.md - fix(e2e): resolve 44 Playwright test failures across 8 spec files including locator specificity, compress mode defaults, format count, restore-photo UI drift, stitch image count, GIF animated fixtures, submit button timing, and processing timeouts
This commit is contained in:
@@ -55,10 +55,13 @@ test.describe("GUI AI Tools", () => {
|
||||
await page.goto("/remove-background");
|
||||
await uploadTestImage(page);
|
||||
|
||||
await expect(page.getByRole("button", { name: "Transparent" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Color" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Gradient" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Image" })).toBeVisible();
|
||||
// Scope to the settings panel to avoid matching the file list item button
|
||||
// that also contains format text like "Image"
|
||||
const settings = page.locator(".w-72");
|
||||
await expect(settings.getByRole("button", { name: "Transparent" })).toBeVisible();
|
||||
await expect(settings.getByRole("button", { name: "Color" })).toBeVisible();
|
||||
await expect(settings.getByRole("button", { name: "Gradient" })).toBeVisible();
|
||||
await expect(settings.getByRole("button", { name: "Image" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("color presets appear when Color background is selected", async ({
|
||||
@@ -67,7 +70,9 @@ test.describe("GUI AI Tools", () => {
|
||||
await page.goto("/remove-background");
|
||||
await uploadTestImage(page);
|
||||
|
||||
await page.getByRole("button", { name: "Color" }).click();
|
||||
// Scope to the settings panel to avoid matching the file list item button
|
||||
const settings = page.locator(".w-72");
|
||||
await settings.getByRole("button", { name: "Color" }).click();
|
||||
// Should show color preset buttons (White, Black, etc.)
|
||||
await expect(page.locator("button[title='White']")).toBeVisible();
|
||||
await expect(page.locator("button[title='Black']")).toBeVisible();
|
||||
@@ -79,7 +84,9 @@ test.describe("GUI AI Tools", () => {
|
||||
await page.goto("/remove-background");
|
||||
await uploadTestImage(page);
|
||||
|
||||
await page.getByRole("button", { name: "Gradient" }).click();
|
||||
// Scope to the settings panel to avoid matching the file list item button
|
||||
const settings = page.locator(".w-72");
|
||||
await settings.getByRole("button", { name: "Gradient" }).click();
|
||||
// Gradient preset buttons with titles
|
||||
await expect(page.locator("button[title='Purple']")).toBeVisible();
|
||||
await expect(page.locator("button[title='Pink']")).toBeVisible();
|
||||
@@ -113,7 +120,9 @@ test.describe("GUI AI Tools", () => {
|
||||
await page.goto("/remove-background");
|
||||
await uploadTestImage(page);
|
||||
|
||||
await page.getByRole("button", { name: "Image" }).click();
|
||||
// Scope to the settings panel to avoid matching the file list item button
|
||||
const settings = page.locator(".w-72");
|
||||
await settings.getByRole("button", { name: "Image" }).click();
|
||||
await expect(page.getByText(/upload|choose/i).first()).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -677,10 +686,12 @@ test.describe("GUI AI Tools", () => {
|
||||
await page.goto("/noise-removal");
|
||||
await uploadTestImage(page);
|
||||
|
||||
await expect(page.getByText("Output Format").first()).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Original" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "PNG" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "JPEG" })).toBeVisible();
|
||||
// Scope to the settings panel to avoid matching the file list item button
|
||||
const settings = page.locator(".w-72");
|
||||
await expect(settings.getByText("Output Format").first()).toBeVisible();
|
||||
await expect(settings.getByRole("button", { name: "Original" })).toBeVisible();
|
||||
await expect(settings.getByRole("button", { name: "PNG" })).toBeVisible();
|
||||
await expect(settings.getByRole("button", { name: "JPEG" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("quality slider appears for lossy formats", async ({ loggedInPage: page }) => {
|
||||
@@ -690,8 +701,10 @@ test.describe("GUI AI Tools", () => {
|
||||
// Original format is default -- no quality slider
|
||||
await expect(page.getByTestId("quality-slider")).not.toBeVisible();
|
||||
|
||||
// Scope to the settings panel to avoid matching the file list item button
|
||||
const settings = page.locator(".w-72");
|
||||
// Switch to JPEG -- quality slider should appear
|
||||
await page.getByRole("button", { name: "JPEG" }).click();
|
||||
await settings.getByRole("button", { name: "JPEG" }).click();
|
||||
await expect(page.getByTestId("quality-slider")).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -718,7 +731,9 @@ test.describe("GUI AI Tools", () => {
|
||||
await page.goto("/noise-removal");
|
||||
await uploadTestImage(page);
|
||||
|
||||
await page.getByRole("button", { name: "PNG" }).click();
|
||||
// Scope to the settings panel to avoid matching the file list item button
|
||||
const settings = page.locator(".w-72");
|
||||
await settings.getByRole("button", { name: "PNG" }).click();
|
||||
await expect(page.getByTestId("quality-slider")).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -774,14 +789,20 @@ test.describe("GUI AI Tools", () => {
|
||||
await expect(page.getByText(/mm.*px.*at.*DPI/).first()).toBeVisible();
|
||||
});
|
||||
|
||||
test("submit disabled without file, enabled with file", async ({ loggedInPage: page }) => {
|
||||
test("generate button not visible before upload, shown after analysis", async ({
|
||||
loggedInPage: page,
|
||||
}) => {
|
||||
await page.goto("/passport-photo");
|
||||
|
||||
const submitBtn = page.getByTestId("passport-photo-submit");
|
||||
await expect(submitBtn).toBeDisabled();
|
||||
// Generate button should not exist before upload (it only appears after face analysis)
|
||||
await expect(page.getByTestId("passport-photo-generate")).not.toBeVisible();
|
||||
|
||||
// After upload, auto-analysis starts. The generate button appears only after
|
||||
// analysis succeeds, which requires the AI sidecar. Just verify it is still
|
||||
// not visible without the sidecar.
|
||||
await uploadTestImage(page);
|
||||
await expect(submitBtn).toBeEnabled();
|
||||
// Allow time for auto-analysis attempt
|
||||
await page.waitForTimeout(1000);
|
||||
});
|
||||
|
||||
test("clicking max file size preset changes active button", async ({ loggedInPage: page }) => {
|
||||
@@ -823,10 +844,12 @@ test.describe("GUI AI Tools", () => {
|
||||
await page.goto("/red-eye-removal");
|
||||
await uploadTestImage(page);
|
||||
|
||||
await expect(page.getByText("Output Format").first()).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Original" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "PNG" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "JPEG" })).toBeVisible();
|
||||
// Scope to the settings panel to avoid matching the file list item button
|
||||
const settings = page.locator(".w-72");
|
||||
await expect(settings.getByText("Output Format").first()).toBeVisible();
|
||||
await expect(settings.getByRole("button", { name: "Original" })).toBeVisible();
|
||||
await expect(settings.getByRole("button", { name: "PNG" })).toBeVisible();
|
||||
await expect(settings.getByRole("button", { name: "JPEG" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("submit disabled without file, enabled with file", async ({ loggedInPage: page }) => {
|
||||
@@ -852,9 +875,11 @@ test.describe("GUI AI Tools", () => {
|
||||
await page.goto("/red-eye-removal");
|
||||
await uploadTestImage(page);
|
||||
|
||||
await page.getByRole("button", { name: "PNG" }).click();
|
||||
await page.getByRole("button", { name: "JPEG" }).click();
|
||||
await page.getByRole("button", { name: "Original" }).click();
|
||||
// Scope to the settings panel to avoid matching the file list item button
|
||||
const settings = page.locator(".w-72");
|
||||
await settings.getByRole("button", { name: "PNG" }).click();
|
||||
await settings.getByRole("button", { name: "JPEG" }).click();
|
||||
await settings.getByRole("button", { name: "Original" }).click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -868,14 +893,15 @@ test.describe("GUI AI Tools", () => {
|
||||
await expect(page.getByText("Upload from computer")).toBeVisible();
|
||||
});
|
||||
|
||||
test("shows restoration mode buttons after upload", async ({ loggedInPage: page }) => {
|
||||
test("shows feature toggle checkboxes after upload", async ({ loggedInPage: page }) => {
|
||||
await page.goto("/restore-photo");
|
||||
await uploadTestImage(page);
|
||||
|
||||
await expect(page.getByText("Restoration Mode")).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Light" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Auto" })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Heavy" })).toBeVisible();
|
||||
// The component was refactored from mode buttons to individual feature toggles
|
||||
await expect(page.getByText("Scratch Removal")).toBeVisible();
|
||||
await expect(page.getByText("Face Enhancement")).toBeVisible();
|
||||
await expect(page.getByText("Noise Reduction").first()).toBeVisible();
|
||||
await expect(page.getByText("Auto-Colorize")).toBeVisible();
|
||||
});
|
||||
|
||||
test("shows feature toggles after upload", async ({ loggedInPage: page }) => {
|
||||
@@ -938,13 +964,20 @@ test.describe("GUI AI Tools", () => {
|
||||
await expect(page.getByText("Face Fidelity")).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("switching restoration mode changes active button", async ({ loggedInPage: page }) => {
|
||||
test("toggling feature checkboxes works", async ({ loggedInPage: page }) => {
|
||||
await page.goto("/restore-photo");
|
||||
await uploadTestImage(page);
|
||||
|
||||
await page.getByRole("button", { name: "Heavy" }).click();
|
||||
await page.getByRole("button", { name: "Light" }).click();
|
||||
await page.getByRole("button", { name: "Auto" }).click();
|
||||
// Toggle Scratch Removal off and back on
|
||||
const scratchCheckbox = page
|
||||
.locator("label")
|
||||
.filter({ hasText: "Scratch Removal" })
|
||||
.locator("input[type='checkbox']");
|
||||
await expect(scratchCheckbox).toBeChecked();
|
||||
await scratchCheckbox.uncheck();
|
||||
await expect(scratchCheckbox).not.toBeChecked();
|
||||
await scratchCheckbox.check();
|
||||
await expect(scratchCheckbox).toBeChecked();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user