# 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/district-maps.spec.ts >> state-senate map of VT: boundaries within budget, polygons keyed to graph Districts
- Location: tests/ui/district-maps.spec.ts:36:7

# Error details

```
Error: expect(locator).toHaveAttribute(expected) failed

Locator: locator('.map-chamber-btn[data-kind="sldu"]')
Expected: "true"
Timeout: 5000ms
Error: element(s) not found

Call log:
  - Expect "toHaveAttribute" locator('.map-chamber-btn[data-kind="sldu"]') with timeout 5000ms
  - waiting for locator('.map-chamber-btn[data-kind="sldu"]')

```

```yaml
- link "← Back to Home":
  - /url: /
- banner:
  - heading "Districts" [level=1]
  - paragraph: Congressional, State Senate and State House districts
- text: Not signed in
- link "Log in":
  - /url: /outpost.goauthentik.io/start?rd=https%3A%2F%2Fus.elect.info%2Fdistricts%2F%3Fview%3Dmap%26tab%3Dstate-senate%26state%3DVT
- link "Create an account":
  - /url: /signin/register?rd=https%3A%2F%2Fus.elect.info%2Fdistricts%2F%3Fview%3Dmap%26tab%3Dstate-senate%26state%3DVT
- toolbar "Member tools":
  - button "All cycles (Sign in to use the cycle facet)" [disabled]: All cycles
  - button "Graph (Sign in to use Graph)" [disabled]: Graph
- button "Congressional map"
- button "List"
- main:
  - textbox "Search districts by name..."
  - button "Search"
  - button "All"
  - button "Congressional"
  - button "State Senate" [pressed]
  - button "State House"
  - text: "State:"
  - combobox "State:":
    - option "All states"
    - option "Alabama"
    - option "Alaska"
    - option "American Samoa"
    - option "Arizona"
    - option "Arkansas"
    - option "California"
    - option "Colorado"
    - option "Connecticut"
    - option "Delaware"
    - option "District of Columbia"
    - option "Florida"
    - option "Georgia"
    - option "Guam"
    - option "Hawaii"
    - option "Idaho"
    - option "Illinois"
    - option "Indiana"
    - option "Iowa"
    - option "Kansas"
    - option "Kentucky"
    - option "Louisiana"
    - option "Maine"
    - option "Maryland"
    - option "Massachusetts"
    - option "Michigan"
    - option "Minnesota"
    - option "Mississippi"
    - option "Missouri"
    - option "Montana"
    - option "Nebraska"
    - option "Nevada"
    - option "New Hampshire"
    - option "New Jersey"
    - option "New Mexico"
    - option "New York"
    - option "North Carolina"
    - option "North Dakota"
    - option "Northern Mariana Islands"
    - option "Ohio"
    - option "Oklahoma"
    - option "Oregon"
    - option "Pennsylvania"
    - option "Puerto Rico"
    - option "Rhode Island"
    - option "South Carolina"
    - option "South Dakota"
    - option "Tennessee"
    - option "Texas"
    - option "U.S. Virgin Islands"
    - option "Utah"
    - option "Vermont" [selected]
    - option "Virginia"
    - option "Washington"
    - option "West Virginia"
    - option "Wisconsin"
    - option "Wyoming"
  - heading "All Districts" [level=3]
  - link "Vermont State Senate District ADD State Senate":
    - /url: /districts/vt-sd-add/
  - link "Vermont State Senate District BEN State Senate":
    - /url: /districts/vt-sd-ben/
  - link "Vermont State Senate District CAL State Senate":
    - /url: /districts/vt-sd-cal/
  - link "Vermont State Senate District CHC State Senate":
    - /url: /districts/vt-sd-chc/
  - link "Vermont State Senate District CHN State Senate":
    - /url: /districts/vt-sd-chn/
  - link "Vermont State Senate District CHS State Senate":
    - /url: /districts/vt-sd-chs/
  - link "Vermont State Senate District ESX State Senate":
    - /url: /districts/vt-sd-esx/
  - link "Vermont State Senate District FRA State Senate":
    - /url: /districts/vt-sd-fra/
  - link "Vermont State Senate District GRI State Senate":
    - /url: /districts/vt-sd-gri/
  - link "Vermont State Senate District LAM State Senate":
    - /url: /districts/vt-sd-lam/
  - link "Vermont State Senate District ORA State Senate":
    - /url: /districts/vt-sd-ora/
  - link "Vermont State Senate District ORL State Senate":
    - /url: /districts/vt-sd-orl/
  - link "Vermont State Senate District RUT State Senate":
    - /url: /districts/vt-sd-rut/
  - link "Vermont State Senate District WAS State Senate":
    - /url: /districts/vt-sd-was/
  - link "Vermont State Senate District WDH State Senate":
    - /url: /districts/vt-sd-wdh/
  - link "Vermont State Senate District WSR State Senate":
    - /url: /districts/vt-sd-wsr/
  - text: Showing all 16 results
```

# Test source

