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.
 
 
 
 

49 line
4.3 KiB

  1. import fs from 'node:fs'
  2. import {chromium,expect} from '@playwright/test'
  3. import {login} from './role-permissions-live.mjs'
  4. const session=await login('root'),checks=[]
  5. const browser=await chromium.launch({channel:'msedge',headless:true})
  6. const page=await browser.newPage({viewport:{width:1920,height:1080}})
  7. await page.addInitScript(s=>{sessionStorage.setItem('unreal-tran:web:access-token:v1',s.accessToken);sessionStorage.setItem('unreal-tran:web:refresh-token:v1',s.refreshToken)},session)
  8. let id
  9. try{
  10. await page.goto('http://127.0.0.1:6180/content/ofd')
  11. await expect(page.getByRole('button',{name:'新建指导书',exact:true})).toBeVisible()
  12. const result=process.env.GUIDE_MEDIA_ID ? await page.evaluate(async id=>{
  13. const {FormalGuideStore}=await import('/src/features/guide-legacy/formalStore.ts')
  14. const store=new FormalGuideStore();await store.load(id)
  15. const step=store.record.state.chapters[0].steps[0]
  16. const valid=step.mediaBlocks.every(b=>b.kind==='video'?!!b.poster:!!b.imageSource)&&!JSON.stringify(step).includes('blob:')
  17. store.dispose();return {id,valid}
  18. },process.env.GUIDE_MEDIA_ID) : await page.evaluate(async()=>{
  19. const {FormalGuideStore}=await import('/src/features/guide-legacy/formalStore.ts')
  20. const store=new FormalGuideStore();await store.createOFDProject({title:'正式图片视频资源回归-20260911'})
  21. const canvas=document.createElement('canvas');canvas.width=400;canvas.height=240
  22. const ctx=canvas.getContext('2d');ctx.fillStyle='#168a72';ctx.fillRect(0,0,400,240);ctx.fillStyle='white';ctx.font='24px sans-serif';ctx.fillText('Formal image resource',45,130)
  23. const png=await new Promise(resolve=>canvas.toBlob(resolve,'image/png'))
  24. const image=await store.assetStore.put(png,{name:'guide-image.png'})
  25. const video=await store.assetStore.put(await(await fetch('/videos/physical-training-real-main-720p.mp4')).blob(),{name:'guide-video.mp4'})
  26. const state=structuredClone(store.record.state)
  27. state.resources=[{...image,id:image.assetKey,name:'guide-image.png',type:'图片',category:'image',resourceUrl:`asset://${image.assetKey}`,blob:undefined},{...video,id:video.assetKey,name:'guide-video.mp4',type:'视频',category:'video',resourceUrl:`asset://${video.assetKey}`,blob:undefined}]
  28. const step=state.chapters[0].steps[0];step.image=`asset://${image.assetKey}`
  29. step.mediaBlocks=[{id:'image-block',kind:'image',imageSource:`asset://${image.assetKey}`,x:120,y:62,width:72,height:58},{id:'video-block',kind:'video',assetId:video.assetKey,assetName:'guide-video.mp4',poster:canvas.toDataURL('image/png'),x:120,y:130,width:72,height:58}]
  30. await store.saveOFDProjectState(store.project.id,state)
  31. const id=store.project.id;store.dispose();const loaded=new FormalGuideStore();await loaded.load(id)
  32. const saved=loaded.record.state.chapters[0].steps[0]
  33. const valid=saved.mediaBlocks.every(b=>b.kind==='video'?!!b.poster:!!b.imageSource)&&!JSON.stringify(saved).includes('blob:')
  34. loaded.dispose();return {id,valid}
  35. })
  36. id=result.id;checks.push({name:'图片与视频上传、截图引用保存后重开保持',pass:result.valid})
  37. await page.goto(`http://127.0.0.1:6180/content/ofd/${id}/studio?view=preview`)
  38. await expect(page.locator('.ofd-preview-pager')).toBeVisible({timeout:60000})
  39. checks.push({name:'OFD 预览读取受控图片和截图',pass:!(await page.locator('.ofd-analysis-empty').allTextContents()).some(t=>t.includes('失败'))})
  40. await page.locator('[data-action="preview-tab"][data-tab="interactive"]').click()
  41. const video=page.locator('.ofd-ia-media-item.video video')
  42. await expect(video).toHaveAttribute('src',/^blob:/,{timeout:30000})
  43. await expect.poll(()=>video.evaluate(v=>v.readyState)).toBeGreaterThanOrEqual(2)
  44. await video.evaluate(v=>v.play());await expect.poll(()=>video.evaluate(v=>v.currentTime)).toBeGreaterThan(0)
  45. await video.scrollIntoViewIfNeeded();await page.screenshot({path:'reports/guide-formal-20260910/18-formal-video.png'})
  46. checks.push({name:'正式视频通过受控字节恢复并实际播放',pass:true})
  47. }catch(e){checks.push({name:'图片视频资源闭环',pass:false,detail:e.message});process.exitCode=1;await page.screenshot({path:'reports/guide-formal-20260910/media-failure.png'})}
  48. finally{console.log(checks);fs.writeFileSync('reports/guide-formal-20260910/media.json',JSON.stringify({projectId:id,checks},null,2));await browser.close()}