# 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: tests/ui/entities.spec.ts >> us.elect.info entity sections - additional index + detail >> counties section - index and random detail pages
- Location: tests/ui/entities.spec.ts:462:7

# Error details

```
Test timeout of 30000ms exceeded.
```

```
Error: page.goto: net::ERR_ABORTED; maybe frame was detached?
Call log:
  - navigating to "http://10.1.105.89/counties/", waiting until "load"

```

# Test source

```ts
  363 |     const body = resp.request().postData() || '';
  364 |     return body.includes('browseEntities');
  365 |   }, { timeout: DATA_LOAD_TIMEOUT });
  366 |   await page.goto(url);
  367 |   const response = await responsePromise;
  368 |   const json = await response.json();
  369 |   const results = json.data?.browseEntities?.results || [];
  370 |   return results.map((r: { score?: number }) => r.score || 0);
  371 | }
  372 | 
  373 | test.describe('entity browse sort order - quantitative fields', () => {
  374 | 
  375 |   test('candidates sorted by total contributions descending', async ({ page }) => {
  376 |     const scores = await getBrowseScores(page, '/candidates/');
  377 |     expect(scores.length).toBeGreaterThan(1);
  378 |     for (let i = 1; i < scores.length; i++) {
  379 |       expect(scores[i - 1]).toBeGreaterThanOrEqual(scores[i]);
  380 |     }
  381 |   });
  382 | 
  383 |   test('committees sorted by total receipts descending', async ({ page }) => {
  384 |     const scores = await getBrowseScores(page, '/committees/');
  385 |     expect(scores.length).toBeGreaterThan(1);
  386 |     for (let i = 1; i < scores.length; i++) {
  387 |       expect(scores[i - 1]).toBeGreaterThanOrEqual(scores[i]);
  388 |     }
  389 |   });
  390 | 
  391 |   test('vendors sorted by total received descending', async ({ page }) => {
  392 |     const scores = await getBrowseScores(page, '/vendors/');
  393 |     expect(scores.length).toBeGreaterThan(1);
  394 |     for (let i = 1; i < scores.length; i++) {
  395 |       expect(scores[i - 1]).toBeGreaterThanOrEqual(scores[i]);
  396 |     }
  397 |   });
  398 | 
  399 |   test('individuals sorted by total contributions descending', async ({ page, request, baseURL }) => {
  400 |     await skipIfPremiumGated(request, '/individuals/');
  401 |     await premiumVisitor(page.context(), baseURL); // donor records are premium (sites#277)
  402 |     const scores = await getBrowseScores(page, '/individuals/');
  403 |     expect(scores.length).toBeGreaterThan(1);
  404 |     // A stale search index scores every donor 0 and the order means nothing.
  405 |     expect(scores[0], 'top individual donor should have contributions').toBeGreaterThan(0);
  406 |     for (let i = 1; i < scores.length; i++) {
  407 |       expect(scores[i - 1]).toBeGreaterThanOrEqual(scores[i]);
  408 |     }
  409 |   });
  410 | 
  411 |   test('employers sorted by total employee contributions descending', async ({ page }) => {
  412 |     const scores = await getBrowseScores(page, '/employers/');
  413 |     expect(scores.length).toBeGreaterThan(1);
  414 |     for (let i = 1; i < scores.length; i++) {
  415 |       expect(scores[i - 1]).toBeGreaterThanOrEqual(scores[i]);
  416 |     }
  417 |   });
  418 | });
  419 | 
  420 | test.describe('us.elect.info entity sections - additional index + detail', () => {
  421 | 
  422 |   test('employers section - index and random pages', async ({ page }) => {
  423 |     const indexResponse = await page.goto('/employers/');
  424 |     expect(indexResponse?.status(), '/employers/ should answer 200').toBe(200);
  425 |     await expect(page.locator('h1')).toContainText(/Employers/i);
  426 | 
  427 |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  428 | 
  429 |     const links = await page.locator('.entity-search-result').all();
  430 |     expect(links.length).toBeGreaterThan(0);
  431 | 
  432 |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  433 |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  434 | 
  435 |     for (const href of randomLinks) {
  436 |       const entityResponse = await page.goto(toRelativePath(href));
  437 |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  438 |       await expect(page.locator('h1')).toBeVisible();
  439 |     }
  440 |   });
  441 | 
  442 |   test('vendors section - index and random pages', async ({ page }) => {
  443 |     const indexResponse = await page.goto('/vendors/');
  444 |     expect(indexResponse?.status(), '/vendors/ should answer 200').toBe(200);
  445 |     await expect(page.locator('h1')).toContainText(/Vendors/i);
  446 | 
  447 |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  448 | 
  449 |     const links = await page.locator('.entity-search-result').all();
  450 |     expect(links.length).toBeGreaterThan(0);
  451 | 
  452 |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  453 |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  454 | 
  455 |     for (const href of randomLinks) {
  456 |       const entityResponse = await page.goto(toRelativePath(href));
  457 |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  458 |       await expect(page.locator('h1')).toBeVisible();
  459 |     }
  460 |   });
  461 | 
  462 |   test('counties section - index and random detail pages', async ({ page }) => {
> 463 |     await page.goto('/counties/');
      |                ^ Error: page.goto: net::ERR_ABORTED; maybe frame was detached?
  464 | 
  465 |     // Map view is default — verify Leaflet map renders with GeoJSON state shapes
  466 |     await expect(page.locator('#map.leaflet-container')).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  467 |     await expect(page.locator('#map .leaflet-interactive').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  468 |     const shapes = await page.locator('#map .leaflet-interactive').all();
  469 |     expect(shapes.length).toBeGreaterThan(0);
  470 | 
  471 |     // Switch to List view to test county results and detail navigation
  472 |     await page.locator('.toggle-btn[data-view="list"]').click();
  473 | 
  474 |     // Wait for results to load
  475 |     await page.waitForSelector('.county-result, .county-error', { timeout: DATA_LOAD_TIMEOUT });
  476 |     const results = await page.locator('.county-result').all();
  477 |     expect(results.length).toBeGreaterThan(0);
  478 | 
  479 |     // County results are <a> tags - pick random ones and visit detail pages
  480 |     const hrefs = await Promise.all(results.slice(0, 20).map(r => r.getAttribute('href')));
  481 |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  482 | 
  483 |     for (const href of randomLinks) {
  484 |       const entityResponse = await page.goto(toRelativePath(href));
  485 |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  486 |       await expect(page.locator('h1')).toBeVisible();
  487 |     }
  488 |   });
  489 | 
  490 |   test('places section - index and random detail pages', async ({ page }) => {
  491 |     await page.goto('/places/');
  492 | 
  493 |     // Map view is default — verify Leaflet map renders with GeoJSON state shapes
  494 |     await expect(page.locator('#map.leaflet-container')).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  495 |     await expect(page.locator('#map .leaflet-interactive').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  496 |     const shapes = await page.locator('#map .leaflet-interactive').all();
  497 |     expect(shapes.length).toBeGreaterThan(0);
  498 | 
  499 |     // Switch to List view to test place results and detail navigation
  500 |     await page.locator('.toggle-btn[data-view="list"]').click();
  501 | 
  502 |     // Wait for results to load
  503 |     await page.waitForSelector('.place-result, .place-error', { timeout: DATA_LOAD_TIMEOUT });
  504 |     const results = await page.locator('.place-result').all();
  505 |     expect(results.length).toBeGreaterThan(0);
  506 | 
  507 |     // Pick random results and visit detail pages
  508 |     const hrefs = await Promise.all(results.slice(0, 20).map(r => r.getAttribute('href')));
  509 |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  510 | 
  511 |     for (const href of randomLinks) {
  512 |       const entityResponse = await page.goto(toRelativePath(href));
  513 |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  514 |       await expect(page.locator('h1')).toBeVisible();
  515 |     }
  516 |   });
  517 | 
  518 |   test('postalcodes section - index and random detail pages', async ({ page }) => {
  519 |     await page.goto('/postalcodes/');
  520 | 
  521 |     // Map view is default — verify Leaflet map renders with GeoJSON state shapes
  522 |     await expect(page.locator('#map.leaflet-container')).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  523 |     await expect(page.locator('#map .leaflet-interactive').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  524 |     const shapes = await page.locator('#map .leaflet-interactive').all();
  525 |     expect(shapes.length).toBeGreaterThan(0);
  526 | 
  527 |     // Switch to List view to test postal code results and detail navigation
  528 |     await page.locator('.toggle-btn[data-view="list"]').click();
  529 | 
  530 |     // Wait for results to load
  531 |     await page.waitForSelector('.postal-result, .postal-error', { timeout: DATA_LOAD_TIMEOUT });
  532 |     const results = await page.locator('.postal-result').all();
  533 |     expect(results.length).toBeGreaterThan(0);
  534 | 
  535 |     // Pick random results and visit detail pages
  536 |     const hrefs = await Promise.all(results.slice(0, 20).map(r => r.getAttribute('href')));
  537 |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  538 | 
  539 |     for (const href of randomLinks) {
  540 |       const entityResponse = await page.goto(toRelativePath(href));
  541 |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  542 |       await expect(page.locator('h1')).toBeVisible();
  543 |     }
  544 |   });
  545 | 
  546 |   test('precincts section - map view and state filter', async ({ page }) => {
  547 |     await page.goto('/precincts/');
  548 | 
  549 |     // Map view is default — verify Leaflet map renders with GeoJSON state shapes
  550 |     await expect(page.locator('#map.leaflet-container')).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  551 |     await expect(page.locator('#map .leaflet-interactive').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  552 |     const shapes = await page.locator('#map .leaflet-interactive').all();
  553 |     expect(shapes.length).toBeGreaterThan(0);
  554 | 
  555 |     // Switch to List view to test state filter and precinct results
  556 |     await page.locator('.toggle-btn[data-view="list"]').click();
  557 | 
  558 |     // Select a state first (precincts require state selection)
  559 |     const stateSelect = page.locator('#precinct-state-select');
  560 |     await expect(stateSelect).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  561 |     await stateSelect.selectOption('TX');
  562 | 
  563 |     // Wait for precinct results to load
```