# 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/identity-bar.spec.ts >> identity bar (sites#355) >> signed out: controls are shown locked and only offer sign-in
- Location: tests/ui/identity-bar.spec.ts:81:7

# Error details

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

```
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://10.1.105.22/
Call log:
  - navigating to "http://10.1.105.22/", waiting until "load"

```

# Test source

```ts
  1   | import { test, expect, Page } from '@playwright/test';
  2   | 
  3   | // Full-width identity bar and member controls (electinfo/sites#355), shared by
  4   | // us.elect.info and us.elect.news (sites/shared/identity/). Controls are
  5   | // visible to everyone and usable only when signed in (and entitled, if a
  6   | // control names an area). /auth/whoami and /auth/account/me are mocked, so
  7   | // these run on CI pods (Authentik off) as well as on preview.
  8   | 
  9   | type Entitlements = { voters?: boolean; individuals?: boolean; blocks?: boolean; curator?: boolean; preview?: boolean };
  10  | 
  11  | // Two test controls, queued before the bar loads like a site script would.
  12  | const ICON = '<svg viewBox="0 0 16 16"><rect width="16" height="16" fill="currentColor"/></svg>';
  13  | const CONTROLS = `
  14  |   window.__activated = [];
  15  |   (window.electinfoBarControls = window.electinfoBarControls || []).push(
  16  |     { id: 'save', label: 'Save search', icon: '${ICON}', onActivate: function (c) { window.__activated.push('save:' + c.email); } },
  17  |     { id: 'export', label: 'Export CSV', icon: '${ICON}', requires: 'individuals', onActivate: function () { window.__activated.push('export'); } }
  18  |   );`;
  19  | 
  20  | async function open(page: Page, email: string, entitlements: Entitlements = {}, controls = true): Promise<void> {
  21  |   if (controls) await page.addInitScript(CONTROLS);
  22  |   await page.route('**/auth/whoami*', r => r.fulfill({ json: { enabled: 'true', email, elevated: false, entitlements } }));
  23  |   await page.route('**/auth/account/me*', r => r.fulfill({ json: { email, requests: [], agreement: null, granted: [] } }));
> 24  |   await page.goto('/');
      |              ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://10.1.105.22/
  25  |   await expect(page.locator('#auth-slot.ei-bar')).toBeVisible();
  26  | }
  27  | 
  28  | const activated = (page: Page): Promise<string[]> => page.evaluate(() => (window as unknown as { __activated: string[] }).__activated);
  29  | 
  30  | test.describe('identity bar (sites#355)', () => {
  31  |   test('spans the page width under the header', async ({ page }) => {
  32  |     await open(page, '');
  33  |     const bar = await page.locator('#auth-slot').boundingBox();
  34  |     const header = await page.locator('body > header').boundingBox();
  35  |     expect(bar && header && Math.abs(bar.width - header.width) <= 2, 'bar as wide as the header').toBe(true);
  36  |     expect(bar && header && bar.y >= header.y + header.height, 'bar below the header').toBe(true);
  37  |   });
  38  | 
  39  |   for (const width of [1280, 390, 320]) {
  40  |     test(`one row at ${width}px: identity and account actions left, controls right`, async ({ page }) => {
  41  |       await page.setViewportSize({ width, height: 800 });
  42  |       await open(page, 'jane.reporter@example.org', { voters: true });
  43  |       const box = async (sel: string) => (await page.locator(sel).boundingBox())!;
  44  |       const bar = await box('#auth-slot');
  45  |       const identity = await box('#auth-slot .ei-bar__identity');
  46  |       const controls = await box('#auth-slot .ei-bar__controls');
  47  |       const logout = await box('#auth-slot a.auth-link');
  48  |       for (const [name, b] of [['identity', identity], ['controls', controls], ['Log out', logout]] as const) {
  49  |         expect(b.y >= bar.y && b.y + b.height <= bar.y + bar.height, `${name} inside the bar's single row`).toBe(true);
  50  |       }
  51  |       expect(bar.height, 'a single row').toBeLessThan(56);
  52  |       expect(identity.x - bar.x, 'identity left-justified').toBeLessThan(16);
  53  |       expect(bar.x + bar.width - (controls.x + controls.width), 'controls right-justified').toBeLessThan(16);
  54  |       expect(controls.x >= identity.x + identity.width, 'controls after the identity').toBe(true);
  55  |       expect(logout.x + logout.width <= controls.x, 'account actions end before the controls').toBe(true);
  56  |       for (const id of ['save', 'export']) {
  57  |         const c = await box(`#auth-slot [data-control="${id}"]`);
  58  |         expect(c.x >= controls.x - 1 && c.x + c.width <= controls.x + controls.width + 1, `${id} fully visible`).toBe(true);
  59  |       }
  60  |       expect(await page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth)).toBeLessThanOrEqual(0);
  61  |     });
  62  |   }
  63  | 
  64  |   for (const width of [1280, 390, 320]) {
  65  |     test(`signed out at ${width}px: Log in and Create an account are buttons in one row`, async ({ page }) => {
  66  |       await page.setViewportSize({ width, height: 800 });
  67  |       await open(page, '');
  68  |       const login = page.locator('#auth-slot a.auth-link');
  69  |       const register = page.locator('#auth-slot a.ei-identity__register');
  70  |       await expect(login).toHaveText('Log in');
  71  |       await expect(register).toBeVisible();
  72  |       await expect(register).toHaveClass(/ei-identity__action/);
  73  |       await expect(register).toHaveText(width <= 480 ? 'Sign up' : 'Create an account', { useInnerText: true });
  74  |       const [l, r, c, bar] = await Promise.all([login.boundingBox(), register.boundingBox(), page.locator('#auth-slot .ei-bar__controls').boundingBox(), page.locator('#auth-slot').boundingBox()]);
  75  |       expect(Math.abs(l!.height - r!.height), 'same height as Log in').toBeLessThanOrEqual(1);
  76  |       expect(r!.x + r!.width <= c!.x, 'before the controls').toBe(true);
  77  |       expect(bar!.height, 'a single row').toBeLessThan(56);
  78  |     });
  79  |   }
  80  | 
  81  |   test('signed out: controls are shown locked and only offer sign-in', async ({ page }) => {
  82  |     await open(page, '');
  83  |     const save = page.locator('#auth-slot [data-control="save"]');
  84  |     await expect(page.locator('#auth-slot .ei-bar__controls')).toBeVisible();
  85  |     await expect(save).toBeVisible();
  86  |     await expect(save).toHaveAttribute('aria-disabled', 'true');
  87  |     await expect(save).toHaveClass(/ei-control--locked/);
  88  |     await expect(page.locator('#auth-slot a.auth-link')).toHaveText('Log in');
  89  |     await save.click({ force: true });
  90  |     const panel = page.locator('dialog#ei-signin');
  91  |     await expect(panel).toBeVisible();
  92  |     await expect(panel.locator('h2')).toHaveText('Sign in to use Save search');
  93  |     expect(await activated(page)).toEqual([]);
  94  |   });
  95  | 
  96  |   test('signed in: member controls work; area controls stay locked and offer Request access', async ({ page }) => {
  97  |     await open(page, 'jane@example.org');
  98  |     const save = page.locator('#auth-slot [data-control="save"]');
  99  |     await expect(save).not.toHaveAttribute('aria-disabled', 'true');
  100 |     await save.click();
  101 |     expect(await activated(page)).toEqual(['save:jane@example.org']);
  102 | 
  103 |     const exp = page.locator('#auth-slot [data-control="export"]');
  104 |     await expect(exp).toHaveAttribute('aria-disabled', 'true');
  105 |     await exp.click({ force: true });
  106 |     const form = page.locator('dialog#ei-request');
  107 |     await expect(form).toBeVisible();
  108 |     await expect(form.locator('input[name="requested"][value="individuals"]')).toBeChecked();
  109 |     expect(await activated(page)).toEqual(['save:jane@example.org']);
  110 |   });
  111 | 
  112 |   test('entitled: the area control works', async ({ page }) => {
  113 |     await open(page, 'jane@example.org', { individuals: true });
  114 |     await page.locator('#auth-slot [data-control="export"]').click();
  115 |     expect(await activated(page)).toEqual(['export']);
  116 |   });
  117 | 
  118 |   test('entitlements open from Registered and never push the controls off the bar', async ({ page }) => {
  119 |     await page.setViewportSize({ width: 1280, height: 800 });
  120 |     await open(page, 'jane.reporter@example.org', { voters: true, individuals: true, blocks: true, curator: true, preview: true });
  121 |     const chips = page.locator('#auth-slot .ei-chip');
  122 |     await expect(chips).toHaveCount(1);
  123 |     await expect(chips).toHaveText('Registered');
  124 |     const controls = (await page.locator('#auth-slot .ei-bar__controls').boundingBox())!;
```