You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

70 line
5.7 KiB

  1. import { chromium, expect } from '@playwright/test';
  2. import fs from 'node:fs';
  3. import assert from 'node:assert/strict';
  4. import crypto from 'node:crypto';
  5. import { login, call } from './role-permissions-live.mjs';
  6. import { scripts, scriptDefinition } from '../../unreal_tran_web/src/features/virtual-training-scripts/definition.js';
  7. import { verifyVisualEffects } from './bridge-three-dimensional-assertions.mjs';
  8. const dir='reports/bridge-three-dimensional-20260913';fs.mkdirSync(dir,{recursive:true});
  9. const session=await login('root'), fixtures=[], checks=[], errors=[];
  10. const api=async(path,data,method)=>{const r=await call(path,session.accessToken,data,method);assert.equal(r.status,200,`${path}: ${r.message}`);return r.data;};
  11. const fixtureFile=`${dir}/fixtures-${Date.now()}.json`;
  12. const saveFixtures=()=>fs.writeFileSync(fixtureFile,JSON.stringify(fixtures,null,2));
  13. const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']});
  14. const page=await browser.newPage({viewport:{width:1600,height:1000}});
  15. await page.addInitScript(s=>sessionStorage.setItem('unreal-tran:web:access-token:v1',s.accessToken),session);
  16. page.on('pageerror',e=>errors.push(e.message));
  17. const waitReady=async()=>{await expect(page.locator('[data-role="loading"]')).toHaveClass(/is-hidden/,{timeout:120000});await page.waitForFunction(()=>globalThis.__trainingSceneDiagnostics()?.bridgeWorkshop?.targets.length>0);await page.waitForTimeout(1800);};
  18. try {
  19. for(const script of scripts.filter(s=>s.version==='3' && (!process.env.BRIDGE_CODE || process.env.BRIDGE_CODE===s.code))) {
  20. const content=await api('tran/v1/content/projects',{type:'TRAINING',name:`三维验收·${script.title}`,description:'Temporary 3D acceptance fixture',categoryCode:'MAINTENANCE',trainingMode:'VIRTUAL',content:scriptDefinition(script),dependencies:[],assets:[]});
  21. const fixture={code:script.code,projectId:content.project.id};fixtures.push(fixture);saveFixtures();
  22. const project=(await api(`tran/v1/content/projects/${fixture.projectId}/publish`,{version:content.project.version})).project;
  23. let task=await api('tran/v1/teaching/assignments',{code:`CHECK-3D-${Date.now()}-${script.code}`,name:`三维验收·${script.title}`,description:'Temporary acceptance fixture',channel:'VIRTUAL',assignmentKind:'TRAINING',executionMode:'PRACTICE',audienceType:'COMMON',collaborationMode:'INDIVIDUAL',digitalHumanAllowed:false,contentProjectId:project.id,contentVersionId:project.publishedVersionId,members:[]});
  24. fixture.taskId=task.id;saveFixtures();
  25. task=await api(`tran/v1/teaching/assignments/${task.id}/publish`,{version:task.version});
  26. await page.goto(`http://127.0.0.1:6180/teaching/virtual-training/preview/${task.id}`);await waitReady();
  27. await expect(page.locator('.bridge-procedure')).toHaveCount(0);
  28. const bar=page.locator('[data-role="model-commandbar"]');
  29. for(const action of ['toggle-structure-tree','focus-selected-part','viewer-reset-view','toggle-part-labels','toggle-issue-highlight','toggle-hide-others','toggle-explosion'])await expect(bar.locator(`[data-action="${action}"]`)).toBeVisible();
  30. await page.screenshot({path:`${dir}/${script.code}-start.png`});
  31. fs.writeFileSync(`${dir}/${script.code}-diagnostic.json`,JSON.stringify(await page.evaluate(()=>globalThis.__trainingSceneDiagnostics()),null,2));
  32. for(let index=0;index<script.definition.steps.length;index++) {
  33. const step=script.definition.steps[index];
  34. await expect(page.locator('[data-role="step-count"]')).toHaveText(`${index+1} / ${script.steps.length}`);
  35. if(step.mode==='measure'){
  36. await page.locator('[data-open-preparation]').click();
  37. await page.locator('[data-measurement]').fill('按原稿连接管路,矛盾描述交教员核对。');
  38. await page.locator('[data-close-preparation]').click();
  39. }
  40. for(let k=0;k<step.action.mechanism.steps.length;k++) {
  41. const op=step.action.mechanism.steps[k];
  42. await expect.poll(async()=>page.evaluate(()=>globalThis.__trainingSceneDiagnostics().bridgeWorkshop.operationIndex)).toBe(k);
  43. if(k===step.action.mechanism.steps.length-1){
  44. const response=page.waitForResponse(r=>r.url().includes(`/assignments/${task.id}/preview/validate`));
  45. await page.locator('[data-role="part-label"]').click();
  46. assert.equal((await response).status(),200);
  47. }else{
  48. await page.locator('[data-role="part-label"]').click();
  49. await expect.poll(async()=>page.evaluate(()=>globalThis.__trainingSceneDiagnostics().bridgeWorkshop.operationIndex)).toBe(k+1);
  50. }
  51. await verifyVisualEffects(page,op);
  52. console.log(script.code,index+1,k+1,op.targetId);
  53. }
  54. await expect(page.locator('[data-action="next"]')).toBeEnabled();
  55. if([1,3,4,7,10,15].includes(index))await page.screenshot({path:`${dir}/${script.code}-step-${index+1}.png`});
  56. if(index<script.steps.length-1)await page.locator('[data-action="next"]').click();
  57. }
  58. assert.equal((await api(`tran/v1/teaching/runs?assignmentId=${task.id}&size=100`)).total,0);
  59. checks.push(`${script.code}: ${script.steps.length} steps, ${script.definition.steps.flatMap(s=>s.action.mechanism.steps).length} 3D operations; preview server validation passed`);
  60. fixture.passed=true;saveFixtures();
  61. }
  62. assert.deepEqual(errors,[]);
  63. fs.writeFileSync(`${dir}/acceptance.json`,JSON.stringify({pass:true,checks,errors},null,2));
  64. } catch(error){
  65. console.log('Feedback',await page.locator('.el-message').allTextContents());
  66. await page.screenshot({path:`${dir}/failure.png`}).catch(()=>{});
  67. fs.writeFileSync(`${dir}/acceptance.json`,JSON.stringify({pass:false,error:error.message,checks,errors},null,2));throw error;
  68. }finally{await browser.close();saveFixtures();}