import { describe, expect, it, vi } from 'vitest'; import { MediaBridge, notifyTab } from '../../src/background/media-bridge.js'; import type { ContentMessage } from '../../src/background/media-bridge.js'; import type { VeloxTransport } from '../../src/background/transport/index.js'; function fakeOnMessage() { let listener: ((msg: ContentMessage, sender: unknown, sendResponse: (r: unknown) => void) => boolean | void) | undefined; return { addListener: (cb: typeof listener) => { listener = cb; }, fire: (msg: ContentMessage) => new Promise((resolve) => { listener?.(msg, {}, resolve); }), }; } function fakeTransport(call: VeloxTransport['call']): VeloxTransport { return { kind: 'ws', state: 'connected', status: { state: 'connected', needsPairing: false, fatal: null, retryAfterSec: null, sessionId: null, daemonVersion: null, capabilities: [], }, connect: async () => undefined, disconnect: () => undefined, call, on: () => undefined, off: () => undefined, onStateChange: () => () => undefined, }; } describe('MediaBridge', () => { it('answers velox-list-variants by calling media.listVariants', async () => { const call = vi.fn(async () => ({ variants: [], manifestType: 'hls', drmProtected: false })); const bridge = new MediaBridge(() => fakeTransport(call as never)); const onMessage = fakeOnMessage(); bridge.attach(onMessage); const response = await onMessage.fire({ type: 'velox-list-variants', params: { manifestUrl: 'https://x/m.m3u8' } }); expect(call).toHaveBeenCalledWith('media.listVariants', { manifestUrl: 'https://x/m.m3u8' }); expect(response).toEqual({ ok: true, result: { variants: [], manifestType: 'hls', drmProtected: false } }); }); it('answers velox-add-variant by calling media.addVariant', async () => { const call = vi.fn(async () => ({ taskId: 't1', state: 'queued' })); const bridge = new MediaBridge(() => fakeTransport(call as never)); const onMessage = fakeOnMessage(); bridge.attach(onMessage); const response = await onMessage.fire({ type: 'velox-add-variant', params: { manifestUrl: 'https://x/m.m3u8', variantId: 'v1' }, }); expect(call).toHaveBeenCalledWith('media.addVariant', { manifestUrl: 'https://x/m.m3u8', variantId: 'v1' }); expect(response).toEqual({ ok: true, result: { taskId: 't1', state: 'queued' } }); }); it('answers with an error when the transport is not ready', async () => { const bridge = new MediaBridge(() => undefined); const onMessage = fakeOnMessage(); bridge.attach(onMessage); const response = await onMessage.fire({ type: 'velox-list-variants', params: { manifestUrl: 'https://x/m.m3u8' } }); expect(response).toEqual({ ok: false, error: 'transport not ready' }); }); it('answers with an error when the call rejects', async () => { const call = vi.fn(async () => { throw new Error('refused: drm protected'); }); const bridge = new MediaBridge(() => fakeTransport(call as never)); const onMessage = fakeOnMessage(); bridge.attach(onMessage); const response = await onMessage.fire({ type: 'velox-list-variants', params: { manifestUrl: 'https://x/m.m3u8' } }); expect(response).toEqual({ ok: false, error: 'refused: drm protected' }); }); }); describe('notifyTab', () => { it('sends a velox-manifest-detected message to the tab', () => { const sent: unknown[] = []; const tabs = { sendMessage: (tabId: number, message: unknown) => { sent.push({ tabId, message }); return Promise.resolve(); } }; notifyTab(tabs, { tabId: 3, url: 'https://x/m.m3u8', headers: [] }); expect(sent).toEqual([{ tabId: 3, message: { type: 'velox-manifest-detected', url: 'https://x/m.m3u8', headers: [] } }]); }); it('swallows a rejection (tab has no content script listening)', () => { const tabs = { sendMessage: () => Promise.reject(new Error('no receiving end')) }; expect(() => notifyTab(tabs, { tabId: 3, url: 'https://x/m.m3u8', headers: [] })).not.toThrow(); }); });