# 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/routing.spec.ts >> routing: every index link resolves >> /vendors/ first-page links all answer 200 or 301
- Location: tests/ui/routing.spec.ts:84:9

# Error details

```
Error: /vendors/ links that do not resolve

expect(received).toEqual(expected) // deep equality

- Expected  -  1
+ Received  + 52

- Array []
+ Array [
+   "/vendors/8fa5282f-53a0-1b1b-f1c2-3028776229e5/ -> 500",
+   "/vendors/10a528f4-b336-2b2b-f368-ace576e29ebf/ -> 500",
+   "/vendors/1aa9d0ff-f8e3-8e48-4ba1-6c40961cff3f/ -> 500",
+   "/vendors/045e0715-8fe2-027d-1bd9-eae91903bf42/ -> 500",
+   "/vendors/a2e9cfea-dee0-2824-46f2-843916cd2199/ -> 500",
+   "/vendors/b4933d51-1079-e820-025f-362e332d013f/ -> 500",
+   "/vendors/3c2e1227-6b93-51d3-0f82-3b287d9cbd68/ -> 500",
+   "/vendors/f3b687a0-9e83-0a38-aace-17e96bf3ff4c/ -> 500",
+   "/vendors/78955348-3768-153b-5317-7b18d170da75/ -> 500",
+   "/vendors/e3d3e242-8b89-e544-794b-956b836d57be/ -> 500",
+   "/vendors/c2aa6986-f2b0-d693-fbd0-8e46276347e1/ -> 500",
+   "/vendors/135eef71-360a-364f-ea1c-f0b96b74df60/ -> 500",
+   "/vendors/b531f4c6-ba4f-c56a-02f5-4b7c190d3282/ -> 500",
+   "/vendors/6dc0be80-5ae7-7fe6-ccbb-757575335553/ -> 500",
+   "/vendors/c62f0a99-452d-9689-0639-2f17ef8ddba6/ -> 500",
+   "/vendors/24bc4dec-21e7-5c99-2042-c4733eac63a5/ -> 500",
+   "/vendors/d5e82d61-20a9-e294-4d2c-b35f528ff6b1/ -> 500",
+   "/vendors/02a10db1-cde1-8428-aff9-6969dfc90764/ -> 500",
+   "/vendors/829a17f1-9f36-cb6b-1bdf-4747243e2418/ -> 500",
+   "/vendors/0966d116-aea0-c9b1-d66a-621dacb4b87e/ -> 500",
+   "/vendors/e5dedb0e-71e7-0435-44aa-4eaf805416f9/ -> 500",
+   "/vendors/de27493b-4beb-eb34-5c69-68a7d0a815d6/ -> 500",
+   "/vendors/cfe1d84d-f810-a058-76cb-4d754e790de6/ -> 500",
+   "/vendors/5ad8168e-37f2-65ce-9a19-e9d40554faa3/ -> 500",
+   "/vendors/5e21015e-5ec8-7406-ca68-1a8ebbe52e6f/ -> 500",
+   "/vendors/0525c95e-c55b-7a75-add2-ad66ce318a8c/ -> 500",
+   "/vendors/aeadfacb-6c78-0cfd-42f6-d42a0d53c186/ -> 500",
+   "/vendors/11467d27-0924-3cfb-0c75-86672c4eb902/ -> 500",
+   "/vendors/e535d69e-d726-9c92-4f9c-9db1aceee72d/ -> 500",
+   "/vendors/8d9c4f7e-d582-3e20-7e4d-3c10f8785bc7/ -> 500",
+   "/vendors/8ec677a3-244f-53d6-7a4d-4acb19dfdc5f/ -> 500",
+   "/vendors/3a3ab30c-8a65-ae9d-d7e5-ab7b71005eb5/ -> 500",
+   "/vendors/5b2db050-06fd-91da-2f76-93088bddc33d/ -> 500",
+   "/vendors/17f75a62-baae-88f1-c646-8d5b89aeb859/ -> 500",
+   "/vendors/ef66ded4-ed81-2a16-6b4f-e8c65353e11d/ -> 500",
+   "/vendors/caeec7a9-f1bc-902c-002c-f15d4c79bd52/ -> 500",
+   "/vendors/a78e71f8-ec98-201b-86f8-f1aae5af5dd5/ -> 500",
+   "/vendors/ccec749b-c26a-7049-775d-e9f61abb04f5/ -> 500",
+   "/vendors/db1c3f7b-c99c-7721-1927-b530cbba72f7/ -> 500",
+   "/vendors/b5c81bba-745f-cde3-3d5b-e9017a1c199e/ -> 500",
+   "/vendors/eaceaffe-911a-6a53-3d02-502099af2ffc/ -> 500",
+   "/vendors/deada85f-03f9-e13e-e706-2046d02bc782/ -> 500",
+   "/vendors/f573ac78-e1ce-9be6-af97-f1e376e0bbff/ -> 500",
+   "/vendors/b33d44d5-31b6-4257-07a5-bbb6b76ca4c0/ -> 500",
+   "/vendors/08aef8fe-2d39-ea76-3688-1877c1d91866/ -> 500",
+   "/vendors/19e1d661-acc7-8fcc-b2ee-c1fa53f25c42/ -> 500",
+   "/vendors/f5ee1323-d683-b8d3-3080-2b393004cd0d/ -> 500",
+   "/vendors/0eefddb6-5fc7-9156-2618-4bffdbfb491b/ -> 500",
+   "/vendors/1ca80be8-3efa-8a24-aa8a-633f76bebd49/ -> 500",
+   "/vendors/ddc36f53-2814-b02e-5659-89ca531d22e8/ -> 500",
+ ]
```

