|
|
@@ -0,0 +1,59 @@
|
|
|
+#!/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;
|
|
|
+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?['editFeedback']:['review'],reviewStatus:'pending',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(),/历史课程补充学习内容/);
|
|
|
+ // 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;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));}
|