# 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: fundcustodian.spec.js >> Transfer Form >> Cash mode: cheque extra fields hidden
- Location: tests/fundcustodian.spec.js:292: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('#nav-transfer')

```

# Page snapshot

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

# Test source

```ts
  125 |     await injectSession(page, SESSION.harish);
  126 |     await page.goto(BASE_URL);
  127 |     await handleModeSelector(page, 'overseer');
  128 |     await expect(page.locator('#my-balance')).toContainText('₹', { timeout: 10000 });
  129 |   });
  130 | 
  131 |   test('pending banner appears with count 2', async ({ page }) => {
  132 |     const banner = page.locator('.pend-banner');
  133 |     await expect(banner).toBeVisible();
  134 |     await expect(banner).toContainText('2 pending transfers');
  135 |   });
  136 | 
  137 |   test('pending banner shows from-name and amount for both', async ({ page }) => {
  138 |     await expect(page.locator('.pend-item-who', { hasText: 'Shambhu' })).toBeVisible();
  139 |     await expect(page.locator('.pend-item-who', { hasText: 'Gurpreet' })).toBeVisible();
  140 |     const amts = page.locator('.pend-item-amt');
  141 |     await expect(amts).toHaveCount(2);
  142 |   });
  143 | 
  144 |   test('pending item has Accept and Reject buttons', async ({ page }) => {
  145 |     await expect(page.locator('.btn-accept').first()).toBeVisible();
  146 |     await expect(page.locator('.btn-reject').first()).toBeVisible();
  147 |   });
  148 | 
  149 |   test('admin balance grid shows all 4 custodians', async ({ page }) => {
  150 |     await expect(page.locator('.bal-grid')).toBeVisible();
  151 |     const tiles = page.locator('.bal-tile');
  152 |     await expect(tiles).toHaveCount(4);
  153 |     await expect(page.locator('.bal-tile-name', { hasText: 'Pramod' })).toBeVisible();
  154 |     await expect(page.locator('.bal-tile-name', { hasText: 'Harish' })).toBeVisible();
  155 |     await expect(page.locator('.bal-tile-name', { hasText: 'Shambhu' })).toBeVisible();
  156 |     await expect(page.locator('.bal-tile-name', { hasText: 'Gurpreet' })).toBeVisible();
  157 |   });
  158 | 
  159 |   test('primary badge on Pramod tile', async ({ page }) => {
  160 |     const pramodTile = page.locator('.bal-tile').filter({ hasText: 'Pramod' });
  161 |     await expect(pramodTile.locator('.bal-tile-badge.primary')).toBeVisible();
  162 |   });
  163 | 
  164 |   test('manage custodian button visible for admin', async ({ page }) => {
  165 |     await expect(page.locator('#manage-hdr-btn')).toBeVisible();
  166 |   });
  167 | 
  168 |   test('admin sees all outstanding advances (not just own)', async ({ page }) => {
  169 |     // Mukesh appears twice (A1 from shambhu + A5 from harish) — admin sees all custodians' advances
  170 |     await expect(page.locator('.adv-name', { hasText: 'Mukesh' }).first()).toBeVisible();
  171 |     await expect(page.locator('.adv-name', { hasText: 'Umashankar' })).toBeVisible();
  172 |     const mukeshCount = await page.locator('.adv-name', { hasText: 'Mukesh' }).count();
  173 |     expect(mukeshCount).toBe(2);
  174 |   });
  175 | });
  176 | 
  177 | // ─────────────────────────────────────────────────────────────────
  178 | // DASHBOARD — PRAMOD (admin, primary, negative balance)
  179 | // ─────────────────────────────────────────────────────────────────
  180 | test.describe('Dashboard — pramod (admin, primary custodian)', () => {
  181 |   test('pramod balance card is negative and shows red text', async ({ page }) => {
  182 |     await injectSession(page, SESSION.pramod);
  183 |     await page.goto(BASE_URL);
  184 |     await handleModeSelector(page, 'custodian');
  185 |     await expect(page.locator('#my-balance')).toContainText('₹', { timeout: 10000 });
  186 |     const balEl = page.locator('#my-balance');
  187 |     // Negative balance → .neg class
  188 |     await expect(balEl).toHaveClass(/neg/);
  189 |     const txt = await balEl.textContent();
  190 |     expect(txt).toMatch(/₹/);
  191 |   });
  192 | });
  193 | 
  194 | // ─────────────────────────────────────────────────────────────────
  195 | // DASHBOARD — GURPREET (non-admin, small positive balance)
  196 | // ─────────────────────────────────────────────────────────────────
  197 | test.describe('Dashboard — gurpreet (new custodian, low activity)', () => {
  198 |   test.beforeEach(async ({ page }) => {
  199 |     await injectSession(page, SESSION.gurpreet);
  200 |     await page.goto(BASE_URL);
  201 |     await expect(page.locator('#my-balance')).toContainText('₹', { timeout: 10000 });
  202 |   });
  203 | 
  204 |   test('gurpreet name shown in header', async ({ page }) => {
  205 |     await expect(page.locator('#dash-sub')).toHaveText('Gurpreet');
  206 |   });
  207 | 
  208 |   test('positive balance shows .pos class', async ({ page }) => {
  209 |     // gurpreet expected balance: +216.44
  210 |     await expect(page.locator('#my-balance')).toHaveClass(/pos/);
  211 |   });
  212 | 
  213 |   test('no pending transfers for gurpreet', async ({ page }) => {
  214 |     await expect(page.locator('.pend-banner')).not.toBeVisible();
  215 |   });
  216 | });
  217 | 
  218 | // ─────────────────────────────────────────────────────────────────
  219 | // TRANSFER FORM
  220 | // ─────────────────────────────────────────────────────────────────
  221 | test.describe('Transfer Form', () => {
  222 |   test.beforeEach(async ({ page }) => {
  223 |     await injectSession(page, SESSION.shambhu);
  224 |     await page.goto(BASE_URL);
> 225 |     await page.locator('#nav-transfer').click();
      |                                         ^ Error: locator.click: Test timeout of 30000ms exceeded.
  226 |     await expect(page.locator('#s-transfer')).toHaveClass(/active/);
  227 |   });
  228 | 
  229 |   test('giver mode selected by default', async ({ page }) => {
  230 |     await expect(page.locator('#role-giver')).toHaveClass(/sel/);
  231 |     await expect(page.locator('#role-receiver')).not.toHaveClass(/sel/);
  232 |   });
  233 | 
  234 |   test('cash + giver mode shows pending info notice', async ({ page }) => {
  235 |     await expect(page.locator('#tf-pending-info')).toBeVisible();
  236 |   });
  237 | 
  238 |   test('switching to receiver mode hides pending notice', async ({ page }) => {
  239 |     await page.locator('#role-receiver').click();
  240 |     await expect(page.locator('#tf-pending-info')).not.toBeVisible();
  241 |   });
  242 | 
  243 |   test('cash mode selected by default, ref field hidden', async ({ page }) => {
  244 |     await expect(page.locator('#tf-mode-chips [data-mode="cash"]')).toHaveClass(/sel/);
  245 |     await expect(page.locator('#tf-ref-group')).not.toBeVisible();
  246 |   });
  247 | 
  248 |   test('selecting UPI shows reference field', async ({ page }) => {
  249 |     await page.locator('#tf-mode-chips [data-mode="upi"]').click();
  250 |     await expect(page.locator('#tf-ref-group')).toBeVisible();
  251 |   });
  252 | 
  253 |   test('selecting NEFT shows reference field', async ({ page }) => {
  254 |     await page.locator('#tf-mode-chips [data-mode="neft"]').click();
  255 |     await expect(page.locator('#tf-ref-group')).toBeVisible();
  256 |   });
  257 | 
  258 |   test('selecting IMPS shows reference field', async ({ page }) => {
  259 |     await page.locator('#tf-mode-chips [data-mode="imps"]').click();
  260 |     await expect(page.locator('#tf-ref-group')).toBeVisible();
  261 |   });
  262 | 
  263 |   test('selecting Cheque shows reference field', async ({ page }) => {
  264 |     await page.locator('#tf-mode-chips [data-mode="cheque"]').click();
  265 |     await expect(page.locator('#tf-ref-group')).toBeVisible();
  266 |   });
  267 | 
  268 |   test('selecting RTGS shows reference field', async ({ page }) => {
  269 |     await page.locator('#tf-mode-chips [data-mode="rtgs"]').click();
  270 |     await expect(page.locator('#tf-ref-group')).toBeVisible();
  271 |   });
  272 | 
  273 |   test('UPI mode: ref label is UPI Ref No., UPI Pay button visible', async ({ page }) => {
  274 |     await page.locator('#tf-mode-chips [data-mode="upi"]').click();
  275 |     await expect(page.locator('#tf-ref-label')).toHaveText('UPI Ref No.');
  276 |     await expect(page.locator('#tf-upi-pay-btn')).toBeVisible();
  277 |   });
  278 | 
  279 |   test('NEFT mode: ref label is UTR No., UPI Pay button hidden', async ({ page }) => {
  280 |     await page.locator('#tf-mode-chips [data-mode="neft"]').click();
  281 |     await expect(page.locator('#tf-ref-label')).toHaveText('UTR No.');
  282 |     await expect(page.locator('#tf-upi-pay-btn')).not.toBeVisible();
  283 |   });
  284 | 
  285 |   test('Cheque mode: shows bank and branch fields', async ({ page }) => {
  286 |     await page.locator('#tf-mode-chips [data-mode="cheque"]').click();
  287 |     await expect(page.locator('#tf-cheque-extra')).toBeVisible();
  288 |     await expect(page.locator('#tf-cheque-bank')).toBeVisible();
  289 |     await expect(page.locator('#tf-cheque-branch')).toBeVisible();
  290 |   });
  291 | 
  292 |   test('Cash mode: cheque extra fields hidden', async ({ page }) => {
  293 |     await page.locator('#tf-mode-chips [data-mode="cheque"]').click();
  294 |     await page.locator('#tf-mode-chips [data-mode="cash"]').click();
  295 |     await expect(page.locator('#tf-cheque-extra')).not.toBeVisible();
  296 |   });
  297 | 
  298 |   test('returning to cash hides reference field again', async ({ page }) => {
  299 |     await page.locator('#tf-mode-chips [data-mode="upi"]').click();
  300 |     await expect(page.locator('#tf-ref-group')).toBeVisible();
  301 |     await page.locator('#tf-mode-chips [data-mode="cash"]').click();
  302 |     await expect(page.locator('#tf-ref-group')).not.toBeVisible();
  303 |   });
  304 | 
  305 |   test('custodian select excludes self (shambhu not in list)', async ({ page }) => {
  306 |     const options = await page.locator('#tf-other option').allTextContents();
  307 |     expect(options.some(o => o.toLowerCase().includes('shambhu'))).toBe(false);
  308 |     // Other custodians present
  309 |     expect(options.some(o => o.toLowerCase().includes('pramod'))).toBe(true);
  310 |     expect(options.some(o => o.toLowerCase().includes('harish'))).toBe(true);
  311 |   });
  312 | 
  313 |   test('submit with no custodian shows error toast', async ({ page }) => {
  314 |     await page.locator('#tf-amount').fill('1000');
  315 |     await page.locator('#tf-submit').click();
  316 |     await expect(page.locator('#toast')).toContainText('Select the other custodian');
  317 |   });
  318 | 
  319 |   test('submit with no amount shows error toast', async ({ page }) => {
  320 |     await page.locator('#tf-other').selectOption({ index: 1 });
  321 |     await page.locator('#tf-submit').click();
  322 |     await expect(page.locator('#toast')).toContainText('valid amount');
  323 |   });
  324 | 
  325 |   test('today date is pre-filled', async ({ page }) => {
```