utils.test.js
1 import { describe, expect, it, vi } from 'vitest'; 2 import { extractJsonAssignmentFromHtml, extractSubscriptionChannel, prepareYoutubeApiPage } from './utils.js'; 3 describe('youtube utils', () => { 4 it('extractJsonAssignmentFromHtml parses bootstrap objects with nested braces in strings', () => { 5 const html = ` 6 <script> 7 var ytInitialPlayerResponse = { 8 "title": "brace { inside } string", 9 "nested": { "count": 2, "text": "quote \\"value\\"" } 10 }; 11 </script> 12 `; 13 expect(extractJsonAssignmentFromHtml(html, 'ytInitialPlayerResponse')).toEqual({ 14 title: 'brace { inside } string', 15 nested: { count: 2, text: 'quote "value"' }, 16 }); 17 }); 18 it('extractJsonAssignmentFromHtml supports window assignments', () => { 19 const html = ` 20 <script> 21 window["ytInitialData"] = {"contents":{"items":[1,2,3]}}; 22 </script> 23 `; 24 expect(extractJsonAssignmentFromHtml(html, 'ytInitialData')).toEqual({ 25 contents: { items: [1, 2, 3] }, 26 }); 27 }); 28 it('prepareYoutubeApiPage loads the quiet API bootstrap page', async () => { 29 const page = { 30 goto: vi.fn().mockResolvedValue(undefined), 31 wait: vi.fn().mockResolvedValue(undefined), 32 }; 33 await expect(prepareYoutubeApiPage(page)).resolves.toBeUndefined(); 34 expect(page.goto).toHaveBeenCalledWith('https://www.youtube.com', { waitUntil: 'none' }); 35 expect(page.wait).toHaveBeenCalledWith(2); 36 }); 37 it('extractSubscriptionChannel prefers explicit handle and subscriber count fields', () => { 38 expect(extractSubscriptionChannel({ 39 title: { simpleText: 'OpenAI' }, 40 channelHandleText: { runs: [{ text: '@openai' }] }, 41 subscriberCountText: { simpleText: '1.23M subscribers' }, 42 videoCountText: { simpleText: '123 videos' }, 43 navigationEndpoint: { browseEndpoint: { canonicalBaseUrl: '/channel/UC123' } }, 44 channelId: 'UC123', 45 })).toEqual({ 46 name: 'OpenAI', 47 handle: '@openai', 48 subscribers: '1.23M subscribers', 49 url: 'https://www.youtube.com/channel/UC123', 50 }); 51 }); 52 it('extractSubscriptionChannel falls back when handle/count fields are overloaded', () => { 53 expect(extractSubscriptionChannel({ 54 title: { 55 runs: [{ text: 'OpenAI' }], 56 }, 57 subscriberCountText: { simpleText: '@openai' }, 58 videoCountText: { simpleText: '1.23M subscribers' }, 59 navigationEndpoint: { browseEndpoint: { canonicalBaseUrl: '/@openai' } }, 60 channelId: 'UC123', 61 })).toEqual({ 62 name: 'OpenAI', 63 handle: '@openai', 64 subscribers: '1.23M subscribers', 65 url: 'https://www.youtube.com/@openai', 66 }); 67 }); 68 });