check-lesson-records-browser.mjs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536
  1. import {chromium} from 'playwright';
  2. import {mkdirSync,writeFileSync,readFileSync} from 'node:fs';
  3. import * as XLSX from 'xlsx';
  4. import assert from 'node:assert/strict';
  5. export async function checkLessonRecordsBrowser({auth,output,studentId=1857,memberObjectId}){
  6. mkdirSync(output,{recursive:true});const browser=await chromium.launch({headless:true,executablePath:'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'});const checks=[],errors=[];
  7. try{const context=await browser.newContext({viewport:{width:1440,height:1050}});await context.addInitScript(auth=>{localStorage.setItem('xiaoshu_admin_session_v1',JSON.stringify(auth));sessionStorage.setItem('xiaoshu_admin_session_v1',JSON.stringify(auth));},auth);
  8. const page=await context.newPage();page.on('pageerror',e=>errors.push(e.message));
  9. await page.goto('http://127.0.0.1:4300/admin/lessons?studentId='+studentId);await page.getByRole('button',{name:'查看反馈',exact:true}).first().waitFor({timeout:60000});
  10. assert.match(await page.locator('.record-count').innerText(),/累计已上课/);checks.push('集中查询入口、真实数据与累计课次');
  11. const total=Number((await page.locator('.record-count').innerText()).match(/当前筛选 (\d+) 节/)[1]);
  12. if(total>20){assert.equal(await page.locator('.lesson-table tbody tr').count(),20);const first=await page.locator('.lesson-table tbody tr').first().innerText();await page.getByRole('button',{name:'下一页',exact:true}).click();await page.waitForFunction(()=>document.querySelector('.lesson-pagination')?.textContent.includes('第 2 页')&&!document.querySelector('[role=status]'));assert.notEqual(await page.locator('.lesson-table tbody tr').first().innerText(),first);checks.push('超过一页的真实记录可以翻页');}
  13. await page.getByRole('button',{name:'查看该学员全部结果'}).click();await page.waitForFunction(n=>document.querySelectorAll('.lesson-table tbody tr').length===n&&!document.querySelector('[role=status]'),total,{timeout:60000});
  14. const dates=await page.locator('.lesson-table tbody tr td:nth-child(2)').allTextContents();assert.deepEqual(dates,[...dates].sort().reverse());checks.push('全部历史行数与累计一致、北京时间倒序');
  15. await page.screenshot({path:output+'/history-desktop.png',fullPage:true});
  16. const downloadPromise=page.waitForEvent('download');await page.getByRole('button',{name:'导出 Excel',exact:true}).click();const download=await downloadPromise;assert.match(download.suggestedFilename(),/上课记录.*xlsx/);await download.saveAs(output+'/lesson-records.xlsx');const book=XLSX.read(readFileSync(output+'/lesson-records.xlsx'));assert.equal(XLSX.utils.sheet_to_json(book.Sheets[book.SheetNames[0]]).length,total);checks.push('真实筛选结果导出 Excel,行数与课次一致');
  17. await page.getByRole('button',{name:'查看反馈',exact:true}).first().click();await page.getByRole('dialog',{name:'上课记录详情'}).waitFor();await page.getByRole('heading',{name:'老师点评',exact:true}).waitFor({timeout:60000}).catch(async e=>{await page.screenshot({path:output+'/detail-error.png',fullPage:true});throw Error((await page.getByRole('dialog').innerText())+' '+e.message);});
  18. const picker=page.getByRole('combobox',{name:'目标陪练'});
  19. for(const viewport of [{width:1440,height:720},{width:390,height:640}]){
  20. await page.setViewportSize(viewport);await page.getByRole('button',{name:'更换抗遗忘陪练'}).click();
  21. await page.waitForFunction(()=>{const r=document.querySelector('.transfer-panel')?.getBoundingClientRect();return r&&r.bottom<=innerHeight-8&&r.bottom>0;},null,{timeout:5000});
  22. await picker.click();await page.locator('#transfer-coach-results').getByRole('option').first().waitFor({timeout:60000});
  23. await page.waitForFunction(()=>{const input=document.querySelector('#transfer-coach-search')?.getBoundingClientRect(),list=document.querySelector('#transfer-coach-results')?.getBoundingClientRect();return input&&list&&input.top>=8&&list.bottom<=innerHeight-8&&list.height>0;},null,{timeout:5000});
  24. await page.screenshot({path:output+'/coach-scroll-'+viewport.width+'.png'});await picker.press('Escape');await page.locator('.transfer-panel').getByRole('button',{name:'取消',exact:true}).click();
  25. }
  26. checks.push('桌面和窄屏:打开转交自动露出底部,展开老师列表后搜索框和列表完整可见');
  27. await page.setViewportSize({width:1440,height:1050});await page.getByRole('button',{name:'更换抗遗忘陪练'}).click();await picker.click();await page.locator('#transfer-coach-results').getByRole('option').first().waitFor({timeout:60000});
  28. const firstOption=page.locator('#transfer-coach-results').getByRole('option').first(),teacherName=await firstOption.locator('span').first().innerText();await picker.fill(teacherName.slice(0,1));await page.locator('#transfer-coach-results').getByRole('option').first().waitFor();assert.ok((await page.locator('#transfer-coach-results').getByRole('option').allTextContents()).every(t=>t.includes(teacherName.slice(0,1))));
  29. await picker.press('ArrowDown');await picker.press('Enter');await page.waitForFunction(()=>document.querySelector('#transfer-coach-search')?.getAttribute('aria-expanded')==='false',null,{timeout:3000});await picker.fill('没有这个老师XYZ');await page.getByText('没有匹配的陪练,请换个姓名或编号').waitFor();await picker.fill(teacherName.slice(0,1));await page.locator('#transfer-coach-results').getByRole('option').first().waitFor();
  30. assert.equal(await page.locator('#transfer-coach-results').getByRole('option').first().evaluate(e=>getComputedStyle(e).backgroundColor),'rgb(255, 255, 255)');await page.locator('.transfer-panel').screenshot({path:output+'/coach-search.png'});checks.push('陪练姓名模糊搜索、键盘选择、无结果提示及白底列表;未提交真实转交');
  31. await page.screenshot({path:output+'/feedback-desktop.png',fullPage:true});await page.getByRole('button',{name:'关闭',exact:true}).click();
  32. await page.setViewportSize({width:390,height:844});await page.screenshot({path:output+'/history-mobile-width.png',fullPage:true});assert.ok(await page.evaluate(()=>document.documentElement.scrollWidth<=window.innerWidth+1));checks.push('窄屏无整页横向溢出');
  33. if(memberObjectId){await page.setViewportSize({width:1440,height:1050});await page.goto('http://127.0.0.1:4300/admin/members/'+memberObjectId);await page.getByRole('button',{name:'上课记录',exact:true}).click();await page.locator('app-lesson-records .data-table tbody tr').first().waitFor();assert.equal(await page.locator('nav.pagination').count(),0);checks.push('会员详情复用同一列表且不重复分页');}
  34. assert.deepEqual(errors,[]);writeFileSync(output+'/browser-result.json',JSON.stringify({passed:true,checks},null,2));return{passed:true,checks};
  35. }finally{await browser.close();}
  36. }