# 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/sitemap.spec.ts >> sitemap URL validation >> committees sitemap - sample URLs resolve
- Location: tests/ui/sitemap.spec.ts:229:7

# Error details

```
Error: Errors: /committees/sitemap-committees.xml lists 83545 URLs (limit 50000)

expect(received).toHaveLength(expected)

Expected length: 0
Received length: 1
Received array:  ["/committees/sitemap-committees.xml lists 83545 URLs (limit 50000)"]
```

# Page snapshot

```yaml
- generic [active] [ref=f5e1]:
  - banner [ref=f5e2]:
    - heading "UNITED WE DREAM ACTION" [level=1] [ref=f5e3]
    - paragraph [ref=f5e4]: Independent Expenditure (Person/Group)
    - paragraph [ref=f5e5]: "FEC ID: C90018045"
  - generic [ref=f5e6]:
    - heading "Overview" [level=2] [ref=f5e7]
    - generic [ref=f5e8]:
      - term [ref=f5e9]: Committee Type
      - definition [ref=f5e10]: Independent Expenditure (Person/Group)
      - term [ref=f5e11]: Designation
      - definition [ref=f5e12]: U
      - term [ref=f5e13]: Party Affiliation
      - definition [ref=f5e14]: N/A
      - term [ref=f5e15]: Treasurer
      - definition [ref=f5e16]: N/A
      - term [ref=f5e17]: Location
      - definition [ref=f5e18]:
        - text: WASHINGTON,
        - link "District of Columbia" [ref=f5e19] [cursor=pointer]:
          - /url: https://us.elect.info/states/district-of-columbia/
        - text: "20036"
      - term [ref=f5e20]: Active Cycles
      - definition [ref=f5e21]:
        - link "2018" [ref=f5e22] [cursor=pointer]:
          - /url: https://us.elect.info/elections/2018/
        - text: ","
        - link "2020" [ref=f5e23] [cursor=pointer]:
          - /url: https://us.elect.info/elections/2020/
        - text: ","
        - link "2022" [ref=f5e24] [cursor=pointer]:
          - /url: https://us.elect.info/elections/2022/
        - text: ","
        - link "2024" [ref=f5e25] [cursor=pointer]:
          - /url: https://us.elect.info/elections/2024/
        - text: ","
        - link "2026" [ref=f5e26] [cursor=pointer]:
          - /url: https://us.elect.info/elections/2026/
  - generic [ref=f5e27]:
    - heading "Financial Summary" [level=2] [ref=f5e28]
    - generic [ref=f5e29]:
      - term [ref=f5e30]: Total Receipts
      - definition [ref=f5e31]:
        - strong [ref=f5e32]: $738K
      - term [ref=f5e33]: Total Disbursements
      - definition [ref=f5e34]: $206K
  - generic [ref=f5e35]:
    - heading "Candidates Funded" [level=2] [ref=f5e36]
    - paragraph [ref=f5e37]: "Candidates receiving contributions from this committee:"
    - list [ref=f5e38]:
      - listitem [ref=f5e39]:
        - link "HARDER, JOSH" [ref=f5e40] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/house/ca-10/harder-josh/
        - text: $42K (7 contributions)
      - listitem [ref=f5e41]:
        - link "VALLEJO, MICHELLE" [ref=f5e42] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/house/tx-15/vallejo-michelle/
        - text: $32K (26 contributions)
      - listitem [ref=f5e43]:
        - link "HAALAND, DEBRA" [ref=f5e44] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/house/nm-01/haaland-debra/
        - text: $26K (122 contributions)
      - listitem [ref=f5e45]:
        - link "TRUMP, DONALD J. / MICHAEL R. PENCE" [ref=f5e46] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/president/trump-donald-j-michael-r-pence/
        - text: $25K (77 contributions)
      - listitem [ref=f5e47]:
        - link "RUBIO, MARCO" [ref=f5e48] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/senate/fl/rubio-marco/
        - text: $20K (7 contributions)
      - listitem [ref=f5e49]:
        - link "MASTERS, BLAKE" [ref=f5e50] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/senate/az/masters-blake/
        - text: $9K (6 contributions)
      - listitem [ref=f5e51]:
        - link "HARRIS, KAMALA D." [ref=f5e52] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/president/harris-kamala-d/
        - text: $9K (9 contributions)
      - listitem [ref=f5e53]:
        - link "SCOTT, RICK SEN" [ref=f5e54] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/senate/fl/scott-rick-sen/
        - text: $8K (15 contributions)
      - listitem [ref=f5e55]:
        - link "CASAR, GREG" [ref=f5e56] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/house/tx-35/casar-greg/
        - text: $7K (50 contributions)
      - listitem [ref=f5e57]:
        - link "CORTEZ MASTO, CATHERINE" [ref=f5e58] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/senate/nv/cortez-masto-catherine/
        - text: $5K (2 contributions)
      - listitem [ref=f5e59]:
        - link "CRUZ, RAFAEL EDWARD TED" [ref=f5e60] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/senate/tx/cruz-rafael-edward-ted/
        - text: $4K (5 contributions)
      - listitem [ref=f5e61]:
        - link "O'ROURKE, ROBERT (BETO)" [ref=f5e62] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/senate/tx/orourke-robert-beto/
        - text: $3K (21 contributions)
      - listitem [ref=f5e63]:
        - link "TORRES SMALL, XOCHITL" [ref=f5e64] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/house/nm-02/torres-small-xochitl/
        - text: $2K (5 contributions)
      - listitem [ref=f5e65]:
        - link "WARNOCK, RAPHAEL" [ref=f5e66] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/senate/ga/warnock-raphael/
        - text: $1K (1 contributions)
      - listitem [ref=f5e67]:
        - link "SANDERS, BERNARD" [ref=f5e68] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/president/sanders-bernard/
        - text: $995 (20 contributions)
      - listitem [ref=f5e69]:
        - link "TLAIB, RASHIDA" [ref=f5e70] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/house/mi-13/tlaib-rashida/
        - text: $928 (28 contributions)
      - listitem [ref=f5e71]:
        - link "OCASIO-CORTEZ, ALEXANDRIA" [ref=f5e72] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/house/ny-14/ocasio-cortez-alexandria/
        - text: $918 (26 contributions)
      - listitem [ref=f5e73]:
        - link "OMAR, ILHAN" [ref=f5e74] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/house/mn-05/omar-ilhan/
        - text: $874 (40 contributions)
      - listitem [ref=f5e75]:
        - link "BIDEN, JOSEPH R JR" [ref=f5e76] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/president/biden-joseph-r-jr/
        - text: $811 (12 contributions)
      - listitem [ref=f5e77]:
        - link "CISNEROS, JESSICA" [ref=f5e78] [cursor=pointer]:
          - /url: https://us.elect.info/candidates/house/tx-28/cisneros-jessica/
        - text: $764 (14 contributions)
    - paragraph [ref=f5e79]: Showing 20 of 20
  - generic [ref=f5e80]:
    - heading "Funded By" [level=2] [ref=f5e81]
    - paragraph [ref=f5e82]: "Committees that have transferred funds to this committee:"
    - list [ref=f5e83]:
      - listitem [ref=f5e84]:
        - link "Unknown" [ref=f5e85] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-h8ca10126/
        - text: $42K (7 transfers)
      - listitem [ref=f5e86]:
        - link "Unknown" [ref=f5e87] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-h2tx15203/
        - text: $32K (26 transfers)
      - listitem [ref=f5e88]:
        - link "Unknown" [ref=f5e89] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-h8nm01331/
        - text: $26K (122 transfers)
      - listitem [ref=f5e90]:
        - link "Unknown" [ref=f5e91] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-p80001571/
        - text: $25K (77 transfers)
      - listitem [ref=f5e92]:
        - link "Unknown" [ref=f5e93] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-s0fl00338/
        - text: $20K (7 transfers)
      - listitem [ref=f5e94]:
        - link "Unknown" [ref=f5e95] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-s2az00372/
        - text: $9K (6 transfers)
      - listitem [ref=f5e96]:
        - link "Unknown" [ref=f5e97] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-p00009423/
        - text: $9K (9 transfers)
      - listitem [ref=f5e98]:
        - link "Unknown" [ref=f5e99] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-s8fl00273/
        - text: $8K (15 transfers)
      - listitem [ref=f5e100]:
        - link "Unknown" [ref=f5e101] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-h2tx35144/
        - text: $7K (50 transfers)
      - listitem [ref=f5e102]:
        - link "Unknown" [ref=f5e103] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-s6nv00200/
        - text: $5K (2 transfers)
      - listitem [ref=f5e104]:
        - link "Unknown" [ref=f5e105] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-s2tx00312/
        - text: $4K (5 transfers)
      - listitem [ref=f5e106]:
        - link "Unknown" [ref=f5e107] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-s8tx00285/
        - text: $3K (21 transfers)
      - listitem [ref=f5e108]:
        - link "Unknown" [ref=f5e109] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-h8nm02248/
        - text: $2K (5 transfers)
      - listitem [ref=f5e110]:
        - link "Unknown" [ref=f5e111] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-s0ga00559/
        - text: $1K (1 transfers)
      - listitem [ref=f5e112]:
        - link "Unknown" [ref=f5e113] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-p60007168/
        - text: $995 (20 transfers)
      - listitem [ref=f5e114]:
        - link "Unknown" [ref=f5e115] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-h8mi13250/
        - text: $928 (28 transfers)
      - listitem [ref=f5e116]:
        - link "Unknown" [ref=f5e117] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-h8ny15148/
        - text: $918 (26 transfers)
      - listitem [ref=f5e118]:
        - link "Unknown" [ref=f5e119] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-h8mn05239/
        - text: $874 (40 transfers)
      - listitem [ref=f5e120]:
        - link "Unknown" [ref=f5e121] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-p80000722/
        - text: $811 (12 transfers)
      - listitem [ref=f5e122]:
        - link "Unknown" [ref=f5e123] [cursor=pointer]:
          - /url: https://us.elect.info/committees/other/unknown-h0tx28077/
        - text: $764 (14 transfers)
    - paragraph [ref=f5e124]: Showing 20 of 20
  - contentinfo [ref=f5e125]:
    - paragraph [ref=f5e126]:
      - link "elect.info" [ref=f5e127] [cursor=pointer]:
        - /url: https://us.elect.info/
      - text: "| Data from"
      - link "Federal Election Commission" [ref=f5e128] [cursor=pointer]:
        - /url: https://www.fec.gov/
      - text: "| Generated 2026-01-16"
```