# Page snapshot

```yaml
- generic [active] [ref=e1]:
  - link "← Back to Home" [ref=e2] [cursor=pointer]:
    - /url: /
  - banner [ref=e3]:
    - heading "Campaign Vendors" [level=1] [ref=e4]
    - paragraph [ref=e5]: Payees receiving campaign expenditures
  - generic [ref=e6]:
    - generic [ref=e7]:
      - textbox "Search vendors by name..." [ref=e8]
      - button "Search" [ref=e9] [cursor=pointer]
    - generic [ref=e10]:
      - heading "All Vendors" [level=3] [ref=e12]
      - generic [ref=e13]:
        - 'link "AMERICAN MADE MEDIA CONSULTANTS, LLC ARLINGTON, VA - $782.2M received Party Affinity – DEM: 33% REP: 67%" [ref=e14] [cursor=pointer]':
          - /url: /vendors/8fa5282f-53a0-1b1b-f1c2-3028776229e5/
          - generic [ref=e15]: AMERICAN MADE MEDIA CONSULTANTS, LLC
          - generic [ref=e16]: ARLINGTON, VA - $782.2M received
          - generic [ref=e17]: "Party Affinity – DEM: 33% REP: 67%"
        - link "JIM A NORRIS FOR THE UNITED STATES PRESIDENT FORT MYERS, FL - $777.0M received" [ref=e18] [cursor=pointer]:
          - /url: /vendors/10a528f4-b336-2b2b-f368-ace576e29ebf/
          - generic [ref=e19]: JIM A NORRIS FOR THE UNITED STATES PRESIDENT
          - generic [ref=e20]: FORT MYERS, FL - $777.0M received
        - 'link "MERCER, LEE L MR. JR. HOUSTON, AA - $704.0M received Party Affinity – DEM: 100% REP: 0%" [ref=e21] [cursor=pointer]':
          - /url: /vendors/1aa9d0ff-f8e3-8e48-4ba1-6c40961cff3f/
          - generic [ref=e22]: MERCER, LEE L MR. JR.
          - generic [ref=e23]: HOUSTON, AA - $704.0M received
          - generic [ref=e24]: "Party Affinity – DEM: 100% REP: 0%"
        - 'link "GMMB WASHINGTON, DC - $555.6M received Party Affinity – DEM: 74% REP: 26%" [ref=e25] [cursor=pointer]':
          - /url: /vendors/045e0715-8fe2-027d-1bd9-eae91903bf42/
          - generic [ref=e26]: GMMB
          - generic [ref=e27]: WASHINGTON, DC - $555.6M received
          - generic [ref=e28]: "Party Affinity – DEM: 74% REP: 26%"
        - 'link "TARGETED VICTORY ALEXANDRIA, VA - $524.4M received Party Affinity – DEM: 28% REP: 72%" [ref=e29] [cursor=pointer]':
          - /url: /vendors/a2e9cfea-dee0-2824-46f2-843916cd2199/
          - generic [ref=e30]: TARGETED VICTORY
          - generic [ref=e31]: ALEXANDRIA, VA - $524.4M received
          - generic [ref=e32]: "Party Affinity – DEM: 28% REP: 72%"
        - 'link "AMERICAN EXPRESS NEWARK, NJ - $518.4M received Party Affinity – DEM: 51% REP: 49%" [ref=e33] [cursor=pointer]':
          - /url: /vendors/b4933d51-1079-e820-025f-362e332d013f/
          - generic [ref=e34]: AMERICAN EXPRESS
          - generic [ref=e35]: NEWARK, NJ - $518.4M received
          - generic [ref=e36]: "Party Affinity – DEM: 51% REP: 49%"
        - 'link "ASSEMBLY NEW YORK, NY - $506.8M received Party Affinity – DEM: 96% REP: 4%" [ref=e37] [cursor=pointer]':
          - /url: /vendors/3c2e1227-6b93-51d3-0f82-3b287d9cbd68/
          - generic [ref=e38]: ASSEMBLY
          - generic [ref=e39]: NEW YORK, NY - $506.8M received
          - generic [ref=e40]: "Party Affinity – DEM: 96% REP: 4%"
        - 'link "ACTBLUE TECHNICAL SERVICES CAMBRIDGE, MA - $466.2M received Party Affinity – DEM: 76% REP: 24%" [ref=e41] [cursor=pointer]':
          - /url: /vendors/f3b687a0-9e83-0a38-aace-17e96bf3ff4c/
          - generic [ref=e42]: ACTBLUE TECHNICAL SERVICES
          - generic [ref=e43]: CAMBRIDGE, MA - $466.2M received
          - generic [ref=e44]: "Party Affinity – DEM: 76% REP: 24%"
        - 'link "MEDIA BUYING & ANALYTICS ATLANTA, GA - $443.0M received Party Affinity – DEM: 69% REP: 31%" [ref=e45] [cursor=pointer]':
          - /url: /vendors/78955348-3768-153b-5317-7b18d170da75/
          - generic [ref=e46]: MEDIA BUYING & ANALYTICS
          - generic [ref=e47]: ATLANTA, GA - $443.0M received
          - generic [ref=e48]: "Party Affinity – DEM: 69% REP: 31%"
        - 'link "SCREEN STRATEGIES MEDIA FAIRFAX, VA - $441.3M received Party Affinity – DEM: 73% REP: 27%" [ref=e49] [cursor=pointer]':
          - /url: /vendors/e3d3e242-8b89-e544-794b-956b836d57be/
          - generic [ref=e50]: SCREEN STRATEGIES MEDIA
          - generic [ref=e51]: FAIRFAX, VA - $441.3M received
          - generic [ref=e52]: "Party Affinity – DEM: 73% REP: 27%"
        - 'link "GMMB INC. WASHINGTON, DC - $429.9M received Party Affinity – DEM: 67% REP: 33%" [ref=e53] [cursor=pointer]':
          - /url: /vendors/c2aa6986-f2b0-d693-fbd0-8e46276347e1/
          - generic [ref=e54]: GMMB INC.
          - generic [ref=e55]: WASHINGTON, DC - $429.9M received
          - generic [ref=e56]: "Party Affinity – DEM: 67% REP: 33%"
        - 'link "BULLY PULPIT INTERACTIVE LLC WASHINGTON, DC - $427.2M received Party Affinity – DEM: 70% REP: 30%" [ref=e57] [cursor=pointer]':
          - /url: /vendors/135eef71-360a-364f-ea1c-f0b96b74df60/
          - generic [ref=e58]: BULLY PULPIT INTERACTIVE LLC
          - generic [ref=e59]: WASHINGTON, DC - $427.2M received
          - generic [ref=e60]: "Party Affinity – DEM: 70% REP: 30%"
        - 'link "BUYING TIME, LLC WASHINGTON, DC - $378.5M received Party Affinity – DEM: 77% REP: 23%" [ref=e61] [cursor=pointer]':
          - /url: /vendors/b531f4c6-ba4f-c56a-02f5-4b7c190d3282/
          - generic [ref=e62]: BUYING TIME, LLC
          - generic [ref=e63]: WASHINGTON, DC - $378.5M received
          - generic [ref=e64]: "Party Affinity – DEM: 77% REP: 23%"
        - 'link "MEDIA BUYING & ANALYTICS LLC ATLANTA, GA - $340.5M received Party Affinity – DEM: 70% REP: 30%" [ref=e65] [cursor=pointer]':
          - /url: /vendors/6dc0be80-5ae7-7fe6-ccbb-757575335553/
          - generic [ref=e66]: MEDIA BUYING & ANALYTICS LLC
          - generic [ref=e67]: ATLANTA, GA - $340.5M received
          - generic [ref=e68]: "Party Affinity – DEM: 70% REP: 30%"
        - 'link "BUYING TIME LLC WASHINGTON, DC - $317.9M received Party Affinity – DEM: 87% REP: 13%" [ref=e69] [cursor=pointer]':
          - /url: /vendors/c62f0a99-452d-9689-0639-2f17ef8ddba6/
          - generic [ref=e70]: BUYING TIME LLC
          - generic [ref=e71]: WASHINGTON, DC - $317.9M received
          - generic [ref=e72]: "Party Affinity – DEM: 87% REP: 13%"
        - 'link "GMMB INC WASHINGTON, DC - $317.5M received Party Affinity – DEM: 72% REP: 28%" [ref=e73] [cursor=pointer]':
          - /url: /vendors/24bc4dec-21e7-5c99-2042-c4733eac63a5/
          - generic [ref=e74]: GMMB INC
          - generic [ref=e75]: WASHINGTON, DC - $317.5M received
          - generic [ref=e76]: "Party Affinity – DEM: 72% REP: 28%"
        - 'link "MOTHERSHIP STRATEGIES WASHINGTON, DC - $300.8M received Party Affinity – DEM: 75% REP: 25%" [ref=e77] [cursor=pointer]':
          - /url: /vendors/d5e82d61-20a9-e294-4d2c-b35f528ff6b1/
          - generic [ref=e78]: MOTHERSHIP STRATEGIES
          - generic [ref=e79]: WASHINGTON, DC - $300.8M received
          - generic [ref=e80]: "Party Affinity – DEM: 75% REP: 25%"
        - 'link "GMMB INC. BOSTON, MA - $294.4M received Party Affinity – DEM: 66% REP: 34%" [ref=e81] [cursor=pointer]':
          - /url: /vendors/02a10db1-cde1-8428-aff9-6969dfc90764/
          - generic [ref=e82]: GMMB INC.
          - generic [ref=e83]: BOSTON, MA - $294.4M received
          - generic [ref=e84]: "Party Affinity – DEM: 66% REP: 34%"
        - 'link "AMERICAN EXPRESS FT LAUDERDALE, FL - $272.2M received Party Affinity – DEM: 64% REP: 36%" [ref=e85] [cursor=pointer]':
          - /url: /vendors/829a17f1-9f36-cb6b-1bdf-4747243e2418/
          - generic [ref=e86]: AMERICAN EXPRESS
          - generic [ref=e87]: FT LAUDERDALE, FL - $272.2M received
          - generic [ref=e88]: "Party Affinity – DEM: 64% REP: 36%"
        - 'link "TOTAL ITEMIZED ABOVE: $265.2M received Party Affinity – DEM: 68% REP: 32%" [ref=e89] [cursor=pointer]':
          - /url: /vendors/0966d116-aea0-c9b1-d66a-621dacb4b87e/
          - generic [ref=e90]: "TOTAL ITEMIZED ABOVE:"
          - generic [ref=e91]: $265.2M received
          - generic [ref=e92]: "Party Affinity – DEM: 68% REP: 32%"
        - 'link "STRATEGIC MEDIA SERVICES, INC. ARLINGTON, VA - $257.4M received Party Affinity – DEM: 37% REP: 63%" [ref=e93] [cursor=pointer]':
          - /url: /vendors/e5dedb0e-71e7-0435-44aa-4eaf805416f9/
          - generic [ref=e94]: STRATEGIC MEDIA SERVICES, INC.
          - generic [ref=e95]: ARLINGTON, VA - $257.4M received
          - generic [ref=e96]: "Party Affinity – DEM: 37% REP: 63%"
        - 'link "AMERICAN EXPRESS NEW YORK, NY - $239.6M received Party Affinity – DEM: 62% REP: 38%" [ref=e97] [cursor=pointer]':
          - /url: /vendors/de27493b-4beb-eb34-5c69-68a7d0a815d6/
          - generic [ref=e98]: AMERICAN EXPRESS
          - generic [ref=e99]: NEW YORK, NY - $239.6M received
          - generic [ref=e100]: "Party Affinity – DEM: 62% REP: 38%"
        - 'link "AMERICAN RAMBLER PRODUCTIONS LLC BEVERLY, MA - $233.6M received Party Affinity – DEM: 34% REP: 66%" [ref=e101] [cursor=pointer]':
          - /url: /vendors/cfe1d84d-f810-a058-76cb-4d754e790de6/
          - generic [ref=e102]: AMERICAN RAMBLER PRODUCTIONS LLC
          - generic [ref=e103]: BEVERLY, MA - $233.6M received
          - generic [ref=e104]: "Party Affinity – DEM: 34% REP: 66%"
        - 'link "TARGETED VICTORY LLC ALEXANDRIA, VA - $223.9M received Party Affinity – DEM: 28% REP: 72%" [ref=e105] [cursor=pointer]':
          - /url: /vendors/5ad8168e-37f2-65ce-9a19-e9d40554faa3/
          - generic [ref=e106]: TARGETED VICTORY LLC
          - generic [ref=e107]: ALEXANDRIA, VA - $223.9M received
          - generic [ref=e108]: "Party Affinity – DEM: 28% REP: 72%"
        - 'link "GUSTO SAN FRANCISCO, CA - $220.0M received Party Affinity – DEM: 76% REP: 24%" [ref=e109] [cursor=pointer]':
          - /url: /vendors/5e21015e-5ec8-7406-ca68-1a8ebbe52e6f/
          - generic [ref=e110]: GUSTO
          - generic [ref=e111]: SAN FRANCISCO, CA - $220.0M received
          - generic [ref=e112]: "Party Affinity – DEM: 76% REP: 24%"
        - 'link "WINRED TECHNICAL SERVICES LLC ARLINGTON, VA - $217.3M received Party Affinity – DEM: 25% REP: 75%" [ref=e113] [cursor=pointer]':
          - /url: /vendors/0525c95e-c55b-7a75-add2-ad66ce318a8c/
          - generic [ref=e114]: WINRED TECHNICAL SERVICES LLC
          - generic [ref=e115]: ARLINGTON, VA - $217.3M received
          - generic [ref=e116]: "Party Affinity – DEM: 25% REP: 75%"
        - 'link "GRASSROOTS MEDIA BALA CYNWYD, PA - $213.1M received Party Affinity – DEM: 69% REP: 31%" [ref=e117] [cursor=pointer]':
          - /url: /vendors/aeadfacb-6c78-0cfd-42f6-d42a0d53c186/
          - generic [ref=e118]: GRASSROOTS MEDIA
          - generic [ref=e119]: BALA CYNWYD, PA - $213.1M received
          - generic [ref=e120]: "Party Affinity – DEM: 69% REP: 31%"
        - 'link "SUBTOTAL: $211.5M received Party Affinity – DEM: 68% REP: 32%" [ref=e121] [cursor=pointer]':
          - /url: /vendors/11467d27-0924-3cfb-0c75-86672c4eb902/
          - generic [ref=e122]: "SUBTOTAL:"
          - generic [ref=e123]: $211.5M received
          - generic [ref=e124]: "Party Affinity – DEM: 68% REP: 32%"
        - 'link "PAYCHEX PANORAMA, NY - $201.1M received Party Affinity – DEM: 76% REP: 24%" [ref=e125] [cursor=pointer]':
          - /url: /vendors/e535d69e-d726-9c92-4f9c-9db1aceee72d/
          - generic [ref=e126]: PAYCHEX
          - generic [ref=e127]: PANORAMA, NY - $201.1M received
          - generic [ref=e128]: "Party Affinity – DEM: 76% REP: 24%"
        - link "MERCER FOR PRESIDENT HOUSTON, AA - $192.0M received" [ref=e129] [cursor=pointer]:
          - /url: /vendors/8d9c4f7e-d582-3e20-7e4d-3c10f8785bc7/
          - generic [ref=e130]: MERCER FOR PRESIDENT
          - generic [ref=e131]: HOUSTON, AA - $192.0M received
        - 'link "MERCER, LEE L MR. JR. HOUSTON, TX - $192.0M received Party Affinity – DEM: 100% REP: 0%" [ref=e132] [cursor=pointer]':
          - /url: /vendors/8ec677a3-244f-53d6-7a4d-4acb19dfdc5f/
          - generic [ref=e133]: MERCER, LEE L MR. JR.
          - generic [ref=e134]: HOUSTON, TX - $192.0M received
          - generic [ref=e135]: "Party Affinity – DEM: 100% REP: 0%"
        - 'link "RWT PRODUCTION LLC SPRINGFIELD, VA - $178.1M received Party Affinity – DEM: 76% REP: 24%" [ref=e136] [cursor=pointer]':
          - /url: /vendors/3a3ab30c-8a65-ae9d-d7e5-ab7b71005eb5/
          - generic [ref=e137]: RWT PRODUCTION LLC
          - generic [ref=e138]: SPRINGFIELD, VA - $178.1M received
          - generic [ref=e139]: "Party Affinity – DEM: 76% REP: 24%"
        - 'link "CANAL PARTNERS MEDIA MARIETTA, GA - $175.0M received Party Affinity – DEM: 81% REP: 19%" [ref=e140] [cursor=pointer]':
          - /url: /vendors/5b2db050-06fd-91da-2f76-93088bddc33d/
          - generic [ref=e141]: CANAL PARTNERS MEDIA
          - generic [ref=e142]: MARIETTA, GA - $175.0M received
          - generic [ref=e143]: "Party Affinity – DEM: 81% REP: 19%"
        - 'link "LEFT HOOK SANTA MONICA, CA - $168.2M received Party Affinity – DEM: 76% REP: 24%" [ref=e144] [cursor=pointer]':
          - /url: /vendors/17f75a62-baae-88f1-c646-8d5b89aeb859/
          - generic [ref=e145]: LEFT HOOK
          - generic [ref=e146]: SANTA MONICA, CA - $168.2M received
          - generic [ref=e147]: "Party Affinity – DEM: 76% REP: 24%"
        - 'link "COMMUNICATIONS CORPORATION OF AMERICA BOSTON, VA - $162.5M received Party Affinity – DEM: 20% REP: 80%" [ref=e148] [cursor=pointer]':
          - /url: /vendors/ef66ded4-ed81-2a16-6b4f-e8c65353e11d/
          - generic [ref=e149]: COMMUNICATIONS CORPORATION OF AMERICA
          - generic [ref=e150]: BOSTON, VA - $162.5M received
          - generic [ref=e151]: "Party Affinity – DEM: 20% REP: 80%"
        - 'link "PAYROLL DATA PROCESSING TAMPA, FL - $162.3M received Party Affinity – DEM: 73% REP: 27%" [ref=e152] [cursor=pointer]':
          - /url: /vendors/caeec7a9-f1bc-902c-002c-f15d4c79bd52/
          - generic [ref=e153]: PAYROLL DATA PROCESSING
          - generic [ref=e154]: TAMPA, FL - $162.3M received
          - generic [ref=e155]: "Party Affinity – DEM: 73% REP: 27%"
        - 'link "GOOGLE LLC MOUNTAIN VIEW, CA - $155.6M received Party Affinity – DEM: 66% REP: 34%" [ref=e156] [cursor=pointer]':
          - /url: /vendors/a78e71f8-ec98-201b-86f8-f1aae5af5dd5/
          - generic [ref=e157]: GOOGLE LLC
          - generic [ref=e158]: MOUNTAIN VIEW, CA - $155.6M received
          - generic [ref=e159]: "Party Affinity – DEM: 66% REP: 34%"
        - 'link "GRASSROOTS MEDIA, LLC BALA CYNWYD, PA - $153.2M received Party Affinity – DEM: 68% REP: 32%" [ref=e160] [cursor=pointer]':
          - /url: /vendors/ccec749b-c26a-7049-775d-e9f61abb04f5/
          - generic [ref=e161]: GRASSROOTS MEDIA, LLC
          - generic [ref=e162]: BALA CYNWYD, PA - $153.2M received
          - generic [ref=e163]: "Party Affinity – DEM: 68% REP: 32%"
        - 'link "MH MEDIA LLC BETHESDA, MD - $152.8M received Party Affinity – DEM: 33% REP: 67%" [ref=e164] [cursor=pointer]':
          - /url: /vendors/db1c3f7b-c99c-7721-1927-b530cbba72f7/
          - generic [ref=e165]: MH MEDIA LLC
          - generic [ref=e166]: BETHESDA, MD - $152.8M received
          - generic [ref=e167]: "Party Affinity – DEM: 33% REP: 67%"
        - 'link "FLS CONNECT LLC ST PAUL, MN - $152.5M received Party Affinity – DEM: 21% REP: 79%" [ref=e168] [cursor=pointer]':
          - /url: /vendors/b5c81bba-745f-cde3-3d5b-e9017a1c199e/
          - generic [ref=e169]: FLS CONNECT LLC
          - generic [ref=e170]: ST PAUL, MN - $152.5M received
          - generic [ref=e171]: "Party Affinity – DEM: 21% REP: 79%"
        - 'link "GAMBIT STRATEGIES LLC WASHINGTON, DC - $147.4M received Party Affinity – DEM: 70% REP: 30%" [ref=e172] [cursor=pointer]':
          - /url: /vendors/eaceaffe-911a-6a53-3d02-502099af2ffc/
          - generic [ref=e173]: GAMBIT STRATEGIES LLC
          - generic [ref=e174]: WASHINGTON, DC - $147.4M received
          - generic [ref=e175]: "Party Affinity – DEM: 70% REP: 30%"
        - 'link "RWT PRODUCTION, LLC ANNANDALE, VA - $143.2M received Party Affinity – DEM: 84% REP: 16%" [ref=e176] [cursor=pointer]':
          - /url: /vendors/deada85f-03f9-e13e-e706-2046d02bc782/
          - generic [ref=e177]: RWT PRODUCTION, LLC
          - generic [ref=e178]: ANNANDALE, VA - $143.2M received
          - generic [ref=e179]: "Party Affinity – DEM: 84% REP: 16%"
        - 'link "JAMESTOWN ASSOCIATES PRINCETON, NJ - $140.1M received Party Affinity – DEM: 25% REP: 75%" [ref=e180] [cursor=pointer]':
          - /url: /vendors/f573ac78-e1ce-9be6-af97-f1e376e0bbff/
          - generic [ref=e181]: JAMESTOWN ASSOCIATES
          - generic [ref=e182]: PRINCETON, NJ - $140.1M received
          - generic [ref=e183]: "Party Affinity – DEM: 25% REP: 75%"
        - 'link "INSPERITY KINGSWOOD, TX - $136.2M received Party Affinity – DEM: 22% REP: 78%" [ref=e184] [cursor=pointer]':
          - /url: /vendors/b33d44d5-31b6-4257-07a5-bbb6b76ca4c0/
          - generic [ref=e185]: INSPERITY
          - generic [ref=e186]: KINGSWOOD, TX - $136.2M received
          - generic [ref=e187]: "Party Affinity – DEM: 22% REP: 78%"
        - 'link "MAVERICK MEDIA, INC. AUSTIN, TX - $135.7M received Party Affinity – DEM: 38% REP: 62%" [ref=e188] [cursor=pointer]':
          - /url: /vendors/08aef8fe-2d39-ea76-3688-1877c1d91866/
          - generic [ref=e189]: MAVERICK MEDIA, INC.
          - generic [ref=e190]: AUSTIN, TX - $135.7M received
          - generic [ref=e191]: "Party Affinity – DEM: 38% REP: 62%"
        - 'link "NEXUS DIRECT VIRGINIA BEACH, VA - $135.5M received Party Affinity – DEM: 59% REP: 41%" [ref=e192] [cursor=pointer]':
          - /url: /vendors/19e1d661-acc7-8fcc-b2ee-c1fa53f25c42/
          - generic [ref=e193]: NEXUS DIRECT
          - generic [ref=e194]: VIRGINIA BEACH, VA - $135.5M received
          - generic [ref=e195]: "Party Affinity – DEM: 59% REP: 41%"
        - 'link "MENTZER MEDIA SERVICES, INC. TOWSON, MD - $133.5M received Party Affinity – DEM: 35% REP: 65%" [ref=e196] [cursor=pointer]':
          - /url: /vendors/f5ee1323-d683-b8d3-3080-2b393004cd0d/
          - generic [ref=e197]: MENTZER MEDIA SERVICES, INC.
          - generic [ref=e198]: TOWSON, MD - $133.5M received
          - generic [ref=e199]: "Party Affinity – DEM: 35% REP: 65%"
        - 'link "ONMESSAGE INC CROFTON, MD - $133.4M received Party Affinity – DEM: 30% REP: 70%" [ref=e200] [cursor=pointer]':
          - /url: /vendors/0eefddb6-5fc7-9156-2618-4bffdbfb491b/
          - generic [ref=e201]: ONMESSAGE INC
          - generic [ref=e202]: CROFTON, MD - $133.4M received
          - generic [ref=e203]: "Party Affinity – DEM: 30% REP: 70%"
        - 'link "FACEBOOK PALO ALTO, CA - $130.3M received Party Affinity – DEM: 71% REP: 29%" [ref=e204] [cursor=pointer]':
          - /url: /vendors/1ca80be8-3efa-8a24-aa8a-633f76bebd49/
          - generic [ref=e205]: FACEBOOK
          - generic [ref=e206]: PALO ALTO, CA - $130.3M received
          - generic [ref=e207]: "Party Affinity – DEM: 71% REP: 29%"
        - 'link "SMART MEDIA GROUP LLC ALEXANDRIA, VA - $129.5M received Party Affinity – DEM: 31% REP: 69%" [ref=e208] [cursor=pointer]':
          - /url: /vendors/ddc36f53-2814-b02e-5659-89ca531d22e8/
          - generic [ref=e209]: SMART MEDIA GROUP LLC
          - generic [ref=e210]: ALEXANDRIA, VA - $129.5M received
          - generic [ref=e211]: "Party Affinity – DEM: 31% REP: 69%"
      - generic [ref=e212]:
        - generic [ref=e213]: Showing 1-50
        - generic [ref=e214]:
          - button "First" [disabled] [ref=e215]
          - button "Prev" [disabled] [ref=e216]
          - button "1" [ref=e217] [cursor=pointer]
          - button "Next" [ref=e218] [cursor=pointer]
  - main [ref=e219]
```

