Fix desktop quiz submission and keep runs enabled until cancelled

This commit is contained in:
2026-10-06 02:23:01 +08:00
parent 08c9e0aa4d
commit 606d264917
7 changed files with 218 additions and 96 deletions
+11 -4
View File
@@ -48,7 +48,7 @@ test('quiz observer scopes exact endpoints and origins', () => {
// The userscript must click choices, submit, confirm, read the answer card and retry.
function simulator({feedback=true,exhaust=false,threshold=100}={}) {
const h=harness(); let view='idle', dialog=false, attempt=0, submissions=0;
let answers={}, selected={}, lastCard=[];
let answers={}, selected={}, lastCard=[],scorePercent=0;
const definitions=[q('q1'),q('q2'),q('q3','multi')];
const expected={q1:['A'],q2:['B'],q3:['A','B']};
const clicks=[];
@@ -66,11 +66,14 @@ function simulator({feedback=true,exhaust=false,threshold=100}={}) {
});
const start=button('开始测验',begin);
const submit=button('交卷',()=>{dialog=true;});
const hiddenFooter=button('交卷',()=>{throw new Error('Must not click hidden mobile footer');});
hiddenFooter.getClientRects=()=>[];
const confirm=button('确定交卷',()=>{
dialog=false; ++submissions; answers=structuredClone(selected);
const body={answers:definitions.map(d=>d.type==='multi'?{questionId:d.id,selectedOptionKeys:answers[d.id]}:{questionId:d.id,selectedOptionKey:answers[d.id][0]})};
lastCard=body.answers.map(a=>({...a,correct:JSON.stringify([...(answers[a.questionId]||[])].sort())===JSON.stringify(expected[a.questionId])}));
const score=lastCard.filter(a=>a.correct).length/definitions.length*100;
scorePercent=score;
const examId=String(900+attempt);
h.api.receive({kind:'answers',examId,body},{bizCode:0,data:{}},200);
h.api.receive({kind:'submit',examId},{bizCode:0,data:{scorePercent:score,passed:score>=threshold,canRetry:!exhaust,attemptsExhausted:exhaust}},200);
@@ -86,12 +89,12 @@ function simulator({feedback=true,exhaust=false,threshold=100}={}) {
})):[]}));
const root=element({
querySelector(s){
if(s==='.quiz-result'&&['pass','fail'].includes(view)) return element({querySelector:s=>s==='.quiz-result__status.is-pass'&&view==='pass'?{}:s==='.quiz-result__score strong'?{textContent:view==='pass'?'100%':'33%'}:null});
if(s==='.quiz-result'&&['pass','fail'].includes(view)) return element({querySelector:s=>s==='.quiz-result__status.is-pass'&&view==='pass'?{}:s==='.quiz-result__score strong'?{textContent:scorePercent+'%'}:null});
if(s==='.quiz-result__exhausted'&&view==='fail'&&exhaust)return {};
if(s==='button.quiz-footer__submit'&&view==='form') return submit;
if(s==='button.quiz-footer__submit'&&view==='form') return hiddenFooter;
return null;
},
querySelectorAll(s){if(s==='article.quiz-question')return view==='form'?nodes:[];if(s==='button')return view==='idle'?[start]:view==='fail'?[retry]:[];return [];}
querySelectorAll(s){if(s==='article.quiz-question')return view==='form'?nodes:[];if(s==='button')return view==='idle'?[start]:view==='fail'?[retry]:view==='form'?[hiddenFooter,submit]:[];return [];}
});
h.selectors.set('.course-quiz',root);
const original=h.context.document.querySelectorAll;
@@ -124,4 +127,8 @@ test('foreign exam submission cannot teach this attempt', () => {
s.h.api.receive({kind:'submit',examId:'123456'},{bizCode:0,data:{scorePercent:100,passed:true}},200);
assert.equal(Object.keys(s.h.api.state.bank).length,0);
});
test('unlimited retakes are not stopped by the old 256-round cap',()=>{
const s=simulator();s.h.api.tickQuiz(ID);s.h.api.state.quiz.round=300;s.run();
assert.equal(s.view,'pass');assert.equal(s.submissions,3);assert.ok(s.h.api.state.armed);
});
console.log(`${passed} quiz tests passed.`);