# Test source

```ts
  145 | 
  146 |   if (isSitemapIndex(xml)) {
  147 |     // This is a sitemap index - sample child sitemaps and recurse
  148 |     console.log(`${indent}Detected sitemap index, sampling ${CHILD_SITEMAP_SAMPLE_SIZE} child sitemaps`);
  149 | 
  150 |     const childSitemaps = getRandomItems(urls, CHILD_SITEMAP_SAMPLE_SIZE, sitemapPath);
  151 |     for (const childUrl of childSitemaps) {
  152 |       const childPath = toRelativePath(childUrl);
  153 |       console.log(`${indent}Processing child sitemap: ${childPath}`);
  154 | 
  155 |       const childResult = await testSitemapUrls(page, baseUrl, childPath, depth + 1);
  156 |       result.tested += childResult.tested;
  157 |       result.errors.push(...childResult.errors);
  158 |     }
  159 |   } else {
  160 |     // This is a regular sitemap with page URLs - sample and test
  161 |     const sampleUrls = getRandomItems(urls, SAMPLE_SIZE, sitemapPath);
  162 | 
  163 |     for (const url of sampleUrls) {
  164 |       const path = toRelativePath(url);
  165 | 
  166 |       // Check if this URL is itself a sitemap (nested sitemap within urlset)
  167 |       if (isSitemapUrl(url)) {
  168 |         console.log(`${indent}Found nested sitemap URL: ${path}`);
  169 |         const nestedResult = await testSitemapUrls(page, baseUrl, path, depth + 1);
  170 |         result.tested += nestedResult.tested;
  171 |         result.errors.push(...nestedResult.errors);
  172 |       } else {
  173 |         // Regular page URL - verify it loads
  174 |         console.log(`${indent}Testing page: ${path}`);
  175 |         const pageResponse = await page.goto(path, { timeout: PAGE_TIMEOUT });
  176 |         result.tested++;
  177 | 
  178 |         if (pageResponse?.status() !== 200) {
  179 |           const error = `Expected 200 for ${path}, got ${pageResponse?.status()}`;
  180 |           console.log(`${indent}  ERROR: ${error}`);
  181 |           result.errors.push(error);
  182 |         } else {
  183 |           // Verify page has content
  184 |           const h1Visible = await page.locator('h1').isVisible().catch(() => false);
  185 |           if (!h1Visible) {
  186 |             const error = `Page ${path} loaded but has no visible h1`;
  187 |             console.log(`${indent}  WARNING: ${error}`);
  188 |             // Don't treat missing h1 as hard failure, just log it
  189 |           }
  190 |         }
  191 |       }
  192 |     }
  193 |   }
  194 | 
  195 |   console.log(`${indent}Tested ${result.tested} URLs from ${sitemapPath}`);
  196 |   return result;
  197 | }
  198 | 
  199 | test.describe('sitemap URL validation', () => {
  200 | 
  201 |   test('sitemap index loads and contains child sitemaps', async ({ page }) => {
  202 |     const response = await page.goto('/sitemap.xml');
  203 |     expect(response?.status()).toBe(200);
  204 | 
  205 |     const content = await page.content();
  206 |     expect(content).toContain('sitemapindex');
  207 |     expect(content).toContain('sitemap-candidates.xml');
  208 |   });
  209 | 
  210 |   test('candidates sitemap - sample URLs resolve', async ({ page }) => {
  211 |     test.setTimeout(120000);
  212 | 
  213 |     // Navigate to a simple page first so we have a page context for fetch
  214 |     await page.goto('/');
  215 |     const baseUrl = new URL(page.url()).origin;
  216 | 
  217 |     const result = await testSitemapUrls(page, baseUrl, '/candidates/sitemap-candidates.xml');
  218 | 
  219 |     if (result.tested === 0 && !STRICT) {
  220 |       test.skip();
  221 |       return;
  222 |     }
  223 |     expect(result.tested, `sitemap sampled no URLs; errors: ${result.errors.join(', ')}`).toBeGreaterThan(0);
  224 | 
  225 |     console.log(`Tested ${result.tested} URLs from candidates sitemap`);
  226 |     expect(result.errors, `Errors: ${result.errors.join(', ')}`).toHaveLength(0);
  227 |   });
  228 | 
  229 |   test('committees sitemap - sample URLs resolve', async ({ page }) => {
  230 |     test.setTimeout(120000);
  231 | 
  232 |     // Navigate to a simple page first so we have a page context for fetch
  233 |     await page.goto('/');
  234 |     const baseUrl = new URL(page.url()).origin;
  235 | 
  236 |     const result = await testSitemapUrls(page, baseUrl, '/committees/sitemap-committees.xml');
  237 | 
  238 |     if (result.tested === 0 && !STRICT) {
  239 |       test.skip();
  240 |       return;
  241 |     }
  242 |     expect(result.tested, `sitemap sampled no URLs; errors: ${result.errors.join(', ')}`).toBeGreaterThan(0);
  243 | 
  244 |     console.log(`Tested ${result.tested} URLs from committees sitemap`);
> 245 |     expect(result.errors, `Errors: ${result.errors.join(', ')}`).toHaveLength(0);
      |                                                                  ^ Error: Errors: /committees/sitemap-committees.xml lists 83545 URLs (limit 50000)
  246 |   });
  247 | 
  248 |   test('parties sitemap - sample URLs resolve', async ({ page }) => {
  249 |     await page.goto('/');
  250 |     const baseUrl = new URL(page.url()).origin;
  251 | 
  252 |     const result = await testSitemapUrls(page, baseUrl, '/parties/sitemap-parties.xml');
  253 | 
  254 |     if (result.tested === 0 && !STRICT) {
  255 |       test.skip();
  256 |       return;
  257 |     }
  258 |     expect(result.tested, `sitemap sampled no URLs; errors: ${result.errors.join(', ')}`).toBeGreaterThan(0);
  259 | 
  260 |     console.log(`Tested ${result.tested} URLs from parties sitemap`);
  261 |     expect(result.errors, `Errors: ${result.errors.join(', ')}`).toHaveLength(0);
  262 |   });
  263 | 
  264 |   test('offices sitemap - sample URLs resolve', async ({ page }) => {
  265 |     await page.goto('/');
  266 |     const baseUrl = new URL(page.url()).origin;
  267 | 
  268 |     const result = await testSitemapUrls(page, baseUrl, '/offices/sitemap-offices.xml');
  269 | 
  270 |     if (result.tested === 0 && !STRICT) {
  271 |       test.skip();
  272 |       return;
  273 |     }
  274 |     expect(result.tested, `sitemap sampled no URLs; errors: ${result.errors.join(', ')}`).toBeGreaterThan(0);
  275 | 
  276 |     console.log(`Tested ${result.tested} URLs from offices sitemap`);
  277 |     expect(result.errors, `Errors: ${result.errors.join(', ')}`).toHaveLength(0);
  278 |   });
  279 | 
  280 |   test('districts sitemap - sample URLs resolve', async ({ page }) => {
  281 |     await page.goto('/');
  282 |     const baseUrl = new URL(page.url()).origin;
  283 | 
  284 |     const result = await testSitemapUrls(page, baseUrl, '/districts/sitemap-districts.xml');
  285 | 
  286 |     if (result.tested === 0 && !STRICT) {
  287 |       test.skip();
  288 |       return;
  289 |     }
  290 |     expect(result.tested, `sitemap sampled no URLs; errors: ${result.errors.join(', ')}`).toBeGreaterThan(0);
  291 | 
  292 |     console.log(`Tested ${result.tested} URLs from districts sitemap`);
  293 |     expect(result.errors, `Errors: ${result.errors.join(', ')}`).toHaveLength(0);
  294 |   });
  295 | 
  296 |   test('states sitemap - sample URLs resolve', async ({ page }) => {
  297 |     await page.goto('/');
  298 |     const baseUrl = new URL(page.url()).origin;
  299 | 
  300 |     const result = await testSitemapUrls(page, baseUrl, '/states/sitemap-states.xml');
  301 | 
  302 |     if (result.tested === 0 && !STRICT) {
  303 |       test.skip();
  304 |       return;
  305 |     }
  306 |     expect(result.tested, `sitemap sampled no URLs; errors: ${result.errors.join(', ')}`).toBeGreaterThan(0);
  307 | 
  308 |     console.log(`Tested ${result.tested} URLs from states sitemap`);
  309 |     expect(result.errors, `Errors: ${result.errors.join(', ')}`).toHaveLength(0);
  310 |   });
  311 | 
  312 |   test('elections sitemap - sample URLs resolve', async ({ page }) => {
  313 |     await page.goto('/');
  314 |     const baseUrl = new URL(page.url()).origin;
  315 | 
  316 |     const result = await testSitemapUrls(page, baseUrl, '/elections/sitemap-elections.xml');
  317 | 
  318 |     if (result.tested === 0 && !STRICT) {
  319 |       test.skip();
  320 |       return;
  321 |     }
  322 |     expect(result.tested, `sitemap sampled no URLs; errors: ${result.errors.join(', ')}`).toBeGreaterThan(0);
  323 | 
  324 |     console.log(`Tested ${result.tested} URLs from elections sitemap`);
  325 |     expect(result.errors, `Errors: ${result.errors.join(', ')}`).toHaveLength(0);
  326 |   });
  327 | 
  328 | });
  329 | 
```