# Test source

```ts
  1   | import { test, expect, APIRequestContext } from '@playwright/test';
  2   | import { canonicalPath, getRandomItems, toRelativePath } from './helpers';
  3   | 
  4   | /*
  5   |  * Routing coverage across every collection (electinfo/rundeck#700, ops#514).
  6   |  *
  7   |  * What slipped past the existing specs:
  8   |  *   - a /tracts/ catch-all location shadowed the alias resolver, so every tract
  9   |  *     URL answered 200 with the JS browse page and no rel=canonical (sites#161)
  10  |  *     -- nothing sampled tracts;
  11  |  *   - the preview nginx lacked the /politicians/ -> /candidates/ rewrite the
  12  |  *     production config has (sites#162) -- only 3 random index links were followed;
  13  |  *   - unknown paths fall through to the Payload slug lookup, which answers
  14  |  *     400/500 instead of 404.
  15  |  */
  16  | 
  17  | const ALIASES = process.env.EXPECT_ALIASES === '1';
  18  | 
  19  | /** Collections whose sitemap lists canonical URLs (rundeck PagePaths Sitemaps + CyclePages). */
  20  | const SITEMAP_COLLECTIONS = ['candidates', 'committees', 'parties', 'offices', 'districts', 'states', 'elections'];
  21  | 
  22  | /** Indexes whose first results page lists entity links (.entity-search-result). */
  23  | // (/states/ lists map chips and /districts/ lists .district-result behind a state
  24  | // filter; their sitemap tests cover them)
  25  | const LINKED_INDEXES = ['/candidates/', '/committees/', '/parties/', '/offices/', '/elections/', '/employers/', '/vendors/'];
  26  | 
  27  | const locs = (xml: string) => [...xml.matchAll(/<loc>([^<]+)<\/loc>/g)].map(m => m[1]);
  28  | 
  29  | /** Canonical page URLs from a collection's sitemap, following one level of sitemap index. */
  30  | async function sitemapLocs(request: APIRequestContext, collection: string): Promise<string[]> {
  31  |   const path = `/${collection}/sitemap-${collection}.xml`;
  32  |   const res = await request.get(path);
  33  |   expect(res.status(), `${path} should answer 200`).toBe(200);
  34  |   const xml = await res.text();
  35  |   if (!xml.includes('<sitemapindex')) return locs(xml);
  36  |   const [child] = getRandomItems(locs(xml), 1, `child:${path}`);
  37  |   const childPath = toRelativePath(child);
  38  |   const childRes = await request.get(childPath);
  39  |   expect(childRes.status(), `${childPath} should answer 200`).toBe(200);
  40  |   return locs(await childRes.text());
  41  | }
  42  | 
  43  | test.describe('routing: canonical URLs per collection', () => {
  44  |   test.skip(!ALIASES, 'Set EXPECT_ALIASES=1 against a target with published page paths');
  45  | 
  46  |   for (const collection of SITEMAP_COLLECTIONS) {
  47  |     test(`${collection}: sitemap URLs serve their page with rel=canonical = the sitemap URL`, async ({ request }) => {
  48  |       // Entity pages only: a sitemap may also list the collection's index (/elections/)
  49  |       const entityLocs = (await sitemapLocs(request, collection)).filter(l => toRelativePath(l) !== `/${collection}/`);
  50  |       const sample = getRandomItems(entityLocs, 3);
  51  |       expect(sample.length, `${collection} sitemap should list URLs`).toBeGreaterThan(0);
  52  |       for (const loc of sample) {
  53  |         const path = toRelativePath(loc);
  54  |         const res = await request.get(path, { maxRedirects: 0 });
  55  |         expect(res.status(), `${path} (from the ${collection} sitemap) should answer 200`).toBe(200);
  56  |         expect(canonicalPath(await res.text()), `${path} rel=canonical`).toBe(path);
  57  |       }
  58  |     });
  59  |   }
  60  | 
  61  |   test('tract URLs resolve through the alias resolver, not a catch-all', async ({ request }) => {
  62  |     // Regression for sites#161: Travis County tract 48453000101 by GEOID
  63  |     const res = await request.get('/tracts/48453000101/', { maxRedirects: 0 });
  64  |     expect([200, 301], '/tracts/48453000101/ should answer 200 or 301').toContain(res.status());
  65  |     if (res.status() === 200) {
  66  |       expect(canonicalPath(await res.text()), '/tracts/48453000101/ rel=canonical').toMatch(/^\/tracts\/[^/]+\/$/);
  67  |     }
  68  |   });
  69  | 
  70  |   test('party badge links by FEC code redirect to the party page', async ({ request }) => {
  71  |     // us.elect.news links parties as /parties/<code>/ (sites#164)
  72  |     for (const code of ['rep', 'dem']) {
  73  |       const res = await request.get(`/parties/${code}/`, { maxRedirects: 0 });
  74  |       expect(res.status(), `/parties/${code}/ should redirect permanently`).toBe(301);
  75  |       const target = res.headers()['location'] ?? '';
  76  |       const page = await request.get(target, { maxRedirects: 0 });
  77  |       expect(page.status(), `/parties/${code}/ -> ${target} should answer 200`).toBe(200);
  78  |     }
  79  |   });
  80  | });
  81  | 
  82  | test.describe('routing: every index link resolves', () => {
  83  |   for (const index of LINKED_INDEXES) {
  84  |     test(`${index} first-page links all answer 200 or 301`, async ({ page, request }) => {
  85  |       test.setTimeout(120000);
  86  |       const res = await page.goto(index);
  87  |       expect(res?.status(), `${index} should answer 200`).toBe(200);
  88  |       await expect(page.locator('.entity-search-result').first(), `${index} should list results`).toBeVisible({ timeout: 15000 });
  89  |       const hrefs = (await page.locator('.entity-search-result').evaluateAll(els => els.map(e => e.getAttribute('href'))))
  90  |         .filter((h): h is string => !!h)
  91  |         .map(toRelativePath);
  92  |       const broken: string[] = [];
  93  |       for (const href of [...new Set(hrefs)]) {
  94  |         const r = await request.get(href, { maxRedirects: 0 });
  95  |         if (r.status() !== 200 && r.status() !== 301) broken.push(`${href} -> ${r.status()}`);
  96  |       }
> 97  |       expect(broken, `${index} links that do not resolve`).toEqual([]);
      |                                                            ^ Error: /vendors/ links that do not resolve
  98  |     });
  99  |   }
  100 | });
  101 | 
  102 | test.describe('routing: unknown addresses', () => {
  103 |   test('an unknown entity path answers 404', async ({ request }) => {
  104 |     // Today the miss falls through to Payload /api/slug-lookup, which answers
  105 |     // 400/500. Remove test.fail once slug-lookup returns 404 (ops#514 follow-up).
  106 |     test.fail(true, 'Payload slug-lookup answers 400/500 for unknown paths');
  107 |     for (const path of ['/candidates/no-such-entity-zz/', '/tracts/no-such-tract/']) {
  108 |       const res = await request.get(path, { maxRedirects: 0 });
  109 |       expect(res.status(), `${path} should answer 404`).toBe(404);
  110 |     }
  111 |   });
  112 | });
  113 | 
```