# 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/graph-control.spec.ts >> graph control everywhere (sites#355) >> a committee page keeps one Graph control: the real pop-over
- Location: tests/ui/graph-control.spec.ts:39:7

# Error details

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

```
Error: locator.click: Test timeout of 30000ms exceeded.
Call log:
  - waiting for locator('#auth-slot [data-control="graph"]')
    - locator resolved to <button type="button" data-control="graph" aria-disabled="true" class="ei-control ei-control--locked" title="Needs Graph access: request it" aria-label="Graph (Needs Graph access: request it)">…</button>
  - attempting click action
    2 × waiting for element to be visible, enabled and stable
      - element is not enabled
    - retrying click action
    - waiting 20ms
    2 × waiting for element to be visible, enabled and stable
      - element is not enabled
    - retrying click action
      - waiting 100ms
    57 × waiting for element to be visible, enabled and stable
       - element is not enabled
     - retrying click action
       - waiting 500ms
    - waiting for element to be visible, enabled and stable

```

# Page snapshot

```yaml
- generic [active] [ref=e1]:
  - banner [ref=e2]:
    - heading "DNC SERVICES CORP / DEMOCRATIC NATIONAL COMMITTEE" [level=1] [ref=e3]
    - paragraph [ref=e4]: Party - Qualified
    - paragraph [ref=e5]: "FEC ID: C00010603"
  - generic [ref=e6]:
    - generic [ref=e7]:
      - generic [ref=e8]:
        - generic "jane@example.org" [ref=e9]
        - button "Registered" [ref=e11] [cursor=pointer]
      - generic [ref=e14]:
        - button "Request additional access" [ref=e15] [cursor=pointer]
        - link "Log out" [ref=e16] [cursor=pointer]:
          - /url: /outpost.goauthentik.io/sign_out
    - toolbar "Member tools" [ref=e17]:
      - 'button "All cycles (Needs Election cycles access: request it)" [disabled] [ref=e18] [cursor=pointer]':
        - generic [ref=e22]: All cycles
      - button "API keys" [ref=e25] [cursor=pointer]
      - 'button "Graph (Needs Graph access: request it)" [disabled] [ref=e30] [cursor=pointer]':
        - generic [ref=e39]: Graph
  - generic [ref=e42]:
    - heading "Overview" [level=2] [ref=e43]
    - generic [ref=e44]:
      - term [ref=e45]: Committee Type
      - definition [ref=e46]: Party - Qualified
      - term [ref=e47]: Designation
      - definition [ref=e48]: U
      - term [ref=e49]: Party Affiliation
      - definition [ref=e50]:
        - link "Democratic Party" [ref=e51] [cursor=pointer]:
          - /url: https://preview.elect.info/parties/democratic/
      - term [ref=e52]: Treasurer
      - definition [ref=e53]: MCGREGOR, VIRGINIA
      - term [ref=e54]: Location
      - definition [ref=e55]:
        - generic [ref=e56]:
          - text: WASHINGTON,
          - link "District of Columbia" [ref=e57] [cursor=pointer]:
            - /url: https://preview.elect.info/states/district-of-columbia/
          - text: "20003"
      - term [ref=e58]: Active Cycles
      - definition [ref=e59]:
        - generic [ref=e60]:
          - link "2026" [ref=e61] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2026/
          - text: ","
          - link "2024" [ref=e62] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2024/
          - text: ","
          - link "2022" [ref=e63] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2022/
          - text: ","
          - link "2020" [ref=e64] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2020/
          - text: ","
          - link "2018" [ref=e65] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2018/
          - text: ","
          - link "2016" [ref=e66] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2016/
          - text: ","
          - link "2014" [ref=e67] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2014/
          - text: ","
          - link "2012" [ref=e68] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2012/
          - text: ","
          - link "2010" [ref=e69] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2010/
          - text: ","
          - link "2008" [ref=e70] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2008/
          - text: ","
          - link "2006" [ref=e71] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2006/
          - text: ","
          - link "2004" [ref=e72] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2004/
          - text: ","
          - link "2002" [ref=e73] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2002/
          - text: ","
          - link "2000" [ref=e74] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/2000/
          - text: ","
          - link "1998" [ref=e75] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/1998/
          - text: ","
          - link "1996" [ref=e76] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/1996/
          - text: ","
          - link "1994" [ref=e77] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/1994/
          - text: ","
          - link "1992" [ref=e78] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/1992/
          - text: ","
          - link "1990" [ref=e79] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/1990/
          - text: ","
          - link "1988" [ref=e80] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/1988/
          - text: ","
          - link "1986" [ref=e81] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/1986/
          - text: ","
          - link "1984" [ref=e82] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/1984/
          - text: ","
          - link "1982" [ref=e83] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/1982/
          - text: ","
          - link "1980" [ref=e84] [cursor=pointer]:
            - /url: https://preview.elect.info/elections/1980/
      - term [ref=e85]: Party Affinity
      - definition [ref=e86]:
        - generic [ref=e87]:
          - link "Democratic Party" [ref=e88] [cursor=pointer]:
            - /url: https://preview.elect.info/parties/democratic/
          - text: ": 81%"
          - link "Republican Party" [ref=e89] [cursor=pointer]:
            - /url: https://preview.elect.info/parties/republican/
          - text: ": 19%"
  - generic [ref=e90]:
    - heading "Financial Summary" [level=2] [ref=e91]
    - generic [ref=e92]:
      - term [ref=e93]: Total Receipts
      - definition [ref=e94]:
        - strong [ref=e95]: $4505.2M
      - term [ref=e96]: Total Disbursements
      - definition [ref=e97]: $4489.7M
  - generic [ref=e98]:
    - heading "Connected Organizations" [level=2] [ref=e99]
    - generic [ref=e100]:
      - term [ref=e101]: Connected Organization
      - definition [ref=e102]: DEMOCRATIC GRASSROOTS VICTORY FUND
  - generic [ref=e104]:
    - generic [ref=e105]:
      - heading "Candidates Funded" [level=2] [ref=e106]
      - group "View" [ref=e107]:
        - button "Sankey view" [pressed] [ref=e108] [cursor=pointer]
        - button "Table view" [ref=e112] [cursor=pointer]
    - paragraph [ref=e116]: "Candidates receiving contributions from this committee:"
    - img "Candidates Funded as a Sankey diagram. Switch to table view for the list." [ref=e117]
    - paragraph [ref=e120]: Showing top 20 of 439
  - generic [ref=e121]:
    - generic [ref=e122]:
      - heading "Independent Expenditures For" [level=2] [ref=e123]
      - group "View" [ref=e124]:
        - button "Sankey view" [pressed] [ref=e125] [cursor=pointer]
        - button "Table view" [ref=e129] [cursor=pointer]
    - paragraph [ref=e133]: "Candidates this committee spent for, independently of their campaigns (not contributions to the campaigns):"
    - img "Independent Expenditures For as a Sankey diagram. Switch to table view for the list." [ref=e134]
    - paragraph [ref=e137]: Showing all 6
  - generic [ref=e138]:
    - generic [ref=e139]:
      - heading "Independent Expenditures Against" [level=2] [ref=e140]
      - group "View" [ref=e141]:
        - button "Sankey view" [pressed] [ref=e142] [cursor=pointer]
        - button "Table view" [ref=e146] [cursor=pointer]
    - paragraph [ref=e150]: "Candidates this committee spent against, independently of their campaigns:"
    - img "Independent Expenditures Against as a Sankey diagram. Switch to table view for the list." [ref=e151]
    - paragraph [ref=e154]: Showing all 5
  - generic [ref=e155]:
    - generic [ref=e156]:
      - heading "Committees Funded" [level=2] [ref=e157]
      - group "View" [ref=e158]:
        - button "Sankey view" [pressed] [ref=e159] [cursor=pointer]
        - button "Table view" [ref=e163] [cursor=pointer]
    - paragraph [ref=e167]: "Committees receiving transfers from this committee:"
    - img "Committees Funded as a Sankey diagram. Switch to table view for the list." [ref=e168]
    - paragraph [ref=e171]: Showing top 20 of 1,646
  - generic [ref=e172]:
    - generic [ref=e173]:
      - heading "Funded By" [level=2] [ref=e174]
      - group "View" [ref=e175]:
        - button "Sankey view" [pressed] [ref=e176] [cursor=pointer]
        - button "Table view" [ref=e180] [cursor=pointer]
    - paragraph [ref=e184]: "Committees that have transferred funds to this committee:"
    - img "Funded By as a Sankey diagram. Switch to table view for the list." [ref=e185]
    - paragraph [ref=e188]: Showing top 20 of 2,262
  - contentinfo [ref=e189]:
    - paragraph [ref=e190]:
      - link "elect.info" [ref=e191] [cursor=pointer]:
        - /url: https://preview.elect.info/
      - text: "| Data from"
      - link "Federal Election Commission" [ref=e192] [cursor=pointer]:
        - /url: https://preview.elect.info/datasources/fec_bulk/
      - text: "| Generated 2026-10-25"
```

