import { test, expect } from '@playwright/test' import { loginAsOperator } from './helpers/auth.js' import { getPaymentSessionTimeout, setPaymentSessionTimeout, getExtensionDefaultAction, setExtensionDefaultAction, } from './helpers/backend-api.js' /** * Settings page e2e tests. * * These tests exercise two of the four Phase 3.7 config rows end-to-end: * - payment_session_timeout_minutes (number input) * - extension_default_action_on_timeout (radio) * * Strategy: * 1. Snapshot the current backend value before each test * 2. Mutate via the UI * 3. Reload to confirm persistence (rules out optimistic-only state) * 4. Restore the original value in afterEach (so the suite is rerunnable) */ test.describe('Settings page — Phase 3.7 config rows', () => { test.beforeEach(async ({ page }) => { await loginAsOperator(page) await page.goto('/settings') await expect(page.getByRole('heading', { name: 'Settings', level: 1 })).toBeVisible() }) // ------------------------------------------------------------------------- // payment_session_timeout_minutes // ------------------------------------------------------------------------- test.describe('Batas Waktu Sesi Pembayaran', () => { let originalValue test.beforeEach(async () => { originalValue = await getPaymentSessionTimeout() }) test.afterEach(async () => { if (typeof originalValue === 'number') { await setPaymentSessionTimeout(originalValue) } }) test('changing 20 → 25 persists across reload', async ({ page }) => { await setPaymentSessionTimeout(20) const section = page .locator('section', { has: page.getByRole('heading', { name: 'Batas Waktu Sesi Pembayaran' }) }) const input = section.getByRole('spinbutton') await page.reload() await expect(input).toHaveValue('20') // Mutate via the UI. The component fires a mutation on every keystroke // (no Save button), so .fill() with the final value is enough. // Wait for the PATCH to complete BEFORE reloading — disabled flip alone // is racy because fill() returns before React processes the onChange. const patchResponse = page.waitForResponse(r => r.url().includes('/internal/config/payment-session-timeout') && r.request().method() === 'PATCH' && r.status() === 200 ) await input.fill('25') await patchResponse // Reload and confirm the new value sticks. await page.reload() await expect(input).toHaveValue('25') // And confirm the backend agrees. const persisted = await getPaymentSessionTimeout() expect(persisted).toBe(25) }) }) // ------------------------------------------------------------------------- // extension_default_action_on_timeout (radio) // ------------------------------------------------------------------------- test.describe('Aksi Default Extension', () => { let originalValue test.beforeEach(async () => { originalValue = await getExtensionDefaultAction() }) test.afterEach(async () => { if (originalValue) { await setExtensionDefaultAction(originalValue) } }) test('flipping auto-approve <-> auto-reject persists across reload', async ({ page }) => { // Start from a known state. await setExtensionDefaultAction('auto_approve') const section = page .locator('section', { has: page.getByRole('heading', { name: 'Aksi Default jika Bestie Tidak Menjawab Extension' }) }) const approveRadio = section.getByRole('radio', { name: /Otomatis disetujui/ }) const rejectRadio = section.getByRole('radio', { name: /Otomatis ditolak/ }) await page.reload() await expect(approveRadio).toBeChecked() await expect(rejectRadio).not.toBeChecked() // Flip to auto-reject. Use click() instead of check() — check() races against // the React re-render that flips `checked` after the mutation roundtrip. // Wait for the PATCH to complete before asserting persistence. const patchToReject = page.waitForResponse(r => r.url().includes('/internal/config/extension-default-action') && r.request().method() === 'PATCH' && r.status() === 200 ) await rejectRadio.click() await patchToReject await page.reload() await expect(rejectRadio).toBeChecked() await expect(approveRadio).not.toBeChecked() expect(await getExtensionDefaultAction()).toBe('auto_reject') // Flip back to auto-approve. const patchToApprove = page.waitForResponse(r => r.url().includes('/internal/config/extension-default-action') && r.request().method() === 'PATCH' && r.status() === 200 ) await approveRadio.click() await patchToApprove await page.reload() await expect(approveRadio).toBeChecked() expect(await getExtensionDefaultAction()).toBe('auto_approve') }) }) })