Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 

81 Zeilen
7.0 KiB

  1. import fs from 'node:fs';
  2. import path from 'node:path';
  3. import assert from 'node:assert/strict';
  4. import {chromium,expect} from '@playwright/test';
  5. import {login,call} from './role-permissions-live.mjs';
  6. const dir='reports/bridge-content-authoring-20260924';
  7. const fixture=JSON.parse(fs.readFileSync(dir+'/fixture.json','utf8'));
  8. const signed=await login('root'),checks=[],errors=[];
  9. const api=async ep=>{const r=await call('tran/v1/'+ep,signed.accessToken);assert.equal(r.status,200,ep);return r.data;};
  10. const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']});
  11. const page=await browser.newPage({viewport:{width:Number(process.env.VIEW_WIDTH)||1440,height:Number(process.env.VIEW_HEIGHT)||1000}});
  12. page.on('pageerror',e=>errors.push(e.message));
  13. await page.addInitScript(({s,theme})=>{sessionStorage.setItem('unreal-tran:web:access-token:v1',s.accessToken);sessionStorage.setItem('unreal-tran:web:refresh-token:v1',s.refreshToken);if(theme)localStorage.setItem('unreal-tran:web:preferences:v1',JSON.stringify({themeMode:theme}));},{s:signed,theme:process.env.THEME});
  14. const base=process.env.BASE_URL||'http://127.0.0.1:6180';
  15. const screenshot=name=>page.screenshot({path:`${dir}/${process.env.IMAGE_PREFIX||''}${name}.png`});
  16. const ready=async()=>{await expect(page.getByRole('button',{name:'播放动作',exact:true})).toBeEnabled({timeout:120000});};
  17. try{
  18. for(const item of process.env.FLOW_ONLY==='1'?[]:fixture.items){
  19. await page.goto(base+'/content/models/'+item.modelProjectId+'/production');await ready();
  20. await expect(page.locator('.production-action-item')).toHaveCount(item.actions);
  21. const dimensions=await page.locator('.production-preview').evaluate(host=>({width:host.clientWidth,canvas:host.querySelector('canvas').clientWidth,height:host.clientHeight}));
  22. assert.ok(Math.abs(dimensions.width-dimensions.canvas)<5,JSON.stringify(dimensions));
  23. await screenshot(`${item.taskCode}-model`);
  24. const candidates=item.taskCode.endsWith('005')?[1,2,4,5]:item.taskCode.endsWith('006')?[2,4,8]:item.taskCode.endsWith('007')?[3,7,11]:[2,5,35];
  25. for(const index of candidates){
  26. await page.locator('.production-action-item').nth(index).click();
  27. await page.getByRole('button',{name:'播放动作',exact:true}).click();await ready();
  28. await screenshot(`${item.taskCode}-action-${index+1}`);
  29. }
  30. checks.push({task:item.taskCode,stage:'model',actions:item.actions,played:candidates.map(i=>i+1),pass:true});
  31. await page.locator('.production-chain button').nth(1).click();await ready();
  32. await expect(page.locator('.production-object')).toHaveCount(item.taskCode.endsWith('005')?2:item.taskCode.endsWith('008')?3:4);
  33. await screenshot(`${item.taskCode}-scene`);
  34. checks.push({task:item.taskCode,stage:'scene',pass:true});
  35. await page.locator('.production-chain button').nth(2).click();await ready();
  36. await page.getByRole('tab',{name:'背景与讲解'}).click();
  37. await screenshot(`${item.taskCode}-training`);
  38. checks.push({task:item.taskCode,stage:'training',pass:true});
  39. if(process.env.CAPTURE_ONLY==='1')break;
  40. }
  41. if(process.env.CAPTURE_ONLY!=='1'){
  42. const item=fixture.items[0];
  43. await page.goto(base+'/content/models/'+item.modelProjectId+'/production');await ready();
  44. await page.locator('.production-action-item').nth(2).click();
  45. const angle=page.locator('.el-form-item').filter({hasText:'旋转角度(°)'}).getByRole('spinbutton');
  46. await angle.fill('75');await angle.press('Tab');
  47. await page.getByRole('button',{name:'播放动作',exact:true}).click();await ready();
  48. await page.getByRole('button',{name:'保存',exact:true}).click();
  49. await expect(page.getByText('已保存,可刷新继续编辑')).toBeVisible();
  50. await page.reload();await ready();await page.locator('.production-action-item').nth(2).click();await expect(angle).toHaveValue('75');
  51. const changed=await api('content/projects/'+item.modelProjectId);assert.equal(changed.currentVersion.content.productionActions[2].motion.angle,75);
  52. await angle.fill('90');await angle.press('Tab');
  53. await page.getByRole('button',{name:'发布并进入场景'}).click();await expect(page).toHaveURL(new RegExp('/scenes/'+item.sceneProjectId+'/production'));await ready();
  54. await page.getByRole('button',{name:'发布并进入编排'}).click();await expect(page).toHaveURL(new RegExp('/training-projects/'+item.trainingProjectId+'/production'));await ready();
  55. await page.getByRole('button',{name:'发布训练内容'}).click();await expect(page.getByText('训练内容已发布;现有仿真仍保持固定版本')).toBeVisible();
  56. checks.push({name:'005 90→75 保存刷新→90 发布模型→场景→编排',pass:true});
  57. const model=await api('content/projects/'+item.modelProjectId),scene=await api('content/projects/'+item.sceneProjectId),training=await api('content/projects/'+item.trainingProjectId);
  58. assert.equal(scene.currentVersion.dependencies[0].targetVersionId,model.project.publishedVersionId);
  59. assert.equal(training.currentVersion.dependencies[0].targetVersionId,scene.project.publishedVersionId);
  60. assert.equal(training.currentVersion.content.steps[2].modelActions[0].motion.angle,90);
  61. assert.equal(training.currentVersion.content.steps[2].modelActions[0].modelVersionId,model.project.publishedVersionId);
  62. await page.locator('.production-chain button').nth(3).click();
  63. await expect(page).toHaveURL(new RegExp('/virtual-training/preview/'+item.taskId));
  64. await expect(page.getByRole('button',{name:'跳过讲解',exact:true})).toBeVisible({timeout:120000});
  65. await screenshot('existing-005-preview');
  66. checks.push({name:'制作链进入原005背景与讲解',pass:true});
  67. for(const list of ['models','scenes','training-projects']){
  68. await page.goto(base+'/content/'+list);await expect(page.getByText(/重型支援桥液压泵应急切换/).first()).toBeVisible({timeout:30000});
  69. await screenshot(`list-${list}`);
  70. const row=page.locator(list==='training-projects'?'.training-project-card':'.asset-row-list article').filter({hasText:'重型支援桥液压泵应急切换'}).first();
  71. await row.getByRole('button',{name:list==='training-projects'?'编辑':'进入编辑',exact:true}).click();await ready();
  72. await expect(page).toHaveURL(/\/production$/);
  73. }
  74. for(const id of [86,87,88,89,116]){const before=JSON.parse(fs.readFileSync(`${dir}/before-task-${id}.json`,'utf8')),after=await api('teaching/assignments/'+id);delete before.serverNow;delete after.serverNow;assert.deepEqual(after,before);}
  75. checks.push({name:'原 005~008 及 005 独立入口固定定义完全未变',pass:true});
  76. }
  77. assert.deepEqual(errors,[]);
  78. fs.writeFileSync(dir+'/'+(process.env.RESULT_FILE||'ui-result.json'),JSON.stringify({pass:true,base,checks,errors},null,2));
  79. console.log(JSON.stringify({pass:true,checks:checks.length}));
  80. }catch(e){await page.screenshot({path:dir+'/failure.png'}).catch(()=>{});fs.writeFileSync(dir+'/failure.json',JSON.stringify({pass:false,message:e.message,checks,errors},null,2));throw e;}finally{await browser.close();}