# 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 >> office profile - dynamic candidates >> office profile renders candidate results in UI
- Location: tests/ui/entities.spec.ts:240:7

# Error details

```
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://10.1.105.22/offices/684f6e2d-7105-932b-8b7f-94016580f727/
Call log:
  - navigating to "http://10.1.105.22/offices/684f6e2d-7105-932b-8b7f-94016580f727/", waiting until "load"

```

# Test source

```ts
  141 |     await expect(page.locator('#map.leaflet-container')).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  142 |     await expect(page.locator('#map .leaflet-interactive').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  143 |     const shapes = await page.locator('#map .leaflet-interactive').all();
  144 |     expect(shapes.length).toBeGreaterThan(0);
  145 | 
  146 |     // Switch to Grid view to test state chips and detail navigation
  147 |     await page.locator('.toggle-btn[data-view="grid"]').click();
  148 | 
  149 |     await expect(page.locator('.state-chip').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  150 | 
  151 |     const links = await page.locator('.state-chip').all();
  152 |     expect(links.length).toBeGreaterThan(0);
  153 | 
  154 |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  155 |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  156 | 
  157 |     for (const href of randomLinks) {
  158 |       const entityResponse = await page.goto(toRelativePath(href));
  159 |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  160 |       await expect(page.locator('h1')).toBeVisible();
  161 |     }
  162 |   });
  163 | 
  164 |   test('elections section - index and random pages', async ({ page }) => {
  165 |     const indexResponse = await page.goto('/elections/');
  166 |     expect(indexResponse?.status(), '/elections/ should answer 200').toBe(200);
  167 |     await expect(page.locator('h1')).toContainText(/Election/i);
  168 | 
  169 |     // Wait for GraphQL browseEntities to populate results
  170 |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  171 | 
  172 |     const links = await page.locator('.entity-search-result').all();
  173 |     expect(links.length).toBeGreaterThan(0);
  174 | 
  175 |     const hrefs = await Promise.all(links.slice(0, 20).map(l => l.getAttribute('href')));
  176 |     const randomLinks = getRandomItems(hrefs.filter(Boolean) as string[], 3);
  177 | 
  178 |     for (const href of randomLinks) {
  179 |       const entityResponse = await page.goto(toRelativePath(href));
  180 |       expect(entityResponse?.status(), `${toRelativePath(href)} should answer 200`).toBe(200);
  181 |       await expect(page.locator('h1')).toBeVisible();
  182 |     }
  183 |   });
  184 | });
  185 | 
  186 | test.describe('office profile - dynamic candidates', () => {
  187 |   // President of the United States office page
  188 |   const PRESIDENT_PATH = '/offices/684f6e2d-7105-932b-8b7f-94016580f727/';
  189 | 
  190 |   test('office profile loads candidates filtered to office', async ({ page }) => {
  191 |     // Intercept the browseEntities GraphQL request to verify filter is applied
  192 |     const responsePromise = page.waitForResponse(resp => {
  193 |       if (!resp.url().includes('/api/graphql')) return false;
  194 |       if (resp.request().method() !== 'POST') return false;
  195 |       const body = resp.request().postData() || '';
  196 |       return body.includes('browseEntities');
  197 |     }, { timeout: DATA_LOAD_TIMEOUT });
  198 | 
  199 |     await page.goto(PRESIDENT_PATH);
  200 |     const response = await responsePromise;
  201 | 
  202 |     // Verify the request includes the office filter
  203 |     const requestBody = JSON.parse(response.request().postData() || '{}');
  204 |     expect(requestBody.variables?.filter).toContain('office:P');
  205 | 
  206 |     // Verify the response has results
  207 |     const json = await response.json();
  208 |     const results = json.data?.browseEntities?.results || [];
  209 |     expect(results.length).toBeGreaterThan(0);
  210 |   });
  211 | 
  212 |   test('office profile candidates sorted by total contributions descending', async ({ page }) => {
  213 |     const responsePromise = page.waitForResponse(resp => {
  214 |       if (!resp.url().includes('/api/graphql')) return false;
  215 |       if (resp.request().method() !== 'POST') return false;
  216 |       const body = resp.request().postData() || '';
  217 |       return body.includes('browseEntities');
  218 |     }, { timeout: DATA_LOAD_TIMEOUT });
  219 | 
  220 |     await page.goto(PRESIDENT_PATH);
  221 |     const response = await responsePromise;
  222 | 
  223 |     const json = await response.json();
  224 |     const results = json.data?.browseEntities?.results || [];
  225 |     expect(results.length).toBeGreaterThan(1);
  226 | 
  227 |     // Extract total_contributions from metadata to verify sort order
  228 |     const contributions = results.map((r: { metadata?: string }) => {
  229 |       try {
  230 |         const meta = JSON.parse(r.metadata || '{}');
  231 |         return meta.total_contributions || 0;
  232 |       } catch { return 0; }
  233 |     });
  234 | 
  235 |     for (let i = 1; i < contributions.length; i++) {
  236 |       expect(contributions[i - 1]).toBeGreaterThanOrEqual(contributions[i]);
  237 |     }
  238 |   });
  239 | 
  240 |   test('office profile renders candidate results in UI', async ({ page }) => {
> 241 |     await page.goto(PRESIDENT_PATH);
      |                ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://10.1.105.22/offices/684f6e2d-7105-932b-8b7f-94016580f727/
  242 | 
  243 |     // Wait for candidate results to render
  244 |     await expect(page.locator('.entity-search-result').first()).toBeVisible({ timeout: DATA_LOAD_TIMEOUT });
  245 | 
  246 |     const results = await page.locator('.entity-search-result').all();
  247 |     expect(results.length).toBeGreaterThan(0);
  248 | 
  249 |     // First result should be a well-known presidential candidate with high contributions
  250 |     const firstResultText = await results[0].textContent();
  251 |     expect(firstResultText).toMatch(/\$/); // Should show dollar amount
  252 |   });
  253 | });
  254 | 
  255 | test.describe('state profile - dynamic candidates', () => {
  256 |   const TEXAS_PATH = '/states/tx/';
  257 | 
  258 |   test('state profile loads candidates filtered to state', async ({ page }) => {
  259 |     const responsePromise = page.waitForResponse(resp => {
  260 |       if (!resp.url().includes('/api/graphql')) return false;
  261 |       if (resp.request().method() !== 'POST') return false;
  262 |       const body = resp.request().postData() || '';
  263 |       return body.includes('browseEntities');
  264 |     }, { timeout: DATA_LOAD_TIMEOUT });
  265 | 
  266 |     await page.goto(TEXAS_PATH);
  267 |     const response = await responsePromise;
  268 | 
  269 |     const requestBody = JSON.parse(response.request().postData() || '{}');
  270 |     expect(requestBody.variables?.filter).toContain('office_state:TX');
  271 | 
  272 |     const json = await response.json();
  273 |     const results = json.data?.browseEntities?.results || [];
  274 |     expect(results.length).toBeGreaterThan(0);
  275 |   });
  276 | 
  277 |   test('state profile candidates sorted by total contributions descending', async ({ page }) => {
  278 |     const responsePromise = page.waitForResponse(resp => {
  279 |       if (!resp.url().includes('/api/graphql')) return false;
  280 |       if (resp.request().method() !== 'POST') return false;
  281 |       const body = resp.request().postData() || '';
  282 |       return body.includes('browseEntities');
  283 |     }, { timeout: DATA_LOAD_TIMEOUT });
  284 | 
  285 |     await page.goto(TEXAS_PATH);
  286 |     const response = await responsePromise;
  287 | 
  288 |     const json = await response.json();
  289 |     const results = json.data?.browseEntities?.results || [];
  290 |     expect(results.length).toBeGreaterThan(1);
  291 | 
  292 |     const contributions = results.map((r: { metadata?: string }) => {
  293 |       try {
  294 |         const meta = JSON.parse(r.metadata || '{}');
  295 |         return meta.total_contributions || 0;
  296 |       } catch { return 0; }
  297 |     });
  298 | 
  299 |     for (let i = 1; i < contributions.length; i++) {
  300 |       expect(contributions[i - 1]).toBeGreaterThanOrEqual(contributions[i]);
  301 |     }
  302 |   });
  303 | });
  304 | 
  305 | test.describe('district profile - dynamic candidates', () => {
  306 |   const DISTRICT_PATH = '/districts/tx-10/';
  307 | 
  308 |   test('district profile loads candidates filtered to district', async ({ page }) => {
  309 |     const responsePromise = page.waitForResponse(resp => {
  310 |       if (!resp.url().includes('/api/graphql')) return false;
  311 |       if (resp.request().method() !== 'POST') return false;
  312 |       const body = resp.request().postData() || '';
  313 |       return body.includes('browseEntities');
  314 |     }, { timeout: DATA_LOAD_TIMEOUT });
  315 | 
  316 |     await page.goto(DISTRICT_PATH);
  317 |     const response = await responsePromise;
  318 | 
  319 |     const requestBody = JSON.parse(response.request().postData() || '{}');
  320 |     const filter = requestBody.variables?.filter || '';
  321 |     expect(filter).toContain('office:H');
  322 |     expect(filter).toContain('office_state:TX');
  323 |     expect(filter).toContain('office_district:10');
  324 | 
  325 |     const json = await response.json();
  326 |     const results = json.data?.browseEntities?.results || [];
  327 |     expect(results.length).toBeGreaterThan(0);
  328 |   });
  329 | 
  330 |   test('district profile candidates sorted by total contributions descending', async ({ page }) => {
  331 |     const responsePromise = page.waitForResponse(resp => {
  332 |       if (!resp.url().includes('/api/graphql')) return false;
  333 |       if (resp.request().method() !== 'POST') return false;
  334 |       const body = resp.request().postData() || '';
  335 |       return body.includes('browseEntities');
  336 |     }, { timeout: DATA_LOAD_TIMEOUT });
  337 | 
  338 |     await page.goto(DISTRICT_PATH);
  339 |     const response = await responsePromise;
  340 | 
  341 |     const json = await response.json();
```