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> & 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 });