mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
1. passport-photo 404 vs 501: add base route at /api/v1/tools/passport-photo
that returns 501 FEATURE_NOT_INSTALLED when the AI bundle is missing,
matching other AI tools. The /generate sub-route is Sharp-only (no
sidecar) so it correctly skips the isToolInstalled guard.
2. AuthGuard analytics consent race: don't evaluate shouldShowConsent()
until analyticsConfig has been fetched (guard on analyticsConfig !== null).
Prevents redirect to /analytics-consent before config is loaded.
3. Fragile sidebar Settings selector: add openSettings(page) helper to
E2E helpers that checks sidebar visibility with fallback to button role.
Replace all 134 occurrences of page.locator("aside").getByText("Settings")
across 18 test files.
108 lines
3.5 KiB
TypeScript
108 lines
3.5 KiB
TypeScript
import { expect, openSettings, test } from "./helpers";
|
||
|
||
test.describe("Settings Dialog", () => {
|
||
test("opens from sidebar", async ({ loggedInPage: page }) => {
|
||
await openSettings(page);
|
||
|
||
// Settings dialog should appear with sections
|
||
await expect(page.getByText("General").first()).toBeVisible();
|
||
});
|
||
|
||
test("General section shows user info", async ({ loggedInPage: page }) => {
|
||
await openSettings(page);
|
||
|
||
// Should show username and version info
|
||
await expect(page.getByText(/admin/i).first()).toBeVisible();
|
||
await expect(page.getByText(/0\.1\.0|version/i).first()).toBeVisible();
|
||
});
|
||
|
||
test("General section has logout button", async ({ loggedInPage: page }) => {
|
||
await openSettings(page);
|
||
|
||
const logoutBtn = page.getByRole("button", { name: /logout|log out/i });
|
||
await expect(logoutBtn).toBeVisible();
|
||
});
|
||
|
||
test("Security section has change password form", async ({ loggedInPage: page }) => {
|
||
await openSettings(page);
|
||
|
||
// Navigate to Security section
|
||
await page.getByText("Security").click();
|
||
|
||
// Should show password change fields
|
||
await expect(page.getByText(/change password|current password/i).first()).toBeVisible();
|
||
});
|
||
|
||
test("People section shows user list", async ({ loggedInPage: page }) => {
|
||
await openSettings(page);
|
||
|
||
// Navigate to People section
|
||
await page.getByText("People").click();
|
||
|
||
// Should show admin user
|
||
await expect(page.getByText("admin").first()).toBeVisible();
|
||
});
|
||
|
||
// TODO: This test is flaky due to dialog state isolation — the API keys
|
||
// endpoint is verified via the API test suite (api.spec.ts)
|
||
test.skip("API Keys section has generate button", async ({ browser }) => {
|
||
// Use a fresh context to avoid dialog state from previous tests
|
||
const context = await browser.newContext({
|
||
storageState: "test-results/.auth/user.json",
|
||
});
|
||
const page = await context.newPage();
|
||
await page.goto("/");
|
||
await page.waitForLoadState("networkidle");
|
||
|
||
await openSettings(page);
|
||
await page.waitForTimeout(500);
|
||
|
||
const apiKeysBtn = page.getByRole("button", { name: /api keys/i });
|
||
await expect(apiKeysBtn).toBeVisible({ timeout: 5_000 });
|
||
await apiKeysBtn.click();
|
||
await page.waitForTimeout(500);
|
||
|
||
await expect(page.getByRole("button", { name: /generate api key/i })).toBeVisible({
|
||
timeout: 5_000,
|
||
});
|
||
|
||
await context.close();
|
||
});
|
||
|
||
test("About section shows app info", async ({ loggedInPage: page }) => {
|
||
await openSettings(page);
|
||
|
||
// Navigate to About section
|
||
await page.getByText("About").click();
|
||
|
||
// Should show app description
|
||
await expect(page.getByText(/snapotter|privacy|self-hosted/i).first()).toBeVisible();
|
||
});
|
||
|
||
test("System Settings section has configuration", async ({ loggedInPage: page }) => {
|
||
await openSettings(page);
|
||
|
||
// Navigate to System Settings section
|
||
await page.getByText("System Settings").click();
|
||
|
||
// Should show system configuration options
|
||
await expect(page.getByText(/app name|upload limit|theme/i).first()).toBeVisible();
|
||
});
|
||
|
||
test("settings dialog can be closed", async ({ loggedInPage: page }) => {
|
||
await openSettings(page);
|
||
await expect(page.getByText("General").first()).toBeVisible();
|
||
|
||
// Close by clicking X or outside
|
||
const closeBtn = page.getByRole("button", { name: /close|×/i }).first();
|
||
if (await closeBtn.isVisible()) {
|
||
await closeBtn.click();
|
||
} else {
|
||
await page.keyboard.press("Escape");
|
||
}
|
||
|
||
// Dialog should be gone
|
||
await page.waitForTimeout(300);
|
||
});
|
||
});
|