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
This commit is contained in:
counterweight 2025-12-26 21:07:56 +01:00
parent 1874c3a057
commit 1ef5ebe493
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
2 changed files with 64 additions and 38 deletions

View file

@ -49,6 +49,7 @@ export default function AdminPricingPage() {
const [errors, setErrors] = useState<ValidationErrors>({}); const [errors, setErrors] = useState<ValidationErrors>({});
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false); const [success, setSuccess] = useState(false);
const [isConfirming, setIsConfirming] = useState(false);
const fetchConfig = useCallback(async () => { const fetchConfig = useCallback(async () => {
setError(null); setError(null);
@ -180,12 +181,16 @@ export default function AdminPricingPage() {
}, },
}); });
const handleSave = () => { const handleConfirmSave = () => {
if (!formData) return; if (!formData) {
setIsConfirming(false);
return;
}
const validationErrors = validateForm(formData); const validationErrors = validateForm(formData);
if (Object.keys(validationErrors).length > 0) { if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors); setErrors(validationErrors);
setIsConfirming(false);
return; return;
} }
@ -199,6 +204,7 @@ export default function AdminPricingPage() {
eur_min_sell: formData.eur_min_sell, eur_min_sell: formData.eur_min_sell,
eur_max_sell: formData.eur_max_sell, eur_max_sell: formData.eur_max_sell,
}); });
setIsConfirming(false);
}; };
if (isLoading || !formData) { if (isLoading || !formData) {
@ -385,16 +391,19 @@ export default function AdminPricingPage() {
<div style={formStyles.actions}> <div style={formStyles.actions}>
<ConfirmationButton <ConfirmationButton
onClick={handleSave} isConfirming={isConfirming}
disabled={hasErrors || isSaving} onConfirm={handleConfirmSave}
confirmMessage={t("pricing.confirmSave")} onCancel={() => setIsConfirming(false)}
style={{ onActionClick={() => setIsConfirming(true)}
actionLabel={isSaving ? tCommon("saving") : t("pricing.save")}
confirmLabel={tCommon("confirm")}
cancelLabel={tCommon("cancel")}
isLoading={isSaving}
actionButtonStyle={{
...buttonStyles.primary, ...buttonStyles.primary,
...(hasErrors || isSaving ? buttonStyles.disabled : {}), ...(hasErrors || isSaving ? buttonStyles.disabled : {}),
}} }}
> />
{isSaving ? tCommon("saving") : t("pricing.save")}
</ConfirmationButton>
</div> </div>
</form> </form>
</div> </div>

View file

@ -24,16 +24,18 @@ test.describe("Admin Pricing Page - Admin Access", () => {
await expect(pricingLink).toBeVisible(); await expect(pricingLink).toBeVisible();
// Test page access and structure // 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 page.goto("/admin/pricing");
await expect(page).toHaveURL("/admin/pricing"); await expect(page).toHaveURL("/admin/pricing");
// Wait for API call to complete and form to render // Wait for API call to complete and form to render
await page.waitForResponse( await responsePromise;
(resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET" // 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.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible();
timeout: 10000,
});
await expect(page.getByText("Configure premium pricing and trade amount limits")).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) // 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 }) => { 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"); await page.goto("/admin/pricing");
// Wait for API call to complete and form to render // Wait for API call to complete and form to render
await page.waitForResponse( await responsePromise;
(resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET"
);
await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({ await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({
timeout: 10000, timeout: 10000,
}); });
@ -77,12 +81,14 @@ test.describe("Admin Pricing Page - Admin Access", () => {
}); });
test("can update pricing configuration", async ({ page }) => { 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"); await page.goto("/admin/pricing");
// Wait for API call to complete and form to render // Wait for API call to complete and form to render
await page.waitForResponse( await responsePromise;
(resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET"
);
await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({ await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({
timeout: 10000, timeout: 10000,
}); });
@ -98,6 +104,13 @@ test.describe("Admin Pricing Page - Admin Access", () => {
await premiumBuyInput.clear(); await premiumBuyInput.clear();
await premiumBuyInput.fill(newBuyValue); 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 // Set up response listener before clicking save
const putPromise = page.waitForResponse( const putPromise = page.waitForResponse(
(resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "PUT" (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" (resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET"
); );
// Click save button (with confirmation) // Click save button (enters confirmation mode)
await page.getByRole("button", { name: /Save Changes/i }).click(); await saveButton.click();
// Confirm the save action // Confirm the save action (button text changes to "Confirm")
await expect(page.getByText(/Are you sure/i)).toBeVisible(); await expect(page.getByRole("button", { name: /Confirm/i })).toBeVisible({ timeout: 5000 });
await page.getByRole("button", { name: /confirm|yes|ok/i }).click(); await page.getByRole("button", { name: /Confirm/i }).click();
// Wait for API calls to complete // Wait for API calls to complete
await putPromise; await putPromise;
@ -127,12 +140,14 @@ test.describe("Admin Pricing Page - Admin Access", () => {
}); });
test("validation prevents invalid values", async ({ page }) => { 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"); await page.goto("/admin/pricing");
// Wait for API call to complete and form to render // Wait for API call to complete and form to render
await page.waitForResponse( await responsePromise;
(resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET"
);
await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({ await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({
timeout: 10000, timeout: 10000,
}); });
@ -146,8 +161,8 @@ test.describe("Admin Pricing Page - Admin Access", () => {
// Try to save // Try to save
await page.getByRole("button", { name: /Save Changes/i }).click(); await page.getByRole("button", { name: /Save Changes/i }).click();
await expect(page.getByText(/confirm|yes|ok/i)).toBeVisible(); await expect(page.getByRole("button", { name: /Confirm/i })).toBeVisible({ timeout: 2000 });
await page.getByRole("button", { name: /confirm|yes|ok/i }).click(); await page.getByRole("button", { name: /Confirm/i }).click();
// Should show validation error // Should show validation error
await expect(page.getByText(/must be between.*-100.*100/i)).toBeVisible({ timeout: 2000 }); 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 // Try to save
await page.getByRole("button", { name: /Save Changes/i }).click(); await page.getByRole("button", { name: /Save Changes/i }).click();
await expect(page.getByText(/confirm|yes|ok/i)).toBeVisible(); await expect(page.getByRole("button", { name: /Confirm/i })).toBeVisible({ timeout: 2000 });
await page.getByRole("button", { name: /confirm|yes|ok/i }).click(); await page.getByRole("button", { name: /Confirm/i }).click();
// Should show validation error // Should show validation error (there are two - one for min, one for max)
await expect(page.getByText(/minimum must be less than maximum/i)).toBeVisible({ await expect(page.getByText(/minimum must be less than maximum/i).first()).toBeVisible({
timeout: 2000, timeout: 2000,
}); });
}); });
test("form fields update correctly when values change", async ({ page }) => { 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"); await page.goto("/admin/pricing");
// Wait for API call to complete and form to render // Wait for API call to complete and form to render
await page.waitForResponse( await responsePromise;
(resp) => resp.url().includes("/api/admin/pricing") && resp.request().method() === "GET"
);
await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({ await expect(page.getByRole("heading", { name: "Pricing Configuration" })).toBeVisible({
timeout: 10000, timeout: 10000,
}); });