# Test source

```ts
  1  | import { test, expect, Page } from '@playwright/test';
  2  | 
  3  | // The Graph member control on every page (sites#355). Entity pages keep the
  4  | // real graph pop-over (entity-graph.js); elsewhere the control
  5  | // is a placeholder: locked for anonymous visitors, and for members a hint
  6  | // pointing at the pages with a graph. Identity is mocked (Authentik on), so
  7  | // these run on CI pods and on preview alike.
  8  | 
  9  | const DNC = '/committees/a75a9110-f1db-a945-39a8-7503ed3c04da/'; // C00010603
  10 | 
  11 | async function as(page: Page, email: string): Promise<void> {
  12 |   await page.route('**/auth/whoami*', r => r.fulfill({ json: { enabled: 'true', email, elevated: false, entitlements: {} } }));
  13 |   await page.route('**/auth/account/me*', r => r.fulfill({ json: { email, requests: [], agreement: null, granted: [] } }));
  14 | }
  15 | 
  16 | test.describe('graph control everywhere (sites#355)', () => {
  17 |   for (const path of ['/', '/candidates/']) {
  18 |     test(`a member sees Graph on ${path} and gets the hint`, async ({ page }) => {
  19 |       await as(page, 'jane@example.org');
  20 |       await page.goto(path);
  21 |       const graph = page.locator('#auth-slot [data-control="graph"]');
  22 |       await expect(graph).toHaveCount(1, { timeout: 15000 });
  23 |       await expect(graph).not.toHaveAttribute('aria-disabled', 'true');
  24 |       await graph.click();
  25 |       await expect(page.locator('#ei-graph-hint')).toContainText('geography page');
  26 |       await expect(page.locator('dialog.eg-dialog')).toHaveCount(0);
  27 |     });
  28 |   }
  29 | 
  30 |   test('signed out, the homepage Graph is locked and offers sign-in', async ({ page }) => {
  31 |     await as(page, '');
  32 |     await page.goto('/');
  33 |     const graph = page.locator('#auth-slot [data-control="graph"]');
  34 |     await expect(graph).toHaveAttribute('aria-disabled', 'true', { timeout: 15000 });
  35 |     await graph.click({ force: true });
  36 |     await expect(page.locator('dialog#ei-signin h2')).toHaveText('Sign in to use Graph');
  37 |   });
  38 | 
  39 |   test('a committee page keeps one Graph control: the real pop-over', async ({ page }) => {
  40 |     await as(page, 'jane@example.org');
  41 |     await page.goto(DNC);
  42 |     const graph = page.locator('#auth-slot [data-control="graph"]');
  43 |     await expect(graph).toHaveCount(1, { timeout: 15000 });
> 44 |     await graph.click();
     |                 ^ Error: locator.click: Test timeout of 30000ms exceeded.
  45 |     await expect(page.locator('dialog.eg-dialog')).toBeVisible();
  46 |     await expect(page.locator('#ei-graph-hint')).toHaveCount(0);
  47 |   });
  48 | });
  49 | 
```