import { describe, it } from './runner.mjs'; import { JSDOM } from 'jsdom'; import * as fs from 'fs'; import * as path from 'path'; import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const viewerSource = fs.readFileSync( path.join(__dirname, '../src/viewer/viewer.js'), 'utf-8' ); /** * Create a JSDOM environment with mocked Chrome extension APIs. * * @param {object} options * @param {object} options.storageData - Initial chrome.storage.local contents * @param {string} options.url - Page URL (must include query params viewer.js reads at load) * @param {string} options.options.hlsMode - 'native' (Safari-like: window.HLS undefined), * 'mocked' (Hls.js loaded with mocked API), or 'none' (no mocking — CDN fetches hang) */ function createBrowserEnv({ storageData = {}, url = 'https://example.com/viewer/viewer.html', hlsMode = 'mocked' } = {}) { const html = `
`; const dom = new JSDOM(html, { url, runScripts: 'dangerously', resources: 'usable', }); const win = dom.window; // Shared mutable storage object const storage = { ...storageData }; win.chrome = { storage: { local: { get: (keys) => new Promise((resolve) => { const result = {}; for (const key of (keys || [])) result[key] = storage[key]; resolve(result); }), set: (data) => new Promise((resolve) => { for (const [k, v] of Object.entries(data)) storage[k] = v; resolve(); }), remove: (keys) => new Promise((resolve) => { for (const key of (keys || [])) delete storage[key]; resolve(); }), }, }, }; // Mock HLS to avoid hanging on CDN fetch in jsdom. // jsdom does not fire onerror for dynamically loaded