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:
SnapOtter
2026-05-15 21:35:02 +08:00
parent d38621d7b9
commit 3b181dd1ac
24 changed files with 3398 additions and 0 deletions
+49
View File
@@ -87,6 +87,34 @@ test.describe("Pipeline Builder - Empty state", () => {
await expect(page.getByPlaceholder("Search tools...")).toBeVisible();
});
test("tool palette search filters results", async ({ loggedInPage: page }) => {
await gotoAutomate(page);
const searchInput = page.getByPlaceholder("Search tools...");
await expect(searchInput).toBeVisible();
// Type "resize" to filter the palette
await searchInput.fill("resize");
await page.waitForTimeout(300);
// A Resize button should be visible in the filtered results
await expect(page.getByRole("button", { name: /resize/i }).first()).toBeVisible();
// Tools unrelated to "resize" should not be visible (e.g. "Compress")
await expect(page.getByRole("button", { name: /^compress$/i }).first()).not.toBeVisible({
timeout: 1_000,
});
// Clear search and verify palette restores full list
await searchInput.clear();
await page.waitForTimeout(300);
// Compress should now be visible again in the full palette
await expect(page.getByRole("button", { name: /compress/i }).first()).toBeVisible({
timeout: 3_000,
});
});
test("process button is disabled when no steps or file", async ({ loggedInPage: page }) => {
await gotoAutomate(page);
@@ -149,6 +177,27 @@ test.describe("Pipeline Builder - Adding steps", () => {
await expect(page.locator(".border-primary").first()).toBeVisible({ timeout: 3_000 });
});
test("collapse expanded step hides settings form", async ({ loggedInPage: page }) => {
await gotoAutomate(page);
await addToolStep(page, "Resize", 1);
// Click on the step row to expand it
const stepRow = page.locator("[role='button']").filter({ hasText: "Resize" }).first();
await stepRow.click();
// Settings form should appear
const expandedStep = page.locator(".border-primary").first();
await expect(expandedStep).toBeVisible({ timeout: 3_000 });
// Click the step header again to collapse
await stepRow.click();
await page.waitForTimeout(300);
// Expanded state (border-primary on the step card) should be gone
await expect(expandedStep).not.toBeVisible({ timeout: 3_000 });
});
test("add 3 steps: resize, compress, convert - all visible in order", async ({
loggedInPage: page,
}) => {