# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: upi_pay.spec.js >> Payee Search >> clear button removes selected payee
- Location: tests/upi_pay.spec.js:238:3

# Error details

```
Test timeout of 30000ms exceeded while running "beforeEach" hook.
```

```
Error: locator.click: Test timeout of 30000ms exceeded.
Call log:
  - waiting for locator('.btn-cta')

```

# Page snapshot

```yaml
- generic [ref=e2]: 404 page not found
```

# Test source

```ts
  119 | test.describe('Reason Chips', () => {
  120 |   test.beforeEach(async ({ page }) => {
  121 |     await injectSession(page, SESSION.pramod);
  122 |     await page.goto(BASE_URL);
  123 |     await page.locator('.btn-cta').click();
  124 |     await expect(page.locator('#s-pay')).toHaveClass(/active/, { timeout: 3000 });
  125 |   });
  126 | 
  127 |   test('Exp. Settlement chip selectable', async ({ page }) => {
  128 |     await page.locator('#reason-chips .chip', { hasText: 'Exp. Settlement' }).click();
  129 |     await expect(page.locator('#reason-chips .chip', { hasText: 'Exp. Settlement' })).toHaveClass(/sel/);
  130 |   });
  131 | 
  132 |   test('Custodian Transfer chip shows notice', async ({ page }) => {
  133 |     await page.locator('#reason-chips .chip', { hasText: 'Custodian Transfer' }).click();
  134 |     await expect(page.locator('#reason-notice')).toBeVisible();
  135 |   });
  136 | 
  137 |   test('switching reason hides Custodian Transfer notice', async ({ page }) => {
  138 |     await page.locator('#reason-chips .chip', { hasText: 'Custodian Transfer' }).click();
  139 |     await expect(page.locator('#reason-notice')).toBeVisible();
  140 |     await page.locator('#reason-chips .chip', { hasText: 'Adv. Salary' }).click();
  141 |     await expect(page.locator('#reason-notice')).not.toBeVisible();
  142 |   });
  143 | });
  144 | 
  145 | // ─────────────────────────────────────────────────────────────────
  146 | // PAYMENT FORM — mode chips
  147 | // ─────────────────────────────────────────────────────────────────
  148 | test.describe('Mode Chips', () => {
  149 |   test.beforeEach(async ({ page }) => {
  150 |     await injectSession(page, SESSION.pramod);
  151 |     await page.goto(BASE_URL);
  152 |     await page.locator('.btn-cta').click();
  153 |     await expect(page.locator('#s-pay')).toHaveClass(/active/, { timeout: 3000 });
  154 |   });
  155 | 
  156 |   test('UPI selected by default, UPI section visible', async ({ page }) => {
  157 |     await expect(page.locator('#mode-chips [data-mode="upi"]')).toHaveClass(/sel/);
  158 |     await expect(page.locator('#upi-section')).toBeVisible();
  159 |   });
  160 | 
  161 |   test('NEFT mode: UPI section hidden, ref label is UTR No.', async ({ page }) => {
  162 |     await page.locator('#mode-chips [data-mode="neft"]').click();
  163 |     await expect(page.locator('#upi-section')).not.toBeVisible();
  164 |     await expect(page.locator('#ref-label')).toHaveText('UTR No.');
  165 |   });
  166 | 
  167 |   test('RTGS mode: UPI section hidden, ref label is UTR No.', async ({ page }) => {
  168 |     await page.locator('#mode-chips [data-mode="rtgs"]').click();
  169 |     await expect(page.locator('#upi-section')).not.toBeVisible();
  170 |     await expect(page.locator('#ref-label')).toHaveText('UTR No.');
  171 |   });
  172 | 
  173 |   test('IMPS mode: UPI section hidden', async ({ page }) => {
  174 |     await page.locator('#mode-chips [data-mode="imps"]').click();
  175 |     await expect(page.locator('#upi-section')).not.toBeVisible();
  176 |   });
  177 | 
  178 |   test('UPI mode: ref label is UPI Ref No.', async ({ page }) => {
  179 |     await expect(page.locator('#ref-label')).toHaveText('UPI Ref No.');
  180 |   });
  181 | 
  182 |   test('switching back to UPI shows UPI section', async ({ page }) => {
  183 |     await page.locator('#mode-chips [data-mode="neft"]').click();
  184 |     await page.locator('#mode-chips [data-mode="upi"]').click();
  185 |     await expect(page.locator('#upi-section')).toBeVisible();
  186 |   });
  187 | });
  188 | 
  189 | // ─────────────────────────────────────────────────────────────────
  190 | // UPI APP PICKER
  191 | // ─────────────────────────────────────────────────────────────────
  192 | test.describe('UPI App Picker', () => {
  193 |   test.beforeEach(async ({ page }) => {
  194 |     await injectSession(page, SESSION.pramod);
  195 |     await page.goto(BASE_URL);
  196 |     await page.locator('.btn-cta').click();
  197 |     await expect(page.locator('#upi-app-row')).toBeVisible({ timeout: 3000 });
  198 |   });
  199 | 
  200 |   test('4 UPI app buttons rendered', async ({ page }) => {
  201 |     const count = await page.locator('#upi-app-row .upi-app-btn').count();
  202 |     expect(count).toBe(4);
  203 |   });
  204 | 
  205 |   test('tapping an app button selects it', async ({ page }) => {
  206 |     await page.locator('#upi-app-row .upi-app-btn', { hasText: 'GPay' }).click();
  207 |     await expect(page.locator('#upi-app-row .upi-app-btn', { hasText: 'GPay' })).toHaveClass(/sel/);
  208 |     await expect(page.locator('#launch-app-lbl')).toHaveText('GPay');
  209 |   });
  210 | });
  211 | 
  212 | // ─────────────────────────────────────────────────────────────────
  213 | // PAYEE SEARCH
  214 | // ─────────────────────────────────────────────────────────────────
  215 | test.describe('Payee Search', () => {
  216 |   test.beforeEach(async ({ page }) => {
  217 |     await injectSession(page, SESSION.pramod);
  218 |     await page.goto(BASE_URL);
> 219 |     await page.locator('.btn-cta').click();
      |                                    ^ Error: locator.click: Test timeout of 30000ms exceeded.
  220 |     await expect(page.locator('#payee-q')).toBeVisible({ timeout: 3000 });
  221 |   });
  222 | 
  223 |   test('typing 2+ chars triggers search and shows dropdown', async ({ page }) => {
  224 |     await page.locator('#payee-q').fill('ash');
  225 |     await expect(page.locator('#payee-drop')).toBeVisible({ timeout: 4000 });
  226 |     const count = await page.locator('#payee-drop .pay-res').count();
  227 |     expect(count).toBeGreaterThan(0);
  228 |   });
  229 | 
  230 |   test('selecting a result shows payee card', async ({ page }) => {
  231 |     await page.locator('#payee-q').fill('ash');
  232 |     await expect(page.locator('#payee-drop .pay-res').first()).toBeVisible({ timeout: 4000 });
  233 |     await page.locator('#payee-drop .pay-res').first().click();
  234 |     await expect(page.locator('#payee-card .payee-sel')).toBeVisible();
  235 |     await expect(page.locator('#payee-drop')).not.toBeVisible();
  236 |   });
  237 | 
  238 |   test('clear button removes selected payee', async ({ page }) => {
  239 |     await page.locator('#payee-q').fill('ash');
  240 |     await expect(page.locator('#payee-drop .pay-res').first()).toBeVisible({ timeout: 4000 });
  241 |     await page.locator('#payee-drop .pay-res').first().click();
  242 |     await page.locator('.payee-clr').click();
  243 |     await expect(page.locator('#payee-card')).not.toBeVisible();
  244 |     await expect(page.locator('#payee-q')).toHaveValue('');
  245 |   });
  246 | 
  247 |   test('searching for harish returns Harish Kumar Lal', async ({ page }) => {
  248 |     await page.locator('#payee-q').fill('harish');
  249 |     await expect(page.locator('#payee-drop')).toBeVisible({ timeout: 4000 });
  250 |     await expect(page.locator('#payee-drop .pay-res-name', { hasText: 'Harish Kumar Lal' })).toBeVisible();
  251 |   });
  252 | });
  253 | 
  254 | // ─────────────────────────────────────────────────────────────────
  255 | // FORM VALIDATION
  256 | // ─────────────────────────────────────────────────────────────────
  257 | test.describe('Form Validation', () => {
  258 |   test.beforeEach(async ({ page }) => {
  259 |     await injectSession(page, SESSION.pramod);
  260 |     await page.goto(BASE_URL);
  261 |     await page.locator('.btn-cta').click();
  262 |     await expect(page.locator('#pay-submit')).toBeVisible({ timeout: 3000 });
  263 |   });
  264 | 
  265 |   test('submit with no payee shows error toast', async ({ page }) => {
  266 |     await page.locator('#pay-amount').fill('1000');
  267 |     await page.locator('#reason-chips .chip').first().click();
  268 |     await page.locator('#pay-submit').click();
  269 |     await expect(page.locator('#toast')).toContainText('Select a payee', { timeout: 3000 });
  270 |   });
  271 | 
  272 |   test('submit with no amount shows error toast', async ({ page }) => {
  273 |     await page.locator('#payee-q').fill('ash');
  274 |     await expect(page.locator('#payee-drop .pay-res').first()).toBeVisible({ timeout: 4000 });
  275 |     await page.locator('#payee-drop .pay-res').first().click();
  276 |     await page.locator('#reason-chips .chip').first().click();
  277 |     await page.locator('#pay-submit').click();
  278 |     await expect(page.locator('#toast')).toContainText('valid amount', { timeout: 3000 });
  279 |   });
  280 | 
  281 |   test('submit with no reason shows error toast', async ({ page }) => {
  282 |     await page.locator('#payee-q').fill('ash');
  283 |     await expect(page.locator('#payee-drop .pay-res').first()).toBeVisible({ timeout: 4000 });
  284 |     await page.locator('#payee-drop .pay-res').first().click();
  285 |     await page.locator('#pay-amount').fill('1000');
  286 |     await page.locator('#pay-submit').click();
  287 |     await expect(page.locator('#toast')).toContainText('reason', { timeout: 3000 });
  288 |   });
  289 | 
  290 |   test('clear form resets all fields', async ({ page }) => {
  291 |     await page.locator('#pay-amount').fill('5000');
  292 |     await page.locator('#pay-notes').fill('test');
  293 |     await page.locator('#reason-chips .chip').first().click();
  294 |     await page.locator('.btn-clr').click();
  295 |     await expect(page.locator('#pay-amount')).toHaveValue('');
  296 |     await expect(page.locator('#pay-notes')).toHaveValue('');
  297 |     // Reason chips all unselected
  298 |     const selChips = await page.locator('#reason-chips .chip.sel').count();
  299 |     expect(selChips).toBe(0);
  300 |   });
  301 | });
  302 | 
  303 | // ─────────────────────────────────────────────────────────────────
  304 | // URL PARAM PRE-FILL (custodian transfer)
  305 | // ─────────────────────────────────────────────────────────────────
  306 | test.describe('URL Param Pre-fill', () => {
  307 |   test('context=custodian-transfer pre-selects reason and opens pay screen', async ({ page }) => {
  308 |     await injectSession(page, SESSION.pramod);
  309 |     await page.goto(BASE_URL + '?context=custodian-transfer&to=shambhu&amount=30000');
  310 |     await expect(page.locator('#s-pay')).toHaveClass(/active/, { timeout: 6000 });
  311 |     await expect(page.locator('#reason-chips .chip', { hasText: 'Custodian Transfer' })).toHaveClass(/sel/);
  312 |     await expect(page.locator('#pay-amount')).toHaveValue('30000');
  313 |     await expect(page.locator('#reason-notice')).toBeVisible();
  314 |   });
  315 | 
  316 |   test('today date is always pre-filled', async ({ page }) => {
  317 |     await injectSession(page, SESSION.pramod);
  318 |     await page.goto(BASE_URL);
  319 |     await page.locator('.btn-cta').click();
```