# 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 >> candidates section - index and random pages
- Location: tests/ui/entities.spec.ts:28:7

# Error details

```
Error: expect(received).toBe(expected) // Object.is equality

Expected: 200
Received: 404
```

# Page snapshot

```yaml
- generic [active] [ref=f1e1]:
  - heading "404 Not Found" [level=1] [ref=f1e3]
  - separator [ref=f1e4]
  - generic [ref=f1e5]: nginx/1.31.6
```

# Test source

```ts
  1   | import { test, expect } from '@playwright/test';
  2   | 
  3   | // Entity section tests verify generated content is properly served
  4   | 
  5   | // Helper to get random items from array
  6   | function getRandomItems<T>(array: T[], count: number): T[] {
  7   |   const shuffled = [...array].sort(() => 0.5 - Math.random());
  8   |   return shuffled.slice(0, Math.min(count, array.length));
  9   | }
  10  | 
  11  | // Helper to convert full URL to relative path
  12  | function toRelativePath(url: string): string {
  13  |   try {
  14  |     const parsed = new URL(url);
  15  |     return parsed.pathname;
  16  |   } catch {
  17  |     return url;
  18  |   }
  19  | }
  20  | 
  21  | // Wait timeout for async data loading (GraphQL browseEntities)
  22  | const DATA_LOAD_TIMEOUT = 15000;
  23  | 
  24  | const hasAuthCredentials = !!process.env.PLAYWRIGHT_AUTH_USER;
  25  | 
  26  | test.describe('us.elect.info entity sections', () => {
  27  | 
  28  |   test('candidates section - index and random pages', async ({ page }) => {
  29  |     const indexResponse = await page.goto('/candidates/');
  30  |     expect(indexResponse?.status()).toBe(200);
  31  |     await expect(page.locator('h1')).toContainText(/Candidates/i);
  32  | 
  33  |     // Wait for GraphQL browseEntities to populate results
  34  |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  35  | 
  36  |     const links = await page.locator('.entity-search-result').all();
  37  |     expect(links.length).toBeGreaterThan(0);
  38  | 
  39  |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  40  |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  41  | 
  42  |     for (const href of randomLinks) {
  43  |       const entityResponse = await page.goto(toRelativePath(href));
> 44  |       expect(entityResponse?.status()).toBe(200);
      |                                        ^ Error: expect(received).toBe(expected) // Object.is equality
  45  |       await expect(page.locator('h1')).toBeVisible();
  46  |     }
  47  |   });
  48  | 
  49  |   test('committees section - index and random pages', async ({ page }) => {
  50  |     const indexResponse = await page.goto('/committees/');
  51  |     expect(indexResponse?.status()).toBe(200);
  52  |     await expect(page.locator('h1')).toContainText(/Committees/i);
  53  | 
  54  |     // Wait for GraphQL browseEntities to populate results
  55  |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  56  | 
  57  |     const links = await page.locator('.entity-search-result').all();
  58  |     expect(links.length).toBeGreaterThan(0);
  59  | 
  60  |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  61  |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  62  | 
  63  |     for (const href of randomLinks) {
  64  |       const entityResponse = await page.goto(toRelativePath(href));
  65  |       expect(entityResponse?.status()).toBe(200);
  66  |       await expect(page.locator('h1')).toBeVisible();
  67  |     }
  68  |   });
  69  | 
  70  |   test('parties section - index and random pages', async ({ page }) => {
  71  |     const indexResponse = await page.goto('/parties/');
  72  |     expect(indexResponse?.status()).toBe(200);
  73  |     await expect(page.locator('h1')).toContainText(/Parties/i);
  74  | 
  75  |     // Wait for GraphQL browseEntities to populate results
  76  |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  77  | 
  78  |     const links = await page.locator('.entity-search-result').all();
  79  |     expect(links.length).toBeGreaterThan(0);
  80  | 
  81  |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  82  |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  83  | 
  84  |     for (const href of randomLinks) {
  85  |       const entityResponse = await page.goto(toRelativePath(href));
  86  |       expect(entityResponse?.status()).toBe(200);
  87  |       await expect(page.locator('h1')).toBeVisible();
  88  |     }
  89  |   });
  90  | 
  91  |   test('offices section - index and random pages', async ({ page }) => {
  92  |     const indexResponse = await page.goto('/offices/');
  93  |     expect(indexResponse?.status()).toBe(200);
  94  |     await expect(page.locator('h1')).toContainText(/Offices/i);
  95  | 
  96  |     // Wait for GraphQL browseEntities to populate results
  97  |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  98  | 
  99  |     const links = await page.locator('.entity-search-result').all();
  100 |     expect(links.length).toBeGreaterThan(0);
  101 | 
  102 |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  103 |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  104 | 
  105 |     for (const href of randomLinks) {
  106 |       const entityResponse = await page.goto(toRelativePath(href));
  107 |       expect(entityResponse?.status()).toBe(200);
  108 |       await expect(page.locator('h1')).toBeVisible();
  109 |     }
  110 |   });
  111 | 
  112 |   test('districts section - index and random pages', async ({ page }) => {
  113 |     const indexResponse = await page.goto('/districts/');
  114 |     expect(indexResponse?.status()).toBe(200);
  115 |     await expect(page.locator('h1')).toContainText(/Districts/i);
  116 | 
  117 |     // Map view is default — verify Leaflet map renders with GeoJSON state shapes
  118 |     await expect(page.locator('#map.leaflet-container')).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  119 |     await expect(page.locator('#map .leaflet-interactive').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  120 |     const shapes = await page.locator('#map .leaflet-interactive').all();
  121 |     expect(shapes.length).toBeGreaterThan(0);
  122 | 
  123 |     // Switch to List view to test state selector and district results
  124 |     await page.locator('.toggle-btn[data-view="list"]').click();
  125 | 
  126 |     // Districts list view loads state options from GraphQL, then requires selection
  127 |     const stateSelect = page.locator('#district-state-select');
  128 |     await expect(stateSelect).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  129 | 
  130 |     // Wait for state options to populate from GraphQL (more than just the placeholder)
  131 |     await page.waitForFunction(() => {
  132 |       const select = document.querySelector('#district-state-select') as HTMLSelectElement;
  133 |       return select && select.options.length > 1;
  134 |     }, { timeout: DATA_LOAD_TIMEOUT });
  135 | 
  136 |     await stateSelect.selectOption('CA');
  137 | 
  138 |     // Wait for district results to load
  139 |     await expect(page.locator('.district-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  140 | 
  141 |     const links = await page.locator('.district-result').all();
  142 |     expect(links.length).toBeGreaterThan(0);
  143 | 
  144 |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
```