mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
test: expand test coverage across unit, integration, e2e, and e2e-docker suites
Add ~210 new tests filling gaps identified by a comprehensive 14-agent coverage audit. Unit+integration tests go from 9,388 to 9,484 (all passing). Unit tests (+36): - AI bridge: OOM fallback path, custom tier option - Web lib: api-errors, format date/datetime, tool-i18n coverage Integration tests (+19): - Format matrix: ai-canvas-expand and find-duplicates added to cross-format matrix - Adversarial: SVG XXE attacks, SQL injection in settings, request body size limits, race conditions with identical filenames E2E Docker (+3): - ai-canvas-expand tool coverage with HEIC input and edge cases E2E GUI (~150+): - Navigation: login rate limiting, ai-canvas-expand in parameterized list - Responsive: dropzone visibility, text readability, dialog bounds at all viewports - Keyboard: shortcuts verified from automate, files, tool, and fullscreen pages - Tool UI: undo/state-reset for 16 tools, crop canvas drag handles, rotate/border live preview, linked aspect-ratio inputs for resize - Batch: per-image undo isolation, batch compress/convert/rotate (not just resize) - Pipeline: tool palette search, step collapse/expand visibility - Settings: audit log entry verification, system settings persistence, teams CRUD, role permission toggling - RBAC: user/editor 403 on roles/teams endpoints, privilege escalation prevention, cross-role tab parity documented as intentional - Accessibility: skip-to-content link (WCAG 2.4.1), comprehensive color contrast for all headings/body/buttons in both themes with DOM-walking background detection - Resilience: auth expiry 401 redirect, rate limit 429 handling - Performance: JS heap memory stability for tool navigation, dialog cycling, upload/clear cycles, rapid page navigation
This commit is contained in:
@@ -908,6 +908,77 @@ test.describe("Server Error Handling", () => {
|
||||
await page.unroute("**/api/v1/tools/resize");
|
||||
});
|
||||
|
||||
test("auth expiry (401) redirects to login or shows re-auth prompt", async ({
|
||||
loggedInPage: page,
|
||||
}) => {
|
||||
await page.goto("/resize");
|
||||
await uploadTestImage(page);
|
||||
|
||||
// Intercept to return 401 (session expired)
|
||||
await page.route("**/api/v1/tools/resize", (route) =>
|
||||
route.fulfill({
|
||||
status: 401,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ error: "Session expired" }),
|
||||
}),
|
||||
);
|
||||
|
||||
await page.locator("input[placeholder='Auto']").first().fill("50");
|
||||
await page.getByRole("button", { name: "Resize" }).click();
|
||||
|
||||
// Wait for error/redirect
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Should either redirect to /login or show an auth error -- not crash
|
||||
const url = page.url();
|
||||
const bodyText = await page.textContent("body");
|
||||
const redirectedToLogin = url.includes("/login");
|
||||
const showsAuthError = /session|expired|unauthorized|login/i.test(bodyText ?? "");
|
||||
|
||||
expect(
|
||||
redirectedToLogin || showsAuthError,
|
||||
"Expected redirect to login or auth error message after 401",
|
||||
).toBeTruthy();
|
||||
|
||||
// Page should not be a white screen
|
||||
const content = await page.textContent("body");
|
||||
expect(content).toBeDefined();
|
||||
expect(content?.length).toBeGreaterThan(0);
|
||||
|
||||
await page.unroute("**/api/v1/tools/resize");
|
||||
});
|
||||
|
||||
test("rate limit (429) shows throttle message, not crash", async ({ loggedInPage: page }) => {
|
||||
await page.goto("/resize");
|
||||
await uploadTestImage(page);
|
||||
|
||||
// Intercept to return 429 (rate limited)
|
||||
await page.route("**/api/v1/tools/resize", (route) =>
|
||||
route.fulfill({
|
||||
status: 429,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ error: "Rate limit exceeded. Try again later." }),
|
||||
}),
|
||||
);
|
||||
|
||||
await page.locator("input[placeholder='Auto']").first().fill("50");
|
||||
await page.getByRole("button", { name: "Resize" }).click();
|
||||
|
||||
// Wait for error to appear
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Page should not crash
|
||||
await expect(page.locator("main")).toBeVisible();
|
||||
await expect(page.locator("aside")).toBeVisible();
|
||||
|
||||
// Body should contain meaningful content (not blank)
|
||||
const bodyText = await page.textContent("body");
|
||||
expect(bodyText).toBeDefined();
|
||||
expect(bodyText?.length).toBeGreaterThan(0);
|
||||
|
||||
await page.unroute("**/api/v1/tools/resize");
|
||||
});
|
||||
|
||||
test("network timeout shows error, not infinite spinner", async ({ loggedInPage: page }) => {
|
||||
await page.goto("/resize");
|
||||
await uploadTestImage(page);
|
||||
|
||||
Reference in New Issue
Block a user