Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 

53 rindas
5.2 KiB

  1. import fs from 'node:fs';
  2. import assert from 'node:assert/strict';
  3. import { chromium, expect } from '@playwright/test';
  4. import { login, call } from './role-permissions-live.mjs';
  5. import { scripts, scriptDefinition } from '../../unreal_tran_web/src/features/virtual-training-scripts/definition.js';
  6. const dir='reports/bridge-grounding-20260914';fs.mkdirSync(dir,{recursive:true});
  7. const session=await login('root'),checks=[],errors=[],writes=[];
  8. const api=async path=>{const r=await call('tran/v1/'+path,session.accessToken);assert.equal(r.status,200);return r.data;};
  9. const before=await api('teaching/assignments?channel=VIRTUAL&assignmentKind=TRAINING&size=100');
  10. const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']});
  11. const page=await browser.newPage({viewport:{width:1920,height:1000}});
  12. await page.addInitScript(s=>{sessionStorage.setItem('unreal-tran:web:access-token:v1',s.accessToken);localStorage.setItem('unreal-tran:web:preferences:v1',JSON.stringify({themeMode:'military'}));},session);
  13. page.on('pageerror',e=>errors.push(e.message));
  14. page.on('request',r=>{if(r.method()!=='GET'&&r.url().includes('/api/tran/'))writes.push(r.url());});
  15. const diag=()=>page.evaluate(()=>globalThis.__trainingSceneDiagnostics());
  16. const ready=async()=>{await expect(page.locator('[data-role="loading"]')).toHaveClass(/is-hidden/,{timeout:120000});await page.waitForTimeout(1300);};
  17. const verify=d=>{
  18. assert.ok(d.bridgeGrounding?.length);assert.ok(d.framing.environmentAlignment.workArea.changedVertices>0);
  19. for(const p of d.bridgeGrounding){assert.ok(Math.abs(p.minGap)<1e-6);assert.ok(p.maxGap<0.03,JSON.stringify(p));}
  20. const vehicle=d.framing.sceneObjects[1];assert.ok(Math.abs(vehicle.min[1])<0.03,JSON.stringify(vehicle));return vehicle;
  21. };
  22. try{
  23. for(const id of [86,87,88,89]){
  24. const task=before.records.find(t=>Number(t.id)===id),script=scripts.find(s=>s.code===task.code);
  25. const isolated=task.definitionSnapshot.runtime.scriptVersion!==script.version;
  26. // Older published tasks remain untouched: only this browser's GET response is replaced for rendering QA.
  27. if(isolated)await page.route(`**/api/tran/v1/teaching/assignments/${id}`,async route=>{
  28. const response=await route.fetch(),body=await response.json();body.data.definitionSnapshot=scriptDefinition(script);await route.fulfill({response,json:body});
  29. });
  30. await page.goto(`http://127.0.0.1:6180/teaching/virtual-training/preview/${id}`);await ready();
  31. const initial=await diag(),pose=verify(initial);
  32. await page.locator('[data-action="viewer-reset-view"]').click();await page.waitForTimeout(700);
  33. assert.deepEqual(verify(await diag()),pose);
  34. await page.screenshot({path:`${dir}/${task.code}.png`});
  35. if(!isolated){
  36. const response=page.waitForResponse(r=>r.url().includes('/preview/validate'));
  37. await page.locator('[data-role="part-label"]').click();assert.equal((await response).status(),200);
  38. await expect(page.locator('[data-action="next"]')).toBeEnabled();await page.locator('[data-action="next"]').click();
  39. await page.locator('[data-action="viewer-reset-view"]').click();await page.waitForTimeout(700);
  40. assert.deepEqual(verify(await diag()),pose);
  41. }
  42. await page.reload();await ready();assert.deepEqual(verify(await diag()),pose);
  43. checks.push({code:task.code,pass:true,mode:isolated?'isolated-v3-render-no-database-change':'published-task-preview',grounding:initial.bridgeGrounding});
  44. await page.unrouteAll({behavior:'wait'});
  45. }
  46. assert.deepEqual(errors,[]);assert.ok(writes.every(url=>url.endsWith('/preview/validate')));
  47. const after=await api('teaching/assignments?channel=VIRTUAL&assignmentKind=TRAINING&size=100');
  48. assert.equal(after.total,before.total);assert.deepEqual(after.records.map(t=>[t.id,t.version,t.contentVersionId]),before.records.map(t=>[t.id,t.version,t.contentVersionId]));
  49. fs.writeFileSync(`${dir}/result.json`,JSON.stringify({pass:true,checks,errors,remainingTasks:after.total,databaseWrites:'preview validation only; no task/run creation or migration'},null,2));
  50. fs.writeFileSync(`${dir}/index.html`,`<!doctype html><html lang="zh-CN"><meta charset="utf-8"><title>桥梁车辆贴地修复</title><style>body{font:16px/1.7 system-ui;background:#edf4f1;color:#123e35;margin:32px auto;max-width:1200px}section{background:white;padding:24px;margin:20px 0}img{max-width:100%}a{color:#008468}</style><section><h1>桥梁车辆贴地修复 · 验证通过</h1><p>仅对四套桥梁脚本的训练位置平整地形,按轮胎与桌脚接触点贴地;原 GLB 文件和其他场景不变。验证复位、重新加载,以及 005/008 原任务切换步骤后车辆保持地面位置。</p><p>006/007 的三维版使用浏览器内隔离渲染验证,未变更旧任务入口或已有记录。列表仍为 ${after.total} 条,没有新增测试任务。</p><p>前端构建与贴地/正式路由测试通过。<a href="result.json">原始验证结果</a></p></section>${checks.map(c=>`<section><h2>${c.code}</h2><p>${c.mode}</p><img src="${c.code}.png" alt="${c.code} 车辆落地"></section>`).join('')}</html>`);
  51. }catch(error){await page.screenshot({path:`${dir}/failure.png`}).catch(()=>{});fs.writeFileSync(`${dir}/result.json`,JSON.stringify({pass:false,error:error.message,checks,errors},null,2));throw error;}
  52. finally{await browser.close();}