/ clis / bloomberg / utils.test.js
utils.test.js
  1  import { describe, expect, it } from 'vitest';
  2  import { extractStoryMediaLinks, parseBloombergRss, renderStoryBody } from './utils.js';
  3  describe('Bloomberg utils', () => {
  4      it('parses Bloomberg RSS items with summary, link, and deduped media links', () => {
  5          const xml = `<?xml version="1.0" encoding="UTF-8"?>
  6        <rss xmlns:media="http://search.yahoo.com/mrss/">
  7          <channel>
  8            <item>
  9              <title><![CDATA[Headline One]]></title>
 10              <description><![CDATA[Summary <b>One</b> &amp; more]]></description>
 11              <link>https://www.bloomberg.com/news/articles/2026-03-19/example-one</link>
 12              <media:content url="https://assets.bwbx.io/example-one.jpg" type="image/jpeg">
 13                <media:thumbnail url="https://assets.bwbx.io/example-one.jpg" />
 14              </media:content>
 15            </item>
 16            <item>
 17              <title>Headline Two</title>
 18              <description>Summary Two</description>
 19              <guid isPermaLink="true">https://www.bloomberg.com/news/articles/2026-03-19/example-two</guid>
 20              <enclosure url="https://assets.bwbx.io/example-two.png" type="image/png" />
 21            </item>
 22          </channel>
 23        </rss>`;
 24          const items = parseBloombergRss(xml);
 25          expect(items).toHaveLength(2);
 26          expect(items[0]).toEqual({
 27              title: 'Headline One',
 28              summary: 'Summary One & more',
 29              link: 'https://www.bloomberg.com/news/articles/2026-03-19/example-one',
 30              mediaLinks: ['https://assets.bwbx.io/example-one.jpg'],
 31          });
 32          expect(items[1]).toEqual({
 33              title: 'Headline Two',
 34              summary: 'Summary Two',
 35              link: 'https://www.bloomberg.com/news/articles/2026-03-19/example-two',
 36              mediaLinks: ['https://assets.bwbx.io/example-two.png'],
 37          });
 38      });
 39      it('renders Bloomberg story rich-text body into readable text', () => {
 40          const body = {
 41              type: 'document',
 42              content: [
 43                  { type: 'inline-newsletter', data: { position: 'top' }, content: [] },
 44                  {
 45                      type: 'paragraph',
 46                      data: {},
 47                      content: [
 48                          { type: 'text', value: 'Lead paragraph with ' },
 49                          { type: 'entity', content: [{ type: 'text', value: 'linked text' }] },
 50                          { type: 'text', value: '.' },
 51                      ],
 52                  },
 53                  {
 54                      type: 'heading',
 55                      data: { level: 2 },
 56                      content: [{ type: 'text', value: 'Key Points' }],
 57                  },
 58                  {
 59                      type: 'list',
 60                      data: { style: 'unordered' },
 61                      content: [
 62                          {
 63                              type: 'list-item',
 64                              content: [
 65                                  { type: 'paragraph', content: [{ type: 'text', value: 'Point one' }] },
 66                              ],
 67                          },
 68                          {
 69                              type: 'list-item',
 70                              content: [
 71                                  { type: 'paragraph', content: [{ type: 'text', value: 'Point two' }] },
 72                              ],
 73                          },
 74                      ],
 75                  },
 76                  {
 77                      type: 'blockquote',
 78                      content: [{ type: 'text', value: 'Quoted line' }],
 79                  },
 80                  {
 81                      type: 'media',
 82                      data: {
 83                          attachment: {
 84                              caption: '<p>Chart caption</p>',
 85                          },
 86                      },
 87                  },
 88                  { type: 'ad', data: { num: 1 }, content: [] },
 89              ],
 90          };
 91          expect(renderStoryBody(body)).toBe([
 92              'Lead paragraph with linked text.',
 93              '## Key Points',
 94              '- Point one\n- Point two',
 95              '> Quoted line',
 96              'Chart caption',
 97          ].join('\n\n'));
 98      });
 99      it('collects deduped story media links from lede, attachments, and body media', () => {
100          const story = {
101              ledeImageUrl: 'https://assets.bwbx.io/lede.webp',
102              lede: { url: 'https://assets.bwbx.io/lede.webp' },
103              socialImageUrl: 'https://assets.bwbx.io/social.png',
104              imageAttachments: {
105                  one: { url: 'https://assets.bwbx.io/figure.jpg' },
106              },
107              body: {
108                  content: [
109                      {
110                          type: 'media',
111                          data: {
112                              chart: {
113                                  src: 'https://resource.bloomberg.com/images/chart.png',
114                                  fallback: 'https://assets.bwbx.io/chart-fallback.png',
115                              },
116                          },
117                      },
118                  ],
119              },
120          };
121          expect(extractStoryMediaLinks(story)).toEqual([
122              'https://assets.bwbx.io/lede.webp',
123              'https://assets.bwbx.io/social.png',
124              'https://assets.bwbx.io/figure.jpg',
125              'https://resource.bloomberg.com/images/chart.png',
126              'https://assets.bwbx.io/chart-fallback.png',
127          ]);
128      });
129  });