From 1ef5ebe493a3a9ad4861538cfd811e7e4a785c3f Mon Sep 17 00:00:00 2001 From: counterweight Date: Fri, 26 Dec 2025 21:07:56 +0100 Subject: [PATCH] Fix ConfirmationButton usage in pricing page and e2e tests - Update pricing page to use ConfirmationButton API correctly (isConfirming, onConfirm, onCancel, onActionClick) - Fix e2e tests to set up response listeners before navigation - Fix validation error selector to use .first() for multiple matches - All 9 e2e tests now passing --- frontend/app/admin/pricing/page.tsx | 27 +++++++---- frontend/e2e/admin-pricing.spec.ts | 75 ++++++++++++++++++----------- 2 files changed, 64 insertions(+), 38 deletions(-) diff --git a/frontend/app/admin/pricing/page.tsx b/frontend/app/admin/pricing/page.tsx index 9db4a0d..3018b48 100644 --- a/frontend/app/admin/pricing/page.tsx +++ b/frontend/app/admin/pricing/page.tsx @@ -49,6 +49,7 @@ export default function AdminPricingPage() { const [errors, setErrors] = useState({}); const [error, setError] = useState(null); const [success, setSuccess] = useState(false); + const [isConfirming, setIsConfirming] = useState(false); const fetchConfig = useCallback(async () => { setError(null); @@ -180,12 +181,16 @@ export default function AdminPricingPage() { }, }); - const handleSave = () => { - if (!formData) return; + const handleConfirmSave = () => { + if (!formData) { + setIsConfirming(false); + return; + } const validationErrors = validateForm(formData); if (Object.keys(validationErrors).length > 0) { setErrors(validationErrors); + setIsConfirming(false); return; } @@ -199,6 +204,7 @@ export default function AdminPricingPage() { eur_min_sell: formData.eur_min_sell, eur_max_sell: formData.eur_max_sell, }); + setIsConfirming(false); }; if (isLoading || !formData) { @@ -385,16 +391,19 @@ export default function AdminPricingPage() {
setIsConfirming(false)} + onActionClick={() => setIsConfirming(true)} + actionLabel={isSaving ? tCommon("saving") : t("pricing.save")} + confirmLabel={tCommon("confirm")} + cancelLabel={tCommon("cancel")} + isLoading={isSaving} + actionButtonStyle={{ ...buttonStyles.primary, ...(hasErrors || isSaving ? buttonStyles.disabled : {}), }} - > - {isSaving ? tCommon("saving") : t("pricing.save")} - + />
diff --git a/frontend/e2e/admin-pricing.spec.ts b/frontend/e2e/admin-pricing.spec.ts index 27b8f61..c10b5af 100644 --- a/frontend/e2e/admin-pricing.spec.ts +++ b/frontend/e2e/admin-pricing.spec.ts @@ -24,16 +24,18 @@ test.describe("Admin Pricing Page - Admin Access", () => { await expect(pricingLink).toBeVisible(); // Test page access and structure + // Set up response listener before navigation + const responsePromise = page.waitForResponse( + (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" + ); await page.goto("/admin/pricing"); await expect(page).toHaveURL("/admin/pricing"); // Wait for API call to complete and form to render - await page.waitForResponse( - (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" - ); - await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({ - timeout: 10000, - }); + await responsePromise; + // Wait for form inputs to be visible (indicates form has loaded) + await expect(page.locator('input[type="number"]').first()).toBeVisible({ timeout: 10000 }); + await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible(); await expect(page.getByText("Configure premium pricing and trade amount limits")).toBeVisible(); // Check all form fields are present (using text + input selector since labels aren't associated) @@ -52,12 +54,14 @@ test.describe("Admin Pricing Page - Admin Access", () => { }); test("can view current pricing configuration", async ({ page }) => { + // Set up response listener before navigation + const responsePromise = page.waitForResponse( + (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" + ); await page.goto("/admin/pricing"); // Wait for API call to complete and form to render - await page.waitForResponse( - (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" - ); + await responsePromise; await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({ timeout: 10000, }); @@ -77,12 +81,14 @@ test.describe("Admin Pricing Page - Admin Access", () => { }); test("can update pricing configuration", async ({ page }) => { + // Set up response listener before navigation + const responsePromise = page.waitForResponse( + (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" + ); await page.goto("/admin/pricing"); // Wait for API call to complete and form to render - await page.waitForResponse( - (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" - ); + await responsePromise; await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({ timeout: 10000, }); @@ -98,6 +104,13 @@ test.describe("Admin Pricing Page - Admin Access", () => { await premiumBuyInput.clear(); await premiumBuyInput.fill(newBuyValue); + // Wait a bit for any validation to clear + await page.waitForTimeout(200); + + // Verify save button is enabled before clicking + const saveButton = page.getByRole("button", { name: /Save Changes/i }); + await expect(saveButton).toBeEnabled({ timeout: 5000 }); + // Set up response listener before clicking save const putPromise = page.waitForResponse( (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "PUT" @@ -106,12 +119,12 @@ test.describe("Admin Pricing Page - Admin Access", () => { (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" ); - // Click save button (with confirmation) - await page.getByRole("button", { name: /Save Changes/i }).click(); + // Click save button (enters confirmation mode) + await saveButton.click(); - // Confirm the save action - await expect(page.getByText(/Are you sure/i)).toBeVisible(); - await page.getByRole("button", { name: /confirm|yes|ok/i }).click(); + // Confirm the save action (button text changes to "Confirm") + await expect(page.getByRole("button", { name: /Confirm/i })).toBeVisible({ timeout: 5000 }); + await page.getByRole("button", { name: /Confirm/i }).click(); // Wait for API calls to complete await putPromise; @@ -127,12 +140,14 @@ test.describe("Admin Pricing Page - Admin Access", () => { }); test("validation prevents invalid values", async ({ page }) => { + // Set up response listener before navigation + const responsePromise = page.waitForResponse( + (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" + ); await page.goto("/admin/pricing"); // Wait for API call to complete and form to render - await page.waitForResponse( - (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" - ); + await responsePromise; await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({ timeout: 10000, }); @@ -146,8 +161,8 @@ test.describe("Admin Pricing Page - Admin Access", () => { // Try to save await page.getByRole("button", { name: /Save Changes/i }).click(); - await expect(page.getByText(/confirm|yes|ok/i)).toBeVisible(); - await page.getByRole("button", { name: /confirm|yes|ok/i }).click(); + await expect(page.getByRole("button", { name: /Confirm/i })).toBeVisible({ timeout: 2000 }); + await page.getByRole("button", { name: /Confirm/i }).click(); // Should show validation error await expect(page.getByText(/must be between.*-100.*100/i)).toBeVisible({ timeout: 2000 }); @@ -167,22 +182,24 @@ test.describe("Admin Pricing Page - Admin Access", () => { // Try to save await page.getByRole("button", { name: /Save Changes/i }).click(); - await expect(page.getByText(/confirm|yes|ok/i)).toBeVisible(); - await page.getByRole("button", { name: /confirm|yes|ok/i }).click(); + await expect(page.getByRole("button", { name: /Confirm/i })).toBeVisible({ timeout: 2000 }); + await page.getByRole("button", { name: /Confirm/i }).click(); - // Should show validation error - await expect(page.getByText(/minimum must be less than maximum/i)).toBeVisible({ + // Should show validation error (there are two - one for min, one for max) + await expect(page.getByText(/minimum must be less than maximum/i).first()).toBeVisible({ timeout: 2000, }); }); test("form fields update correctly when values change", async ({ page }) => { + // Set up response listener before navigation + const responsePromise = page.waitForResponse( + (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" + ); await page.goto("/admin/pricing"); // Wait for API call to complete and form to render - await page.waitForResponse( - (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" - ); + await responsePromise; await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({ timeout: 10000, });