# 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 >> parties section - index and random pages
- Location: tests/ui/entities.spec.ts:57: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.85.212/parties/", waiting until "load"

```

# Test source

```ts
  1   | import { test, expect } from '@playwright/test';
  2   | import { getRandomItems, toRelativePath, premiumVisitor } from './helpers';
  3   | 
  4   | // Entity section tests verify generated content is properly served.
  5   | // Samples are seeded (helpers.getRandomItems, SAMPLE_SEED) and logged, and every
  6   | // status assertion names its URL, so a failure is reproducible from the log alone.
  7   | 
  8   | // Wait timeout for async data loading (GraphQL browseEntities)
  9   | const DATA_LOAD_TIMEOUT = 15000;
  10  | 
  11  | const hasAuthCredentials = !!process.env.PLAYWRIGHT_AUTH_USER;
  12  | 
  13  | test.describe('us.elect.info entity sections', () => {
  14  | 
  15  |   test('candidates section - index and random pages', async ({ page }) => {
  16  |     const indexResponse = await page.goto('/candidates/');
  17  |     expect(indexResponse?.status(), '/candidates/ should answer 200').toBe(200);
  18  |     await expect(page.locator('h1')).toContainText(/Candidates/i);
  19  | 
  20  |     // Wait for GraphQL browseEntities to populate results
  21  |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  22  | 
  23  |     const links = await page.locator('.entity-search-result').all();
  24  |     expect(links.length).toBeGreaterThan(0);
  25  | 
  26  |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  27  |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  28  | 
  29  |     for (const href of randomLinks) {
  30  |       const entityResponse = await page.goto(toRelativePath(href));
  31  |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  32  |       await expect(page.locator('h1')).toBeVisible();
  33  |     }
  34  |   });
  35  | 
  36  |   test('committees section - index and random pages', async ({ page }) => {
  37  |     const indexResponse = await page.goto('/committees/');
  38  |     expect(indexResponse?.status(), '/committees/ should answer 200').toBe(200);
  39  |     await expect(page.locator('h1')).toContainText(/Committees/i);
  40  | 
  41  |     // Wait for GraphQL browseEntities to populate results
  42  |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  43  | 
  44  |     const links = await page.locator('.entity-search-result').all();
  45  |     expect(links.length).toBeGreaterThan(0);
  46  | 
  47  |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  48  |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  49  | 
  50  |     for (const href of randomLinks) {
  51  |       const entityResponse = await page.goto(toRelativePath(href));
  52  |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  53  |       await expect(page.locator('h1')).toBeVisible();
  54  |     }
  55  |   });
  56  | 
  57  |   test('parties section - index and random pages', async ({ page }) => {
> 58  |     const indexResponse = await page.goto('/parties/');
      |                                      ^ Error: page.goto: net::ERR_ABORTED; maybe frame was detached?
  59  |     expect(indexResponse?.status(), '/parties/ should answer 200').toBe(200);
  60  |     await expect(page.locator('h1')).toContainText(/Parties/i);
  61  | 
  62  |     // Wait for GraphQL browseEntities to populate results
  63  |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  64  | 
  65  |     const links = await page.locator('.entity-search-result').all();
  66  |     expect(links.length).toBeGreaterThan(0);
  67  | 
  68  |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  69  |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  70  | 
  71  |     for (const href of randomLinks) {
  72  |       const entityResponse = await page.goto(toRelativePath(href));
  73  |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  74  |       await expect(page.locator('h1')).toBeVisible();
  75  |     }
  76  |   });
  77  | 
  78  |   test('offices section - index and random pages', async ({ page }) => {
  79  |     const indexResponse = await page.goto('/offices/');
  80  |     expect(indexResponse?.status(), '/offices/ should answer 200').toBe(200);
  81  |     await expect(page.locator('h1')).toContainText(/Offices/i);
  82  | 
  83  |     // Wait for GraphQL browseEntities to populate results
  84  |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  85  | 
  86  |     const links = await page.locator('.entity-search-result').all();
  87  |     expect(links.length).toBeGreaterThan(0);
  88  | 
  89  |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  90  |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  91  | 
  92  |     for (const href of randomLinks) {
  93  |       const entityResponse = await page.goto(toRelativePath(href));
  94  |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  95  |       await expect(page.locator('h1')).toBeVisible();
  96  |     }
  97  |   });
  98  | 
  99  |   test('districts section - index and random pages', async ({ page }) => {
  100 |     const indexResponse = await page.goto('/districts/');
  101 |     expect(indexResponse?.status(), '/districts/ should answer 200').toBe(200);
  102 |     await expect(page.locator('h1')).toContainText(/Districts/i);
  103 | 
  104 |     // Map view is default — verify Leaflet map renders with GeoJSON state shapes
  105 |     await expect(page.locator('#map.leaflet-container')).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  106 |     await expect(page.locator('#map .leaflet-interactive').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  107 |     const shapes = await page.locator('#map .leaflet-interactive').all();
  108 |     expect(shapes.length).toBeGreaterThan(0);
  109 | 
  110 |     // Switch to List view to test state selector and district results
  111 |     await page.locator('.toggle-btn[data-view="list"]').click();
  112 | 
  113 |     // Districts list view loads state options from GraphQL, then requires selection
  114 |     const stateSelect = page.locator('#district-state-select');
  115 |     await expect(stateSelect).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  116 | 
  117 |     // Wait for state options to populate from GraphQL (more than just the placeholder)
  118 |     await page.waitForFunction(() => {
  119 |       const select = document.querySelector('#district-state-select') as HTMLSelectElement;
  120 |       return select && select.options.length > 1;
  121 |     }, { timeout: DATA_LOAD_TIMEOUT });
  122 | 
  123 |     await stateSelect.selectOption('CA');
  124 | 
  125 |     // Wait for district results to load
  126 |     await expect(page.locator('.district-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  127 | 
  128 |     const links = await page.locator('.district-result').all();
  129 |     expect(links.length).toBeGreaterThan(0);
  130 | 
  131 |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  132 |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  133 | 
  134 |     for (const href of randomLinks) {
  135 |       const entityResponse = await page.goto(toRelativePath(href));
  136 |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  137 |       await expect(page.locator('h1')).toBeVisible();
  138 |     }
  139 |   });
  140 | 
  141 |   test('states section - index and random pages', async ({ page }) => {
  142 |     const indexResponse = await page.goto('/states/');
  143 |     expect(indexResponse?.status(), '/states/ should answer 200').toBe(200);
  144 |     await expect(page.locator('h1')).toContainText(/States/i);
  145 | 
  146 |     // Map view is default — verify Leaflet map renders with GeoJSON state shapes
  147 |     await expect(page.locator('#map.leaflet-container')).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  148 |     await expect(page.locator('#map .leaflet-interactive').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  149 |     const shapes = await page.locator('#map .leaflet-interactive').all();
  150 |     expect(shapes.length).toBeGreaterThan(0);
  151 | 
  152 |     // Switch to Grid view to test state chips and detail navigation
  153 |     await page.locator('.toggle-btn[data-view="grid"]').click();
  154 | 
  155 |     await expect(page.locator('.state-chip').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  156 | 
  157 |     const links = await page.locator('.state-chip').all();
  158 |     expect(links.length).toBeGreaterThan(0);
```