import fs from 'node:fs'; import path from 'node:path'; import assert from 'node:assert/strict'; import {chromium,expect} from '@playwright/test'; import {login,call} from './role-permissions-live.mjs'; const dir='reports/bridge-introduction-20260918',signed=await login('root'); const api=async(endpoint,body,method)=>{const r=await call('tran/v1/'+endpoint,signed.accessToken,body,method);assert.equal(r.status,200,endpoint+': '+r.message);return r.data;}; const original=await api('content/projects/458'); let fixture=await api('content/projects/458/copy',{version:original.project.version,code:'INTRO-UPLOAD-'+Date.now(),name:'讲解上传验收临时工程'}),pass=false; const id=fixture.project.id; const browser=await chromium.launch({channel:'msedge',headless:true,args:['--enable-unsafe-swiftshader']}); const page=await browser.newPage({viewport:{width:1366,height:768}}); await page.addInitScript(s=>{sessionStorage.setItem('unreal-tran:web:access-token:v1',s.accessToken);sessionStorage.setItem('unreal-tran:web:refresh-token:v1',s.refreshToken);},signed); try{ await page.goto('http://127.0.0.1:6180/content/training-projects/'+id+'/legacy'); await expect(page.locator('[data-action="save"]')).toBeVisible({timeout:120000}); await page.getByRole('button',{name:'背景与前置讲解',exact:true}).click(); const dialog=page.getByRole('dialog',{name:'背景与前置讲解'}); await dialog.locator('input[type="file"]').setInputFiles('../unreal_tran_web/public/models/bridge-training/bridge-pump-switch-v2.jpg'); await expect(dialog.locator('button').filter({hasText:'上传背景图'})).not.toHaveClass(/is-loading/,{timeout:60000}); await dialog.getByRole('button',{name:/讲解1 ·/}).click(); await dialog.getByText('视频',{exact:true}).click(); const videos='../unreal_tran_web/public/legacy/virtual-training/videos/narration/bridge-pump-switch/v2/instructor-1-v2'; const file=fs.readdirSync(videos).find(f=>f.endsWith('.mp4'));assert.ok(file); await dialog.locator('input[type="file"]').setInputFiles(path.join(videos,file)); await expect(dialog.locator('button').filter({hasText:'上传讲解视频'})).not.toHaveClass(/is-loading/,{timeout:60000}); await dialog.getByRole('button',{name:'应用到编排',exact:true}).click(); const saved=page.waitForResponse(r=>r.url().includes('/content/projects/'+id)&&r.request().method()==='PUT'); await page.locator('[data-action="save"]').click();assert.equal((await saved).status(),200); fixture=await api('content/projects/'+id);const intro=fixture.currentVersion.content.introduction; assert.ok(intro.background.imageAssetCode.startsWith('INTRO-'));assert.ok(intro.lectures[0].videoAssetCode.startsWith('INTRO-')); assert.equal(intro.lectures[0].format,'video'); for(const [code,type]of [[intro.background.imageAssetCode,'IMAGE'],[intro.lectures[0].videoAssetCode,'VIDEO']]){const asset=fixture.currentVersion.assets.find(a=>a.code===code);assert.equal(asset.type,type);assert.equal(asset.status,'READY');} fixture=await api('content/projects/'+id+'/publish',{version:fixture.project.version});assert.equal(fixture.project.status,'PUBLISHED'); await page.reload();await expect(page.locator('[data-action="save"]')).toBeVisible({timeout:120000});await page.getByRole('button',{name:'背景与前置讲解',exact:true}).click(); await dialog.getByRole('button',{name:/讲解1 ·/}).click();await expect(dialog.locator('button').filter({hasText:'上传讲解视频'})).toBeVisible(); await page.screenshot({path:dir+'/authoring-uploaded-video.png'});pass=true; }catch(error){await page.screenshot({path:dir+'/upload-failure.png'});throw error;} finally{ await browser.close();fixture=await api('content/projects/'+id); if(fixture.project.status==='PUBLISHED')fixture=await api('content/projects/'+id+'/review',{version:fixture.project.version,action:'REVOKE',comment:'讲解上传验收结束,清理临时工程'}); await api('content/projects/'+id+'?version='+fixture.project.version,undefined,'DELETE'); const deleted=await call('tran/v1/content/projects/'+id,signed.accessToken);assert.equal(deleted.status,404); fs.writeFileSync(dir+'/upload-result.json',JSON.stringify({pass,projectId:id,uploadedImage:true,uploadedVideo:true,published:pass,softDeleted:true},null,2)); } console.log('UPLOAD/PUBLISH/CLEANUP PASS',id);