# 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 >> candidates sitemap - sample URLs resolve
- Location: tests/ui/sitemap.spec.ts:210:7

# Error details

```
Error: Errors: /candidates/sitemap-candidates.xml lists 50975 URLs (limit 50000)

expect(received).toHaveLength(expected)

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

# Page snapshot

```yaml
- generic [active] [ref=f5e1]:
  - banner [ref=f5e2]:
    - heading "GOLDBERG, JACK MR" [level=1] [ref=f5e3]
    - paragraph [ref=f5e4]: Democratic Party candidate for U.S. Representative - New Jersey District 10
    - paragraph [ref=f5e5]: "FEC ID: e4cb80a6-2e83-177e-4e88-8addeba7c3f3"
  - generic [ref=f5e6]:
    - heading "Profile" [level=2] [ref=f5e7]
    - generic [ref=f5e8]:
      - term [ref=f5e9]: Party
      - definition [ref=f5e10]:
        - link "Democratic Party" [ref=f5e11] [cursor=pointer]:
          - /url: https://us.elect.info/parties/democratic/
      - term [ref=f5e12]: Office
      - definition [ref=f5e13]:
        - link "U.S. Representative - New Jersey District 10" [ref=f5e14] [cursor=pointer]:
          - /url: https://us.elect.info/offices/house/nj-10/
      - term [ref=f5e15]: District
      - definition [ref=f5e16]:
        - link "New Jersey District 10" [ref=f5e17] [cursor=pointer]:
          - /url: https://us.elect.info/districts/nj/10/
      - term [ref=f5e18]: Status
      - definition [ref=f5e19]: Not Yet a Candidate (Challenger)
      - term [ref=f5e20]: Home Location
      - definition [ref=f5e21]: WEST ORANGE, New Jersey 07052
  - generic [ref=f5e22]:
    - heading "Campaign Timeline" [level=2] [ref=f5e23]
    - generic [ref=f5e24]:
      - term [ref=f5e25]: Election Years
      - definition [ref=f5e26]:
        - link "2020" [ref=f5e28] [cursor=pointer]:
          - /url: https://us.elect.info/elections/2020/
      - term [ref=f5e29]: First Campaign
      - definition [ref=f5e30]: "2020"
      - term [ref=f5e31]: Most Recent
      - definition [ref=f5e32]: "2020"
  - generic [ref=f5e33]:
    - heading "Campaign Funding" [level=2] [ref=f5e34]
    - generic [ref=f5e35]:
      - term [ref=f5e36]: Total Received
      - definition [ref=f5e37]:
        - strong [ref=f5e38]: $0
      - term [ref=f5e39]: Contributing Committees
      - definition [ref=f5e40]: "0"
    - heading "Top Contributors" [level=3] [ref=f5e41]
  - generic [ref=f5e42]:
    - heading "Supporting Committees" [level=2] [ref=f5e43]
    - generic [ref=f5e44]:
      - term [ref=f5e45]: Principal Committee
      - definition [ref=f5e46]: Not designated
  - contentinfo [ref=f5e47]:
    - paragraph [ref=f5e48]:
      - link "elect.info" [ref=f5e49] [cursor=pointer]:
        - /url: https://us.elect.info/
      - text: "| Data from"
      - link "Federal Election Commission" [ref=f5e50] [cursor=pointer]:
        - /url: https://www.fec.gov/
      - text: "| Generated 2026-02-09"
```

# Test source

```ts
  126 | 
  127 |   if (status === 404 && !STRICT) {
  128 |     console.log(`${indent}Sitemap not found: ${sitemapPath} (skipping)`);
  129 |     return result;
  130 |   }
  131 | 
  132 |   expect(status, `Sitemap ${sitemapPath} should load`).toBe(200);
  133 | 
  134 |   const urls = parseUrlsFromSitemap(xml);
  135 | 
  136 |   if (urls.length === 0) {
  137 |     console.log(`${indent}No URLs found in ${sitemapPath}`);
  138 |     if (STRICT) result.errors.push(`${sitemapPath} lists no URLs`);
  139 |     return result;
  140 |   }
  141 | 
  142 |   result.errors.push(...checkSitemapProtocol(sitemapPath, xml, urls));
  143 | 
  144 |   console.log(`${indent}Found ${urls.length} URLs in ${sitemapPath}`);
  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);
      |                                                                  ^ Error: Errors: /candidates/sitemap-candidates.xml lists 50975 URLs (limit 50000)
  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);
  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 |   });
```