# 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/news-feed.spec.ts >> news feed (source: OPENSEARCH) >> candidate chips carry an FEC party code, never a graph id
- Location: tests/ui/news-feed.spec.ts:47:7

# Error details

```
Error: candidate party must be an FEC code (REP, DEM, ...)

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

- Expected  -  1
+ Received  + 27

- Array []
+ Array [
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "BIDEN, JOSEPH R JR (ffd8f1ef-a47c-ad4f-d23a-b4bbc0b92fd3) party=12cac40d-b40d-abcc-6735-5e49f3925192",
+   "SPANBERGER, ABIGAIL (f18e9c98-017d-5a98-bed8-f33386901a9d) party=12cac40d-b40d-abcc-6735-5e49f3925192",
+   "SPANBERGER, ABIGAIL (f18e9c98-017d-5a98-bed8-f33386901a9d) party=12cac40d-b40d-abcc-6735-5e49f3925192",
+   "SPANBERGER, ABIGAIL (f18e9c98-017d-5a98-bed8-f33386901a9d) party=12cac40d-b40d-abcc-6735-5e49f3925192",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "ARENHOLZ, ASHLEY HINSON (f3af873b-039f-98a4-0b75-1568ca5d4984) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "ANDREWS, ANNIE DR. (b0d6af45-6be8-bbaa-a736-36c964f4f179) party=12cac40d-b40d-abcc-6735-5e49f3925192",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "BUSH, GEORGE W (8eaa6bab-c1a3-3687-7e4c-c1ff0dde4553) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "ANDREWS, ANNIE DR. (b0d6af45-6be8-bbaa-a736-36c964f4f179) party=12cac40d-b40d-abcc-6735-5e49f3925192",
+   "ARENHOLZ, ASHLEY HINSON (f3af873b-039f-98a4-0b75-1568ca5d4984) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+   "SPANBERGER, ABIGAIL (f18e9c98-017d-5a98-bed8-f33386901a9d) party=12cac40d-b40d-abcc-6735-5e49f3925192",
+   "TRUMP, DONALD J. (10e488e1-4d9d-19d9-c96e-869dd0c45757) party=78a61b65-dba1-0686-97bb-bdde7e1944a9",
+ ]
```

# Test source

```ts
  1  | import { test, expect } from '@playwright/test';
  2  | import { gql } from './helpers';
  3  | 
  4  | /*
  5  |  * us.elect.news feed contract, through the target's /api/graphql (api-graphql
  6  |  * serves the news schema too). Production runs FEED_SOURCE=opensearch; preview
  7  |  * doesn't set it, so every query asks for source: OPENSEARCH explicitly and
  8  |  * exercises the production path against the preview graph.
  9  |  *
  10 |  * Chip colours, party facets and the party filter all key on the FEC party code.
  11 |  * On the rebuilt graph Party.id is a UUID5, and before sites#164 the feed handed
  12 |  * out those UUIDs: every chip lost its colour and the REP/DEM facets broke
  13 |  * (electinfo/ops#514). Party codes must stay codes on either graph.
  14 |  */
  15 | 
  16 | const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
  17 | const PARTY_CODE = /^[A-Z0-9]{1,4}$/;
  18 | 
  19 | type Feed = {
  20 |   newsFeed: {
  21 |     totalArticles: number | null;
  22 |     items: {
  23 |       article: { url: string };
  24 |       resolvedEntities: { uuid: string; name: string; labels: string[]; party: string | null }[] | null;
  25 |     }[];
  26 |     facets: { canonicalParties: { key: string; count: number }[] } | null;
  27 |   };
  28 | };
  29 | 
  30 | const FEED = `query($party: String) {
  31 |   newsFeed(pageSize: 30, source: OPENSEARCH, canonical: { party: $party }) {
  32 |     totalArticles
  33 |     items { article { url } resolvedEntities { uuid name labels party } }
  34 |     facets { canonicalParties { key count } }
  35 |   }
  36 | }`;
  37 | 
  38 | test.describe('news feed (source: OPENSEARCH)', () => {
  39 |   test('the feed serves articles with resolved entities', async ({ request }) => {
  40 |     const { newsFeed } = await gql<Feed>(request, FEED);
  41 |     expect(newsFeed.totalArticles ?? 0, 'roster index should hold articles (check _cat/aliases/gdelt-roster-us-politics)').toBeGreaterThan(0);
  42 |     expect(newsFeed.items.length, 'first page should list articles').toBeGreaterThan(0);
  43 |     const resolved = newsFeed.items.flatMap(i => i.resolvedEntities ?? []);
  44 |     expect(resolved.length, 'first page should resolve some roster entities').toBeGreaterThan(0);
  45 |   });
  46 | 
  47 |   test('candidate chips carry an FEC party code, never a graph id', async ({ request }) => {
  48 |     const { newsFeed } = await gql<Feed>(request, FEED);
  49 |     const candidates = newsFeed.items
  50 |       .flatMap(i => i.resolvedEntities ?? [])
  51 |       .filter(e => e.labels.includes('Candidate'));
  52 |     expect(candidates.length, 'first page should resolve candidates').toBeGreaterThan(0);
  53 |     const bad = candidates.filter(e => e.party !== null && !PARTY_CODE.test(e.party));
  54 |     expect(
  55 |       bad.map(e => `${e.name} (${e.uuid}) party=${e.party}`),
  56 |       'candidate party must be an FEC code (REP, DEM, ...)',
> 57 |     ).toEqual([]);
     |       ^ Error: candidate party must be an FEC code (REP, DEM, ...)
  58 |     expect(candidates.some(e => e.party === 'REP' || e.party === 'DEM'), 'some candidates should be REP or DEM').toBe(true);
  59 |   });
  60 | 
  61 |   test('party facet keys are FEC codes and the party filter matches', async ({ request }) => {
  62 |     const { newsFeed } = await gql<Feed>(request, FEED);
  63 |     const keys = (newsFeed.facets?.canonicalParties ?? []).map(b => b.key);
  64 |     expect(keys.length, 'party facet should have buckets').toBeGreaterThan(0);
  65 |     expect(keys.filter(k => UUID.test(k)), 'party facet keys must not be graph UUIDs').toEqual([]);
  66 |     expect(keys, 'party facet should offer REP').toContain('REP');
  67 | 
  68 |     const rep = await gql<Feed>(request, FEED, { party: 'REP' });
  69 |     expect(rep.newsFeed.items.length, 'canonical.party=REP should match articles').toBeGreaterThan(0);
  70 |   });
  71 | });
  72 | 
```