feat: add draggable panel and restrict assistant to site directory
This commit is contained in:
+28
-15
@@ -12,7 +12,7 @@ const scriptPath = path.join(workspace, '谊安学助手 2.0.user.js');
|
||||
const script = fs.readFileSync(scriptPath, 'utf8');
|
||||
const coreScript = fs.readFileSync(path.join(workspace, 'question-bank-core.js'), 'utf8');
|
||||
assert(script.includes('// @name 谊安学助手 2.0'));
|
||||
assert(script.includes('// @match *://*/*'));
|
||||
assert.deepEqual(Array.from(script.matchAll(/^\/\/ @match\s+(.+)$/gm), match => match[1].trim()), ['http://10.119.67.7/clientcn/*']);
|
||||
const qaDir = process.env.YIAN_EXAM_QA_DIR || path.join(os.tmpdir(), 'yianxue-exam-qa');
|
||||
fs.mkdirSync(qaDir, { recursive: true });
|
||||
const roles = ['主操', '班长', '副操'];
|
||||
@@ -110,11 +110,16 @@ function installExamModel() {
|
||||
await context.exposeBinding('__gmRead', (_, key, fallback) => memory.has(key) ? structuredClone(memory.get(key)) : fallback);
|
||||
await context.exposeBinding('__gmWrite', (_, key, value) => { memory.set(key, structuredClone(value)); });
|
||||
const sandboxScript = '(function(){const GM_getValue=(key,fallback)=>window.__gmRead(key,fallback);const GM_setValue=(key,value)=>window.__gmWrite(key,value);\n' + script + '\n})();';
|
||||
let origin = '';
|
||||
await context.route('**/*', route => {
|
||||
const url = route.request().url();
|
||||
if (url.startsWith(origin + '/') && origin || url.startsWith(pathToFileURL(qaDir).href + '/')) return route.continue();
|
||||
externalRequests.push(url); return route.abort();
|
||||
const origin = 'http://10.119.67.7';
|
||||
let localOrigin = '';
|
||||
await context.route('**/*', async route => {
|
||||
const url = new URL(route.request().url());
|
||||
if (url.origin === origin && localOrigin) {
|
||||
const response = await route.fetch({ url: localOrigin + url.pathname + url.search });
|
||||
return route.fulfill({ response });
|
||||
}
|
||||
if (localOrigin && url.origin === localOrigin || url.protocol === 'file:' && url.href.startsWith(pathToFileURL(qaDir).href + '/')) return route.continue();
|
||||
externalRequests.push(url.href); return route.abort();
|
||||
});
|
||||
const capture = page => {
|
||||
page.on('pageerror', error => errors.push(error.message));
|
||||
@@ -139,8 +144,8 @@ function installExamModel() {
|
||||
const pageHTML = documentHTML(fixture);
|
||||
server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' }); res.end(pageHTML); });
|
||||
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
||||
origin = 'http://127.0.0.1:' + server.address().port;
|
||||
const examURL = origin + '/unfixed/exam-path?flag=test&title=' + encodeURIComponent(fixture.title);
|
||||
localOrigin = 'http://127.0.0.1:' + server.address().port;
|
||||
const examURL = origin + '/clientcn/exam/nested/exam-path?flag=test&title=' + encodeURIComponent(fixture.title);
|
||||
const page = await context.newPage(); capture(page);
|
||||
async function open(page, url) {
|
||||
await page.goto(url);
|
||||
@@ -231,7 +236,7 @@ function installExamModel() {
|
||||
YianAssistant.setAutoAnswer(false);
|
||||
const root = document.createElement('app-examtest'); root.innerHTML = html;
|
||||
document.querySelector('app-examtest').replaceWith(root);
|
||||
history.replaceState({}, '', '/unfixed/case-' + sequence + '?flag=' + mode + '&title=' + encodeURIComponent(title));
|
||||
history.replaceState({}, '', '/clientcn/exam/case-' + sequence + '?flag=' + mode + '&title=' + encodeURIComponent(title));
|
||||
if (mode === 'exercise') {
|
||||
root.querySelector('.pc_examtest_commit').textContent = '重新练习';
|
||||
root.querySelectorAll('.pc_examtest_content_question p').forEach(p => { const button = document.createElement('span'); button.className = 'pc_examtest_content_question_showanswer'; button.textContent = '查看答案'; p.appendChild(button); });
|
||||
@@ -305,17 +310,25 @@ function installExamModel() {
|
||||
const filePath = path.join(qaDir, 'saved-exam.html');
|
||||
fs.writeFileSync(filePath, '<!-- saved from url=(0000)' + examURL + ' -->\n' + pageHTML);
|
||||
const saved = await context.newPage(); capture(saved);
|
||||
await open(saved, pathToFileURL(filePath).href);
|
||||
await saved.waitForFunction(() => YianAssistant.getState().pageReady);
|
||||
assert.equal(await saved.evaluate(() => YianAssistant.setAutoAnswer(true)), false);
|
||||
assert.equal(await saved.evaluate(() => examModel.clicks.length), 0);
|
||||
await saved.goto(pathToFileURL(filePath).href);
|
||||
const inspectSaved = () => saved.evaluate(() => ({
|
||||
body: document.body.innerHTML, select: document.body.getAttribute('onselectstart'),
|
||||
context: document.body.getAttribute('oncontextmenu'), style: document.body.getAttribute('style'),
|
||||
userSelect: getComputedStyle(document.body).userSelect,
|
||||
selected: document.querySelectorAll('.examtest_content_list_des_checked').length
|
||||
}));
|
||||
const beforeSaved = await inspectSaved();
|
||||
await saved.addScriptTag({ content: sandboxScript });
|
||||
assert.equal(await saved.locator('#yian-question-assistant').count(), 0);
|
||||
assert.deepEqual(await saved.evaluate(() => [typeof window.JSZip, typeof window.QuestionBankCore, typeof window.YianAssistant]), ['undefined', 'undefined', 'undefined']);
|
||||
assert.deepEqual(await inspectSaved(), beforeSaved, 'file 保存考试不能注入或改变复制限制和选项');
|
||||
await saved.close();
|
||||
await reset('exercise');
|
||||
const navigation = await page.evaluate(({ html, title }) => {
|
||||
const enabled = YianAssistant.setAutoAnswer(true);
|
||||
const root = document.createElement('app-examtest'); root.innerHTML = html;
|
||||
document.querySelector('app-examtest').replaceWith(root);
|
||||
history.pushState({}, '', '/unfixed/final-exam?flag=test&title=' + encodeURIComponent(title));
|
||||
history.pushState({}, '', '/clientcn/exam/final-exam?flag=test&title=' + encodeURIComponent(title));
|
||||
dispatchEvent(new PopStateEvent('popstate'));
|
||||
return { enabled, afterNavigation: YianAssistant.getState().autoAnswer };
|
||||
}, { html: fixture.html, title: fixture.title });
|
||||
@@ -323,7 +336,7 @@ function installExamModel() {
|
||||
assert.equal(navigation.afterNavigation, false);
|
||||
await page.waitForTimeout(250);
|
||||
assert.equal(await page.getByRole('switch', { name: '自动选择答案' }).isChecked(), false);
|
||||
console.log('PASS 未知 flag、移动页面、file 保存考试拒绝自动选择;练习转考试恢复关闭');
|
||||
console.log('PASS 未知 flag、移动页面拒绝自动选择;file 保存考试完全不注入;练习转考试恢复关闭');
|
||||
|
||||
assert.deepEqual(externalRequests, [], '测试不允许任何外部业务请求');
|
||||
assert.deepEqual(downloads, [], '考试不得自动或手动发起题库下载');
|
||||
|
||||
Reference in New Issue
Block a user