Files

73 lines
6.1 KiB
JavaScript

'use strict';
const assert=require('node:assert/strict');
const core=require('./icampus-study-helper.user.js');
const {harness,ID,detail}=require('./study-helper.test.cjs');
let passed=0;
async function test(name,fn){await fn();passed++;console.log(`PASS ${name}`);}
function mount(h){const nodes=new Map();const panel={querySelector(s){if(!nodes.has(s))nodes.set(s,{textContent:'',value:'all',checked:true});return nodes.get(s);}};
h.context.document.body={append(){}};h.context.document.createElement=()=>({style:{},attachShadow:()=>panel});h.api.mount();return nodes;}
(async()=>{
await test('100 percent learning with a 75 percent passing exam is complete',()=>{
const d=detail();d.course.status='COMPLETED';d.course.progressPercent=100;d.course.examScorePercent=75;d.resources.forEach(r=>r.completed=true);
assert.equal(core.courseDone(d.course),true);assert.equal(core.videoPlan(d).kind,'finish');
d.course.examPassed=false;assert.equal(core.courseDone(d.course),false);assert.equal(core.videoPlan(d).kind,'quiz');
});
await test('an unpassed exam starts its quiz before playing or switching video',async()=>{
const h=harness();let starts=0;const c=h.api.details.get(ID).course;c.examPassed=false;c.examScorePercent=0;
const button={textContent:'开始测验',disabled:false,getClientRects:()=>[{}],click(){starts++;}};
h.selectors.set('.course-quiz',{querySelector:()=>null,querySelectorAll:s=>s==='button'?[button]:[]});
h.api.begin();await h.api.tickCourse(ID);assert.equal(starts,1);assert.equal(h.video.playCount,0);assert.equal(h.buttons[1].clicks,0);assert.equal(h.video.paused,true);
});
await test('a low passing score needs no retake entry and allows return to the list',async()=>{
const h=harness();const d=h.api.details.get(ID);const c=d.course;c.examScorePercent=75;c.progressPercent=100;d.resources.forEach(r=>r.completed=true);
let backs=0;h.selectors.set('.van-nav-bar__left',{click(){backs++;}});
const result={getClientRects:()=>[{}],querySelector:s=>s==='.quiz-result__status.is-pass'?{}:s==='.quiz-result__score strong'?{textContent:'75%'}:null};
h.selectors.set('.course-quiz',{querySelector:s=>s==='.quiz-result'?result:null,querySelectorAll:()=>[]});
h.api.begin();h.api.tickQuiz(ID);await h.api.tickCourse(ID);assert.equal(h.api.state.armed,true);assert.equal(h.api.state.running,true);
assert.equal(backs,1);assert.equal(h.video.playCount,0);assert.equal(h.buttons[1].clicks,0);
});
await test('learning progress and exam score cannot substitute for each other',()=>{
const c={...detail().course,progressPercent:75,examScorePercent:100};assert.equal(core.courseDone(c),false);
c.progressPercent=100;c.examPassed=false;assert.equal(core.courseDone(c),false);
c.examPassed=true;c.examScorePercent=60;assert.equal(core.courseDone(c),true);
c.progressPercent=99;c.status='COMPLETED';assert.equal(core.courseDone(c),false);
});
await test('a passed 75 percent exam with unfinished learning continues its videos',async()=>{
const h=harness();const c=h.api.details.get(ID).course;c.progressPercent=75;c.examScorePercent=75;
h.api.begin();await h.api.tickCourse(ID);h.advance(1000);await h.api.tickCourse(ID);
assert.equal(h.video.playCount,1);assert.equal(core.courseDone(c),false);
});
await test('100 percent learning and a 75 percent pass advance from the result to the next course',async()=>{
const h=harness(),d=h.api.details.get(ID);d.course.progressPercent=100;d.course.examScorePercent=75;d.course.status='COMPLETED';
d.resources.forEach(r=>r.completed=true);
h.selectors.set('.van-nav-bar__left',{click(){h.context.location.hash='#/my-course';}});
const clicks=[0,0];const names=['课程一','课程二'];
h.selectors.set('article.course-row',names.map((name,index)=>({getClientRects:()=>[{}],querySelector:()=>({textContent:name}),click(){clicks[index]++;}})));
h.api.begin();await h.api.tickCourse(ID);h.advance(1000);
h.api.receive({id:'packages',tail:''},{bizCode:0,data:[{courses:[d.course,{id:'123',courseName:'课程二',progressPercent:0,examRequired:true,examPassed:false,status:'NOT_STARTED'}]}]},200);
await h.api.tick();assert.deepEqual(clicks,[0,1]);assert.equal(h.api.state.running,true);assert.equal(h.api.state.armed,true);
});
await test('temporary waits retain the started button state and retry automatically',async()=>{
const h=harness();const ui=mount(h);ui.get('[data-start]').onclick();h.api.park('网络等待');
assert.equal(h.api.state.armed,true);assert.equal(ui.get('[data-start]').disabled,true);assert.match(ui.get('[data-start]').textContent,/已启动/);
h.advance(15001);await h.api.tick();h.advance(1000);await h.api.tick();assert.equal(h.video.playCount,1);
});
await test('ordinary refresh restores the enabled intent without another start click',()=>{
const h=harness();const ui=mount(h);ui.get('[data-start]').onclick();h.api.park('等待页面响应');
const restarted=harness({sessionEntries:[...h.session]});mount(restarted);
assert.equal(restarted.api.state.armed,true);assert.equal(restarted.api.state.running,true);
});
await test('manual cancellation is the only action that clears the stored enabled intent',async()=>{
const h=harness();const ui=mount(h);ui.get('[data-start]').onclick();h.api.park('暂时等待');
ui.get('[data-stop]').onclick();assert.equal(h.api.state.armed,false);assert.equal(h.session.has('icampus.study.v1.enabled'),false);
h.advance(60000);await h.api.tick();assert.equal(h.video.playCount,0);
const restarted=harness({sessionEntries:[...h.session]});mount(restarted);assert.equal(restarted.api.state.armed,false);
});
await test('manual video pause waits for a real resume without cancelling the intent',async()=>{
const h=harness();h.api.begin();h.api.track(h.video);h.video.paused=true;h.api.monitor();
assert.equal(h.api.state.armed,true);h.advance(30000);await h.api.tick();assert.equal(h.video.playCount,0);
h.video.paused=false;await h.api.tick();assert.equal(h.api.state.running,true);
});
console.log(`${passed} persistent-run tests passed.`);
})().catch(e=>{console.error(e);process.exitCode=1;});