feat: support all page URLs and iframe shortcut forwarding

This commit is contained in:
suanyuit-cyber
2026-11-03 10:24:30 +08:00
parent a7d2d30daa
commit a9986a8b7f
6 changed files with 172 additions and 15 deletions
+113
View File
@@ -0,0 +1,113 @@
'use strict';
// Local browser fixtures cover URL protocols and frame focus without opening
// any production site or depending on a user's extension configuration.
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { pathToFileURL } = require('node:url');
const ROOT = path.resolve(__dirname, '..');
const record = { bankName: '全网址题库', bankId: 'global-bank', questionId: 'global-question', type: 'single',
stem: '全网页核验题的标准答案是什么?', options: [{ id: 'a', label: 'A', text: '正确内容' }, { id: 'b', label: 'B', text: '其他内容' }],
correctOptionIds: ['a'], answerTexts: ['正确内容'], answerStatus: 'confirmed' };
function installGM(record) {
window.unsafeWindow = window;
const values = new Map([['huayi-qb.records.v1', [record]], ['huayi-qb.config.v1', { collecting: true }]]);
window.__globalMenus = [];
window.GM_getValue = (key, fallback) => structuredClone(values.has(key) ? values.get(key) : fallback);
window.GM_setValue = (key, value) => values.set(key, structuredClone(value));
window.GM_deleteValue = key => values.delete(key);
window.GM_registerMenuCommand = (name, action) => window.__globalMenus.push({ name, action });
window.GM_addValueChangeListener = () => 1;
window.GM_removeValueChangeListener = () => {};
window.__originalFetch = window.fetch;
}
test('全网址:HTTP、HTTPS、内网地址、本地文件及iframe快捷键', { timeout: 45000 }, async t => {
let playwright;
try { playwright = require(process.env.QB_PLAYWRIGHT_MODULE || 'playwright'); }
catch { playwright = require(path.join(os.homedir(), '.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright')); }
const cached = path.join(os.homedir(), 'AppData/Local/ms-playwright/chromium_headless_shell-1228/chrome-headless-shell-win64/chrome-headless-shell.exe');
const executable = process.env.QB_CHROMIUM_EXECUTABLE || process.env.QB_CHROMIUM_PATH || (fs.existsSync(cached) ? cached : undefined);
const browser = await playwright.chromium.launch({ headless: true, ...(executable ? { executablePath: executable } : {}) });
const context = await browser.newContext();
const errors = [];
const script = fs.readFileSync(path.join(ROOT, 'dist/huayi-question-bank.user.js'), 'utf8');
const localDir = fs.mkdtempSync(path.join(os.tmpdir(), 'huayi-qb-global-'));
const localFile = path.join(localDir, 'index.html');
fs.writeFileSync(localFile, '<!doctype html><html><body><p>本地HTML验收</p></body></html>', 'utf8');
try {
await context.route('**/*', route => {
const url = new URL(route.request().url());
let content = '<p>普通网页验收</p>';
if (url.pathname === '/framed') content = '<iframe src="https://child.example.test/child" id="child"></iframe>';
else if (url.pathname === '/child') content = '<input id="frame-input"><iframe src="https://nested.example.test/nested"></iframe>';
else if (url.pathname === '/nested') content = '<input id="frame-input">';
if (url.pathname === '/blocks-document') content += '<script>document.addEventListener("keydown",event=>event.stopImmediatePropagation(),true)</script>';
return route.fulfill({ status: 200, contentType: 'text/html', body: `<!doctype html><html><body>${content}</body></html>` });
});
await context.addInitScript({ content: `(${installGM.toString()})(${JSON.stringify(record)});\n${script}` });
context.on('page', page => page.on('pageerror', error => errors.push(error.message)));
await t.test('元数据覆盖三种协议,并允许iframe接收快捷键', () => {
const patterns = [...script.matchAll(/^\/\/ @match\s+(\S+)$/gm)].map(match => match[1]);
assert.deepEqual(patterns, ['http://*/*', 'https://*/*', 'file:///*']);
assert.equal(/^\/\/ @noframes\b/m.test(script), false);
});
for (const url of ['http://public.example.test/path', 'https://public.example.test/path?x=1#part',
'http://127.0.0.1:52345/local', 'https://intranet:8443/path', pathToFileURL(localFile).href]) {
await t.test(`${url.startsWith('file:') ? '本地file页面' : url}:默认隐藏、快捷键显示、搜索已导入标准答案`, async () => {
const page = await context.newPage();
try {
await page.goto(url);
await page.locator('#huayi-qb-root').waitFor({ state: 'attached' });
assert.equal(await page.locator('#qb-panel').isVisible(), false);
await page.keyboard.press('Alt+Shift+Q');
assert.equal(await page.locator('#qb-panel').isVisible(), true);
await page.locator('#qb-search').fill('全网页核验题');
await page.locator('#qb-search').press('Enter');
assert.match(await page.locator('#qb-results').innerText(), /正确内容/);
assert.equal(await page.evaluate(() => window.fetch === window.__originalFetch), true, '普通页面不观察业务请求');
await page.keyboard.press('Alt+Shift+Q');
assert.equal(await page.locator('#qb-panel').isVisible(), false);
} finally { await page.close(); }
});
}
await t.test('网页在document阻止键盘传播时仍可唤出,缺少code的Q键也可用', async () => {
const page = await context.newPage();
try {
await page.goto('https://public.example.test/blocks-document');
await page.locator('#huayi-qb-root').waitFor({ state: 'attached' });
await page.keyboard.press('Alt+Shift+Q');
assert.equal(await page.locator('#qb-panel').isVisible(), true);
await page.evaluate(() => window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Q', altKey: true, shiftKey: true })));
assert.equal(await page.locator('#qb-panel').isVisible(), false);
} finally { await page.close(); }
});
await t.test('跨域及嵌套iframe中按快捷键,控制顶层唯一面板且子页不启动存储或采集', async () => {
const page = await context.newPage();
try {
await page.goto('https://public.example.test/framed');
await page.locator('#huayi-qb-root').waitFor({ state: 'attached' });
const child = page.frame({ url: 'https://child.example.test/child' });
const nested = page.frame({ url: 'https://nested.example.test/nested' });
assert.ok(child); assert.ok(nested);
for (const frame of [child, nested]) {
assert.equal(await frame.locator('#huayi-qb-root').count(), 0);
assert.equal(await frame.evaluate(() => window.__globalMenus.length), 0);
assert.equal(await frame.evaluate(() => window.fetch === window.__originalFetch), true);
}
await child.locator('#frame-input').press('Alt+Shift+Q');
await page.locator('#qb-panel').waitFor({ state: 'visible' });
await nested.locator('#frame-input').press('Alt+Shift+Q');
await page.locator('#qb-panel').waitFor({ state: 'hidden' });
assert.equal(await page.locator('#huayi-qb-root').count(), 1);
} finally { await page.close(); }
});
assert.deepEqual(errors, []);
} finally {
await context.close(); await browser.close();
fs.unlinkSync(localFile); fs.rmdirSync(localDir);
}
});