# 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/voters.spec.ts >> voters (premium) >> every Districts row links; legislative districts open the filtered voters list
- Location: tests/ui/voters.spec.ts:148:7

# Error details

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

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

Locator:  locator('section#location').locator('a[href^="/places/"]')
Expected: "Rochester city"
Received: "Rochester town"
Timeout:  30000ms

Call log:
  - Expect "toHaveText" locator('section#location').locator('a[href^="/places/"]') with timeout 30000ms
  - waiting for locator('section#location').locator('a[href^="/places/"]')
    64 × locator resolved to <a href="/places/2b751de5-876e-d8a8-618e-c495d58943d1/">Rochester town</a>
       - unexpected value "Rochester town"

```

```yaml
- link "Rochester town":
  - /url: /places/2b751de5-876e-d8a8-618e-c495d58943d1/
```

# Test source

```ts
  67  |     expect(res.headers()['cache-control']).toContain('no-store');
  68  |     const body = await res.json() as { data?: { browseEntities?: { results: Array<{ id: string; name: string }> } } };
  69  |     expect(body.data?.browseEntities?.results.length).toBe(1);
  70  | 
  71  |     const get = await request.get('/voters/api/graphql', { maxRedirects: 0 });
  72  |     expect(get.status()).toBe(405);
  73  |   });
  74  | 
  75  |   test('browse lists the most active voters and links to detail pages', async ({ page, request }) => {
  76  |     await skipIfPremiumGated(request, '/voters/');
  77  |     const response = await page.goto('/voters/');
  78  |     expect(response?.status()).toBe(200);
  79  |     const results = page.locator('.entity-search-result');
  80  |     await expect(results.first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  81  |     await expect(results.first()).toContainText(/elections voted/);
  82  | 
  83  |     const href = await results.first().getAttribute('href');
  84  |     expect(href).toMatch(/^\/voters\/[0-9a-f-]{36}\/$/);
  85  |     const detail = await page.goto(href!);
  86  |     expect(detail?.status()).toBe(200);
  87  |     await expect(page.locator('.leaf-detail-page h1')).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  88  |     await expect(page.locator('.leaf-detail-page h1')).not.toHaveText(/Not Found/i);
  89  |     await expect(page.locator('section#history')).toContainText('Elections voted');
  90  |     await expect(page.locator('meta[name="robots"]')).toHaveAttribute('content', 'noindex, follow');
  91  |   });
  92  | 
  93  |   test('search needs every term: name plus city', async ({ page, request }) => {
  94  |     await skipIfPremiumGated(request, '/voters/');
  95  |     // "austin" names no state: Austin, AR matches every term as well as Austin, TX does (as does a voter in
  96  |     // Austin County, TX, through the county field), and which comes first is relevance order, which moves with
  97  |     // each voters index rebuild. The state filter is what picks Texas.
  98  |     await page.goto('/voters/');
  99  |     await page.fill('#entity-search-input', 'john smith austin');
  100 |     await page.locator('#entity-search-form button[type="submit"]').click();
  101 |     const results = page.locator('.entity-search-result');
  102 |     await expect(results.first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  103 | 
  104 |     await page.selectOption('#entity-filter-state', 'TX');
  105 |     await expect(page).toHaveURL(/[?&]f_state=TX/);
  106 |     await expect(page.locator('#entity-search-input')).toHaveValue('john smith austin');
  107 |     await expect(results.filter({ hasText: /SMITH, JOHN/ }).filter({ hasText: /Austin, TX\b/i }).first())
  108 |       .toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  109 |   });
  110 | 
  111 |   test('state filter narrows the list and scopes the county and district dropdowns', async ({ page, request }) => {
  112 |     await skipIfPremiumGated(request, '/voters/');
  113 |     await page.goto('/voters/');
  114 |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  115 |     await expect(page.locator('.entity-filter-control[data-key="county"]')).toBeHidden();
  116 |     await page.selectOption('#entity-filter-state', 'FL');
  117 |     await expect(page.locator('.entity-search-result').first()).toContainText(', FL', { timeout: DATA_LOAD_TIMEOUT });
  118 |     await expect(page).toHaveURL(/[?&]f_state=FL/);
  119 |     await expect(page.locator('#entity-filter-county option', { hasText: /Miami-Dade \([\d,]+\)/ })).toHaveCount(1, { timeout: DATA_LOAD_TIMEOUT });
  120 |     await expect(page.locator('#entity-filter-cd option', { hasText: 'FL-27' })).toHaveCount(1);
  121 |   });
  122 | 
  123 |   test('filters combine, count matches and survive a reload', async ({ page, request }) => {
  124 |     await skipIfPremiumGated(request, '/voters/');
  125 |     await page.goto('/voters/?f_state=FL&f_county=MIAMI-DADE&f_age=65-74&f_party=DEM');
  126 |     const results = page.locator('.entity-search-result');
  127 |     await expect(results.first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  128 |     await expect(results.first()).toContainText('Democratic (DEM)');
  129 |     await expect(results.first()).toContainText(/Age (6[5-9]|7[0-4])\b/);
  130 |     await expect(page.locator('#entity-results-count')).toHaveText(/^[\d,]+ match(es)?$/);
  131 |     await expect(page.locator('#entity-results-title')).toContainText('Miami-Dade');
  132 |     await expect(page.locator('#entity-filter-county')).toHaveValue('MIAMI-DADE', { timeout: DATA_LOAD_TIMEOUT });
  133 | 
  134 |     await page.locator('#entity-filter-reset').click();
  135 |     await expect(page).not.toHaveURL(/f_state=/);
  136 |     await expect(page.locator('.entity-filter-control[data-key="county"]')).toBeHidden();
  137 |   });
  138 | 
  139 |   test('race and Civitech score filters (persuadable, high-turnout Latinx voters in Miami-Dade)', async ({ page, request }) => {
  140 |     await skipIfPremiumGated(request, '/voters/');
  141 |     await page.goto('/voters/?f_state=FL&f_county=MIAMI-DADE&f_race=LATINX&f_support_dem=tossup&f_turnout_pres=high');
  142 |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  143 |     await expect(page.locator('#entity-results-count')).toHaveText(/^[\d,]+ match(es)?$/);
  144 |     await expect(page.locator('#entity-results-title')).toContainText('Latinx');
  145 |     await expect(page.locator('#entity-filter-support_dem')).toHaveValue('tossup');
  146 |   });
  147 | 
  148 |   test('every Districts row links; legislative districts open the filtered voters list', async ({ page, request }) => {
  149 |     await skipIfPremiumGated(request, '/voters/');
  150 |     // A Rochester, NY voter with all four district codes and a precinct.
  151 |     await page.goto('/voters/0c13135d-8e3d-3aa4-1077-28a81eb5d10f/');
  152 |     const districts = page.locator('section#districts');
  153 |     await expect(districts.locator('a').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  154 |     const hrefs = await districts.locator('a').evaluateAll(as => as.map(a => a.getAttribute('href')));
  155 |     expect(hrefs).toEqual(expect.arrayContaining([
  156 |       '/districts/ny-25/',
  157 |       '/voters/?f_state=NY&f_cd=NY-25',
  158 |       '/voters/?f_state=NY&f_senate=NY-055',
  159 |       '/voters/?f_state=NY&f_house=NY-138',
  160 |       '/voters/?f_state=NY&f_precinct=36055Rochester_Ward_24_ED_6',
  161 |     ]));
  162 |     await expect(districts.locator('dd')).toHaveCount(4);
  163 |     expect(await districts.locator('dd:not(:has(a))').count()).toBe(0);
  164 | 
  165 |     // Location: state, place, ZIP and county pages (graph ids from the public API) plus the filtered voters list.
  166 |     const location = page.locator('section#location');
> 167 |     await expect(location.locator('a[href^="/places/"]')).toHaveText('Rochester city', { timeout: DATA_LOAD_TIMEOUT });
      |                                                           ^ Error: expect(locator).toHaveText(expected) failed
  168 |     const locationHrefs = await location.locator('a').evaluateAll(as => as.map(a => a.getAttribute('href')));
  169 |     expect(locationHrefs).toEqual([
  170 |       '/states/ny/', '/voters/?f_state=NY',
  171 |       expect.stringMatching(/^\/places\/[0-9a-f-]+\/$/), '/voters/?f_state=NY&f_city=ROCHESTER',
  172 |       expect.stringMatching(/^\/postalcodes\/[0-9a-f-]+\/$/), '/voters/?f_state=NY&f_zip=14620',
  173 |       expect.stringMatching(/^\/counties\/[0-9a-f-]+\/$/), '/voters/?f_state=NY&f_county=MONROE',
  174 |     ]);
  175 |     await expect(location.locator('a[href^="/counties/"]')).toHaveText('Monroe County');
  176 |     await expect(page.locator('section#overview a')).toHaveCount(0);
  177 | 
  178 |     await districts.locator('a', { hasText: 'NY-055' }).click();
  179 |     await expect(page).toHaveURL(/\/voters\/\?f_state=NY&f_senate=NY-055$/);
  180 |     await expect(page.locator('.entity-search-result').first()).toContainText(', NY', { timeout: DATA_LOAD_TIMEOUT });
  181 |     await expect(page.locator('#entity-filter-senate')).toHaveValue('NY-055', { timeout: DATA_LOAD_TIMEOUT });
  182 |   });
  183 | 
  184 |   test('an unknown voter id renders Not Found', async ({ page, request }) => {
  185 |     await skipIfPremiumGated(request, '/voters/');
  186 |     await page.goto('/voters/00000000-0000-0000-0000-000000000000/');
  187 |     await expect(page.locator('.leaf-detail-page h1')).toHaveText(/Voter Not Found/, { timeout: DATA_LOAD_TIMEOUT });
  188 |   });
  189 | });
  190 | 
  191 | test.describe('voter Location card (mocked)', () => {
  192 |   test.beforeEach(async ({ request }) => {
  193 |     await skipIfPremiumGated(request, '/voters/');
  194 |   });
  195 | 
  196 |   const voter = {
  197 |     id: 'v-loc', name_last: 'MOLLICA', name_first: 'CAROL', state: 'MA', addr_state: 'MA',
  198 |     city: 'WRENTHAM', zip: '02093', county: 'NORFOLK', congressional_district: 'MA-04',
  199 |     state_senate_district: 'MA-D14', state_house_district: 'MA-104', precinct: '25021823150003',
  200 |   };
  201 | 
  202 |   test('State, Place, Postal code and County link their pages plus the filtered voters list; Overview and Districts drop them', async ({ page }) => {
  203 |     const lookups: string[] = [];
  204 |     await page.route('**/api/graphql', async route => {
  205 |       const body = route.request().postData() || '';
  206 |       if (route.request().url().includes('/voters/api/graphql')) {
  207 |         return route.fulfill({ json: { data: { voter } } });
  208 |       }
  209 |       lookups.push(body);
  210 |       return route.fulfill({ json: { data: {
  211 |         postalCodes: [{ id: 'zip-02093', zip_code: '02093', overlapsCounties: [
  212 |           { id: 'cty-bristol', name: 'Bristol County', state: 'MA' },
  213 |           { id: 'cty-norfolk', name: 'Norfolk County', state: 'MA' },
  214 |         ] }],
  215 |         places: [{ id: 'place-wrentham', name: 'Wrentham CDP' }],
  216 |       } } });
  217 |     });
  218 |     await page.goto('/voters/v-loc/');
  219 |     const location = page.locator('section#location');
  220 |     await expect(location.locator('a').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  221 |     expect(await location.locator('a').evaluateAll(as => as.map(a => a.getAttribute('href')))).toEqual([
  222 |       '/states/ma/', '/voters/?f_state=MA',
  223 |       '/places/place-wrentham/', '/voters/?f_state=MA&f_city=WRENTHAM',
  224 |       '/postalcodes/zip-02093/', '/voters/?f_state=MA&f_zip=02093',
  225 |       '/counties/cty-norfolk/', '/voters/?f_state=MA&f_county=NORFOLK',
  226 |     ]);
  227 |     await expect(location.locator('a[href="/counties/cty-norfolk/"]')).toHaveText('Norfolk County');
  228 |     expect(lookups.some(b => b.includes('query VoterLocation(') && b.includes('"city":"WRENTHAM"') && b.includes('"zip":"02093"'))).toBe(true);
  229 |     await expect(page.locator('section#overview')).not.toContainText('Wrentham');
  230 |     await expect(page.locator('section#districts dt')).toHaveText(['Congressional district', 'State senate district', 'State house district', 'Precinct']);
  231 |   });
  232 | 
  233 |   test('without graph matches (or a failed lookup) each value opens the filtered voters list', async ({ page }) => {
  234 |     await page.route('**/api/graphql', async route => {
  235 |       if (route.request().url().includes('/voters/api/graphql')) {
  236 |         return route.fulfill({ json: { data: { voter } } });
  237 |       }
  238 |       return route.fulfill({ status: 500, body: '' });
  239 |     });
  240 |     await page.goto('/voters/v-loc/');
  241 |     const location = page.locator('section#location');
  242 |     await expect(location.locator('a').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  243 |     expect(await location.locator('a').evaluateAll(as => as.map(a => a.getAttribute('href')))).toEqual([
  244 |       '/states/ma/', '/voters/?f_state=MA',
  245 |       '/voters/?f_state=MA&f_city=WRENTHAM',
  246 |       '/voters/?f_state=MA&f_zip=02093',
  247 |       '/voters/?f_state=MA&f_county=NORFOLK',
  248 |     ]);
  249 |   });
  250 | });
  251 | 
```