diff --git a/apps/web/src/pages/AdminSettingsPage.vue b/apps/web/src/pages/AdminSettingsPage.vue index 7130741..a994940 100644 --- a/apps/web/src/pages/AdminSettingsPage.vue +++ b/apps/web/src/pages/AdminSettingsPage.vue @@ -33,7 +33,15 @@ async function save() { message.value = ''; error.value = ''; try { - Object.assign(settings, await updateAdminSettings(settings, csrfToken())); + const payload = { + ...settings, + general: { + ...settings.general, + publicContactEmail: settings.general.publicContactEmail || undefined, + publicContactPhone: settings.general.publicContactPhone || undefined, + }, + }; + Object.assign(settings, await updateAdminSettings(payload, csrfToken())); message.value = 'Settings saved.'; } catch (requestError) { error.value = requestError.message; diff --git a/apps/web/src/services/admin-saved-view-service.js b/apps/web/src/services/admin-saved-view-service.js index a05ce32..ec78077 100644 --- a/apps/web/src/services/admin-saved-view-service.js +++ b/apps/web/src/services/admin-saved-view-service.js @@ -4,7 +4,10 @@ async function request(path, options = {}) { const response = await fetch(`${apiBaseUrl}${path}`, { credentials: 'include', ...options, - headers: { 'content-type': 'application/json', ...options.headers }, + headers: { + ...(options.body ? { 'content-type': 'application/json' } : {}), + ...options.headers, + }, }); const payload = await response.json().catch(() => null); if (!response.ok) throw new Error(payload?.error?.message ?? 'Saved view request failed.'); diff --git a/apps/web/tests/e2e/admin-catalog-workflow.spec.js b/apps/web/tests/e2e/admin-catalog-workflow.spec.js index 70913c3..9dca1fd 100644 --- a/apps/web/tests/e2e/admin-catalog-workflow.spec.js +++ b/apps/web/tests/e2e/admin-catalog-workflow.spec.js @@ -179,3 +179,41 @@ test("signs in and publishes a listing after adding an inventory image", async ( .getByText(`${createdInventoryId} ยท ACTIVE`), ).toBeVisible(); }); + +test("manages settings, saved views, and export controls", async ({ page }) => { + const email = process.env.INITIAL_ADMIN_EMAIL; + const password = process.env.INITIAL_ADMIN_PASSWORD; + + test.skip( + !email || !password, + "Set INITIAL_ADMIN_EMAIL and INITIAL_ADMIN_PASSWORD for the local authenticated workflow.", + ); + + await page.goto("/login"); + await page.getByLabel("Email").fill(email); + await page.getByLabel("Password").fill(password); + await page.getByRole("button", { name: "Sign in" }).click(); + await expect(page).toHaveURL(/\/admin$/); + await expect( + page.getByRole("link", { name: "Download inventory JSON" }), + ).toHaveAttribute("download", "inventory.json"); + + await page.goto("/admin/settings"); + await expect(page.getByRole("heading", { name: "Settings" })).toBeVisible(); + await page.getByLabel("Maximum images per item").fill("12"); + await page.getByRole("button", { name: "Save settings" }).click(); + await expect(page.getByRole("status")).toContainText("Settings saved."); + + await page.goto("/admin/saved-views"); + await expect( + page.getByRole("heading", { name: "Saved views", exact: true }), + ).toBeVisible(); + const viewName = `Browser view ${Date.now()}`; + await page.getByLabel("Name").fill(viewName); + await page.getByLabel("Filters (JSON)").fill('{"status":"ACTIVE"}'); + await page.getByRole("button", { name: "Save view" }).click(); + const savedView = page.getByRole("listitem").filter({ hasText: viewName }); + await expect(savedView).toBeVisible(); + await savedView.getByRole("button", { name: "Delete" }).click(); + await expect(savedView).toHaveCount(0); +}); diff --git a/docs/IMPLEMENTATION-STATUS.md b/docs/IMPLEMENTATION-STATUS.md index 6203753..84f3b2e 100644 --- a/docs/IMPLEMENTATION-STATUS.md +++ b/docs/IMPLEMENTATION-STATUS.md @@ -159,6 +159,7 @@ Remaining roadmap areas are approved external provider credentials/adapters, bro - Completed asynchronous inventory JSON exports can be downloaded through `GET /api/v1/admin/exports/jobs/:id/download`. - PostgreSQL restore verification and image snapshot restore scripts are implemented and covered by shell tests as part of the deployment drill workflow. - `npm audit --omit=dev` currently reports zero production vulnerabilities. Re-run the audit after dependency changes and review development-dependency findings separately before production releases. +- Authenticated E2E coverage now verifies both the core catalog workflow and the Settings, Saved Views, and export-controls workflow. ## Local Commands diff --git a/docs/TESTING.md b/docs/TESTING.md index a0f09fa..c4e4b76 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -69,6 +69,10 @@ Core workflows: 9. Record sale 10. View dashboard +The authenticated browser suite currently covers the core catalog workflow and +the Settings, Saved Views, and export-controls workflow. Both require the local +admin credentials and services described in the repository setup docs. + ## Accessibility Run automated accessibility checks and maintain manual keyboard/assistive technology coverage for critical workflows.