| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364 |
- #!/usr/bin/env node
- // Isolated local fixture browser acceptance. Does not read or write online accounts.
- import {chromium} from 'playwright';
- import {createServer} from 'node:http';
- import {readFileSync,existsSync,mkdirSync,writeFileSync} from 'node:fs';
- import {resolve,extname} from 'node:path';
- import assert from 'node:assert/strict';
- const root=resolve('dist/xiaoshu-mobile/browser'),out=resolve('tmp/lesson-feedback-qa');mkdirSync(out,{recursive:true});
- const server=createServer((req,res)=>{let file=resolve(root,'.'+decodeURIComponent(new URL(req.url,'http://local').pathname));if(!file.startsWith(root+'/'))file=root+'/index.html';if(!existsSync(file)||!extname(file))file=root+'/index.html';try{res.setHeader('Content-Type',({'.html':'text/html','.js':'text/javascript','.css':'text/css','.png':'image/png','.svg':'image/svg+xml','.woff2':'font/woff2'})[extname(file)]||'application/octet-stream');res.end(readFileSync(file));}catch{res.statusCode=404;res.end();}});
- await new Promise(r=>server.listen(0,'127.0.0.1',r));const origin='http://127.0.0.1:'+server.address().port;
- const browser=await chromium.launch({headless:true,executablePath:'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'});
- const errors=[],calls=[];
- let feedback=null,failSave=false,role=3,reviewed=false;
- const revision='2026-10-04 06:00:00.123456+00';
- const order=()=>({GeneralID:901,szyh:100,pl:300,HoneyName:'测试学员',kcid:801,kcmc:'历史课程',dszt:30,updatedAt:revision,allowedActions:role===3?(reviewed&&feedback?.feedbackStatus==='submitted'?[]:['editFeedback']):(reviewed?[]:['review']),reviewStatus:reviewed?'submitted':'pending',feedbackLocked:reviewed&&feedback?.feedbackStatus==='submitted',yysj:'2026-10-04 14:00',feedback});
- try {
- const context=await browser.newContext({viewport:{width:390,height:844}});
- await context.addInitScript(()=>{if(!localStorage.getItem('xiaoshu.angular.session'))localStorage.setItem('xiaoshu.angular.session',JSON.stringify({token:'r:local-fixture-only',user:{userId:300,groupId:3,honeyName:'测试老师'}}));});
- await context.route('**/*',async route=>{
- const u=new URL(route.request().url());if(u.origin!==origin)return route.abort();
- if(!u.pathname.startsWith('/cloud-functions/'))return route.continue();
- const {params:p}=route.request().postDataJSON();calls.push(p);let result=[];
- if(p.action==='app_lesson_report')result={order:order(),feedback,records:[],antiForget:{total:10,completed:0}};
- if(p.action==='e_order_detail')result=[order()];
- if(p.action==='app_lesson_study')result={batches:[],record:null,pending:0};
- if(p.action==='app_live_versions')result={version:'1',scopeVersions:{},syncStatus:{state:'current'}};
- if(p.action==='app_lesson_feedback_update'){
- if(failSave)return route.fulfill({status:409,json:{retcode:-1,retmsg:'模拟保存失败'}});
- assert.equal(p.expectedUpdatedAt,revision);
- feedback={...JSON.parse(p.feedback),feedbackStatus:'submitted',revision:'2026-10-15 06:00:00.987654+00'};result={saved:true};
- }
- await route.fulfill({json:{retcode:1,result}});
- });
- const page=await context.newPage();page.on('pageerror',e=>errors.push(e.message));
- await page.goto(origin+'/pages/shop/baobiao?sid=901');
- await page.getByRole('link',{name:'补充反馈',exact:true}).waitFor();
- assert.match(await page.locator('.status-badge').innerText(),/老师反馈待补/);
- await page.screenshot({path:out+'/missing-report-390.png',fullPage:true});
- await page.getByRole('link',{name:'补充反馈',exact:true}).click();
- await page.locator('dialog[open]').waitFor();assert.match(await page.locator('#completion-title').innerText(),/补充课堂反馈/);
- await page.getByPlaceholder('例:人教版三年级 Unit 3,重点词汇与句型').fill('历史课程补充学习内容');
- await page.getByPlaceholder('说明当前进度与下次起点').fill('已学完第一单元');
- await page.getByText('本次无作业',{exact:true}).click();
- failSave=true;await page.getByRole('button',{name:'提交反馈',exact:true}).click();await page.locator('.feedback-error').waitFor();
- assert.equal(await page.locator('dialog[open]').count(),1);assert.equal(await page.getByPlaceholder('说明当前进度与下次起点').inputValue(),'已学完第一单元');
- failSave=false;await page.getByRole('button',{name:'提交反馈',exact:true}).click();await page.locator('dialog[open]').waitFor({state:'hidden'});
- assert.equal(calls.filter(c=>c.action==='app_lesson_complete').length,0);
- await page.goto(origin+'/pages/shop/baobiao?sid=901');await page.getByRole('link',{name:'修改报告',exact:true}).waitFor();
- assert.match(await page.locator('.lesson-report').innerText(),/历史课程补充学习内容/);
- reviewed=true;await page.reload();await page.getByText('学生已评价,课堂反馈已锁定',{exact:true}).waitFor();
- assert.equal(await page.getByRole('link',{name:'修改报告',exact:true}).count(),0);
- await page.goto(origin+'/pages/shop/get_order?id=901&editReport=1');await page.getByText('学生已评价,课堂反馈已锁定',{exact:true}).waitFor();
- assert.equal(await page.locator('dialog[open]').count(),0);
- await page.goto(origin+'/pages/shop/baobiao?sid=901');
- // Pending modern reports use the same clear supplement action.
- feedback={feedbackStatus:'pending',revision};await page.reload();await page.getByRole('link',{name:'补充反馈',exact:true}).waitFor();
- assert.match(await page.locator('.status-badge').innerText(),/老师反馈待补/);
- role=1;feedback=null;reviewed=false;await page.evaluate(()=>localStorage.setItem('xiaoshu.angular.session',JSON.stringify({token:'r:student-fixture',user:{userId:100,groupId:1,honeyName:'测试学员'}})));
- await page.reload();await page.locator('.report-card').first().waitFor();assert.equal(await page.getByRole('link',{name:'补充反馈',exact:true}).count(),0);
- await page.goto(origin+'/pages/shop/get_order?id=901');await page.getByRole('button',{name:'提交评价',exact:true}).waitFor();assert.equal(await page.getByRole('button',{name:'补充反馈',exact:true}).count(),0);
- assert.deepEqual(errors,[]);
- writeFileSync(out+'/result.json',JSON.stringify({passed:true,checks:['历史报告补交入口与自动打开表单','保存失败保留输入,重试成功后报告可见','仅调用反馈接口,不调用结课接口','评价后隐藏编辑入口,旧编辑链接不打开表单','先评价仍可首次补交','待补报告文案','学生没有老师补交入口,保留独立评价'],environment:'local production build + isolated API fixtures; not real WeChat'},null,2));
- console.log('Feedback browser checks passed');
- } finally {await browser.close();await new Promise(r=>server.close(r));}
|