```ts
  1  | import { test, expect, type Page } from '@playwright/test';
  2  | 
  3  | /*
  4  |  * /districts/ Map + List per chamber (sites#467), against an installed target.
  5  |  *
  6  |  * Boundaries are GeoShapeImageSync's simplified per-state TIGER collections
  7  |  * (Congressional /geo/districts/, State Senate /geo/sldu/, State House
  8  |  * /geo/sldl/). Each state file is fetched only on drill-in and must stay within
  9  |  * the payload budget. Polygons link to the serving graph's District: on a graph
  10 |  * with state legislative Districts every polygon the graph has links and its
  11 |  * page answers 200; on a graph without them (production before the SLD graph
  12 |  * release) no SLD polygon links. Both are asserted, never skipped.
  13 |  */
  14 | 
  15 | const BUDGET_BYTES = 500_000;  // uncompressed; largest today is TX State House (~490 KB)
  16 | 
  17 | const SAMPLES: Array<{ tab: string; kind: string; state: string; url: string; label?: [string, string] }> = [
  18 |   { tab: 'congressional', kind: 'cd', state: 'TX', url: '/geo/districts/us-districts-tx.geojson' },
  19 |   { tab: 'state-senate', kind: 'sldu', state: 'AK', url: '/geo/sldu/us-sldu-ak.geojson', label: ['ak-00a', 'Alaska State Senate District A'] },
  20 |   { tab: 'state-senate', kind: 'sldu', state: 'VT', url: '/geo/sldu/us-sldu-vt.geojson', label: ['vt-ben', 'Vermont Bennington Senatorial District'] },
  21 |   { tab: 'state-house', kind: 'sldl', state: 'MN', url: '/geo/sldl/us-sldl-mn.geojson', label: ['mn-01a', 'Minnesota State House District 1A'] },
  22 |   { tab: 'state-house', kind: 'sldl', state: 'TX', url: '/geo/sldl/us-sldl-tx.geojson' },
  23 | ];
  24 | 
  25 | function inventoryResponse(page: Page, kind: string, state: string) {
  26 |   return page.waitForResponse(response => {
  27 |     if (!response.url().includes('/api/graphql') || response.request().method() !== 'POST') return false;
  28 |     try {
  29 |       const body = response.request().postDataJSON();
  30 |       return /\bbrowseEntities\s*\(/.test(body?.query || '') && body?.variables?.filter === `district_type:${kind},state:${state}` && body?.variables?.page === 1;
  31 |     } catch { return false; }
  32 |   });
  33 | }
  34 | 
  35 | for (const sample of SAMPLES) {
  36 |   test(`${sample.tab} map of ${sample.state}: boundaries within budget, polygons keyed to graph Districts`, async ({ page, request }) => {
  37 |     test.setTimeout(90000);
  38 |     const geo = await request.get(sample.url);
  39 |     expect(geo.status(), `${sample.url} should answer 200`).toBe(200);
  40 |     expect(geo.headers()['content-type']).toContain('application/geo+json');
  41 |     const body = await geo.body();
  42 |     expect(body.length, `${sample.url} payload budget`).toBeLessThanOrEqual(BUDGET_BYTES);
  43 |     const features = JSON.parse(body.toString()).features;
  44 |     expect(features.length).toBeGreaterThan(0);
  45 | 
  46 |     const inventory = sample.kind === 'cd' ? null : inventoryResponse(page, sample.kind, sample.state);
  47 |     await page.goto(`/districts/?view=map&tab=${sample.tab}&state=${sample.state}`);
> 48 |     await expect(page.locator(`.map-chamber-btn[data-kind="${sample.kind}"]`)).toHaveAttribute('aria-pressed', 'true');
     |                                                                                ^ Error: expect(locator).toHaveAttribute(expected) failed
  49 |     await expect(page.locator('#map-view')).toHaveAttribute('data-loaded', sample.kind, { timeout: 45000 });
  50 |     await expect(page.locator('#map path[data-district]')).toHaveCount(features.length);
  51 |     if (sample.label) {
  52 |       await expect(page.locator(`#map path[data-district="${sample.label[0]}"]`)).toHaveAttribute('aria-label', new RegExp(`^${sample.label[1]}( \\(page not yet available\\))?$`));
  53 |     }
  54 | 
  55 |     const linked = page.locator('#map path[data-href]');
  56 |     if (inventory) {
  57 |       const payload = await (await inventory).json();
  58 |       expect(payload.errors, 'graph inventory query must succeed').toBeUndefined();
  59 |       const total = payload.data?.browseEntities?.total ?? 0;
  60 |       if (total === 0) {
  61 |         // A serving graph without state legislative Districts: nothing links to a missing page.
  62 |         await expect(linked).toHaveCount(0);
  63 |         return;
  64 |       }
  65 |       // TIGER 2025 and the graph's Districts match 1:1 (sites#467): every polygon links.
  66 |       await expect(linked).toHaveCount(features.length);
  67 |     } else {
  68 |       await expect(linked).toHaveCount(features.length);
  69 |     }
  70 | 
  71 |     const href = await linked.first().getAttribute('data-href');
  72 |     expect(href).toMatch(/^\/districts\/[^/]+\/$/);
  73 |     await linked.first().scrollIntoViewIfNeeded();
  74 |     await Promise.all([page.waitForURL(`**${href}`), linked.first().dispatchEvent('click')]);
  75 |     const destination = await request.get(href!);
  76 |     expect(destination.status(), `${href} should answer 200`).toBe(200);
  77 |     await expect(page.locator('h1')).toBeVisible();
  78 |   });
  79 | }
  80 | 
  81 | test('State House on Nebraska explains the unicameral legislature', async ({ page }) => {
  82 |   await page.goto('/districts/?view=map&tab=state-house&state=NE');
  83 |   await expect(page.locator('.map-notice')).toContainText('unicameral', { timeout: 45000 });
  84 | });
  85 | 
```