browser-case.mjs 3.7 KB

123456789101112131415161718192021222324252627282930
  1. import {chromium} from 'playwright';
  2. export async function startBrowser(){return chromium.launch({headless:true,executablePath:'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'});}
  3. const pages={ 'ops/users/list':['members','app-operations-users table tbody tr'], 'ops/stores/list':['stores','app-operations-stores table tbody tr'], 'ops/schedule/calendar':['schedule','[aria-busy="false"].content-card'], 'ops/dashboard/summary':['dashboard','.overview-metrics'], 'ops/payroll/overview':['payroll','.payroll-summary-table tbody tr'] };
  4. export async function browserCase(browser,{variant,operation,path,token,me,pair,hash,origin='http://127.0.0.1:4300'}){
  5. const [pagePath,selector]=pages[operation],ctx=await browser.newContext({viewport:{width:1440,height:1000}}),records=[],errors=[];
  6. await ctx.addInitScript(auth=>localStorage.setItem('xiaoshu_admin_session_v1',JSON.stringify(auth)),{objectId:me.objectId,sessionToken:token,username:'性能验收',roles:me.roles,operationsRole:'ops-manager',isSuperAdmin:true});
  7. let complete;const received=new Promise(resolve=>complete=resolve);
  8. const page=await ctx.newPage();page.on('pageerror',e=>errors.push(e.message));
  9. await page.route('**/cloud-functions/xiaoshu/ops/gateway-v3',async route=>{
  10. const req=route.request(),data=req.postDataJSON();if(data?.params?.operation!==operation)return route.continue();
  11. const start=performance.now();try{const r=await route.fetch({url:'https://server.xiaoshu.pro/api/functions/'+path,timeout:90000}),raw=await r.body(),json=JSON.parse(raw.toString());records.push({ms:Math.round(performance.now()-start),bytes:raw.length,hash:hash(json.data),success:json.success!==false,status:r.status()});await route.fulfill({response:r,body:raw});complete();}catch(e){errors.push(e.message);await route.abort().catch(()=>{});complete();}
  12. });
  13. try{
  14. const start=performance.now();await page.goto(origin+'/admin/'+pagePath,{waitUntil:'domcontentloaded'});await received;
  15. const ready=operation==='ops/users/list'?'table tbody tr':operation==='ops/stores/list'?'table tbody tr':selector;
  16. await page.locator(ready).first().waitFor({state:'visible',timeout:60000});
  17. await page.waitForFunction(()=>!document.querySelector('.schedule-loading')&&!document.querySelector('[role="alert"]'));
  18. if(!records.length||records.some(r=>!r.success))throw Error('No successful primary data request');
  19. await page.evaluate(()=>new Promise(r=>requestAnimationFrame(()=>requestAnimationFrame(r))));const readyMs=Math.round(performance.now()-start);
  20. if(pair===0){await page.screenshot({path:`tmp/admin-data-2026-10-15/browser-${pagePath}-${variant}-1440.png`,fullPage:false,animations:'disabled'});await page.setViewportSize({width:1000,height:900});await page.evaluate(()=>new Promise(r=>requestAnimationFrame(()=>requestAnimationFrame(r))));await page.waitForTimeout(200);await page.screenshot({path:`tmp/admin-data-2026-10-15/browser-${pagePath}-${variant}-1000.png`,fullPage:false,animations:'disabled'});}
  21. let menuReturn;
  22. if(process.env.PERF_MENU_RETURN&&variant==='B'&&operation!=='ops/schedule/calendar'){
  23. await page.setViewportSize({width:1440,height:1000});
  24. await page.locator('#admin-sidebar a[href="/admin/support-requests"]').last().click();await page.waitForTimeout(500);
  25. const count=records.length,at=performance.now();await page.locator('#admin-sidebar a[href="/admin/'+pagePath+'"]').last().click();await page.locator(ready).first().waitFor({state:'visible'});await page.evaluate(()=>new Promise(r=>requestAnimationFrame(()=>requestAnimationFrame(r))));
  26. menuReturn={ms:Math.round(performance.now()-at),primaryRequests:records.length-count};
  27. }
  28. return{operation,pair,variant,ms:readyMs,requests:records,menuReturn,errors};
  29. }finally{await ctx.close();}
  30. }