/**
* UI smoke tests: load the real page scripts (popup.js / options.js) as ES
* modules against a mocked browser API and verify that they initialize the
* DOM. This catches import/export mismatches that pure unit tests miss
* (e.g. a named import that the module does not export -> SyntaxError and
* the whole page script never runs).
*/
import { LANGUAGES } from '../src/shared/constants.js';
const { describe, it, assertTrue, assertEqual } = window.TestRunner;
/**
* Install a fake `browser` global before importing page scripts.
*/
function installFakeBrowser() {
const data = new Map();
const listeners = [];
const local = {
async get(defaults) {
const result = {};
for (const key of Object.keys(defaults)) {
result[key] = data.has(key) ? data.get(key) : defaults[key];
}
return result;
},
async set(obj) {
const changes = {};
for (const [key, value] of Object.entries(obj)) {
changes[key] = { oldValue: data.get(key), newValue: value };
data.set(key, value);
}
for (const listener of listeners) listener(changes, 'local');
}
};
globalThis.browser = {
storage: {
local,
onChanged: {
addListener(fn) {
listeners.push(fn);
}
}
},
runtime: {
/** @returns {Promise<{ok: boolean, models?: string[]}>} */
sendMessage: async () => ({ ok: true, models: ['fake-model'] }),
openOptionsPage: () => {}
}
};
return globalThis.browser;
}
/**
* Run an ES module script by importing it with a cache-busting query.
* The page's markup is added inside a sandbox container; the script runs,
* async init settles, assertions are collected via a probe callback, and
* finally the sandbox is removed and the runner's own elements restored.
* @param {string} scriptPath
* @param {string} markup
* @param {() => *} probe assertions to run while the sandbox is attached
*/
async function runPageScript(scriptPath, markup, probe) {
installFakeBrowser();
// Preserve the runner's result elements across the DOM swap.
const results = document.getElementById('results');
const summary = document.getElementById('summary');
const resultsParent = results ? results.parentNode : null;
const summaryParent = summary ? summary.parentNode : null;
const resultsNext = results ? results.nextSibling : null;
const summaryNext = summary ? summary.nextSibling : null;
const sandbox = document.createElement('div');
sandbox.id = 'smoke-sandbox';
sandbox.innerHTML = markup;
document.body.append(sandbox);
try {
// Import with cache-buster so repeated runs re-execute the script.
await import(`${scriptPath}?smoke=${Date.now()}-${Math.random().toString(36).slice(2)}`);
// Let async init settle.
await new Promise((resolve) => setTimeout(resolve, 50));
return probe();
} finally {
sandbox.remove();
if (results && resultsParent) resultsParent.insertBefore(results, resultsNext);
if (summary && summaryParent) summaryParent.insertBefore(summary, summaryNext);
// Reset the fake browser so later tests use the real API if needed.
delete globalThis.browser;
}
}
describe('popup page script smoke', () => {
it('initializes language options and model select from markup', async () => {
const r = await runPageScript('../src/popup/popup.js', `
Ollama Translations