import { test, expect } from '@playwright/test' import { signIn, freshScene, openEditor, ready, diagnostics, action, save, shot, evidence, closeSession, call } from './scene-editor-closure-helpers' test.afterEach(async ({page})=>closeSession(page)) const setName=async(page:any,name:string)=>{const input=page.locator('[data-inspector-field="name"]');await input.fill(name);await input.press('Tab')} test('加载失败重试与加载互斥:不展示可写半场景、恢复网络后模型完整加载',async({page},info)=>{ const token=await signIn(page),copy=await freshScene(token,'fox','加载互斥'),id=copy.project.id const endpoint=`**/api/tran/v1/content/projects/${id}` await page.route(endpoint,route=>route.fulfill({status:503,contentType:'application/json',body:JSON.stringify({code:503,message:'验收模拟场景加载失败'})})) await page.goto(`/content/scenes/${id}/edit`) await expect(page.getByText('场景工程加载失败',{exact:true})).toBeVisible() await expect(page.locator('.se-shell')).toHaveCount(0) await shot(page,info,'60-scene-load-failure-retry');await page.unroute(endpoint) let release!:()=>void;const gate=new Promise(resolve=>release=resolve) await page.route('**/api/tran/v1/content/projects/*/assets/*/content',async route=>{await gate;await route.continue()}) await page.getByRole('button',{name:'重新加载',exact:true}).click() await expect(page.locator('.se-shell')).toBeVisible() await expect.poll(async()=>Boolean((await diagnostics(page))?.pendingLoads)).toBe(true) for(const name of ['new','open','save','export','verify','publish','preview'])await expect(action(page,name)).toBeDisabled() await page.getByRole('button',{name:'关闭数字场景编辑',exact:true}).click() await page.getByRole('button',{name:'关闭标签',exact:true}).click() await expect(page.locator('.el-message-box')).toHaveCount(0) await expect(page.locator('.se-shell')).toBeVisible() await shot(page,info,'61-model-loading-blocks-partial-save') release();await ready(page);await page.unroute('**/api/tran/v1/content/projects/*/assets/*/content') expect((await diagnostics(page)).objects.some((o:any)=>o.targetProjectId==='157')).toBe(true) evidence('loading-retry',{projectId:id,ready:await diagnostics(page)}) }) test('延迟保存快照:重复点击只提交一次,期间新编辑保留,旧恢复稿不会误报冲突',async({page},info)=>{ const token=await signIn(page),copy=await freshScene(token,'truck','并发保存'),id=copy.project.id await openEditor(page,id);const truck=(await diagnostics(page)).objects.find((o:any)=>o.targetProjectId==='156') await page.locator(`#se-tree [data-object-id="${truck.sceneId}"]`).click();await setName(page,'提交中的快照A') let release!:()=>void;const gate=new Promise(resolve=>release=resolve),submitted:any[]=[] const endpoint=`**/api/tran/v1/content/projects/${id}` await page.route(endpoint,async route=>{if(route.request().method()!=='PUT')return route.continue();submitted.push(route.request().postDataJSON());await gate;await route.continue()}) await action(page,'save').click();await expect.poll(()=>submitted.length).toBe(1) await expect(action(page,'save')).toBeDisabled() await page.keyboard.press('Control+s');expect(submitted.length).toBe(1) await setName(page,'提交之后的新修改B') await expect.poll(()=>page.evaluate(()=>Object.keys(localStorage).some(k=>k.includes('scene-api-recovery')))).toBe(true) release();await expect.poll(async()=>(await diagnostics(page)).saving).toBe(false) expect((await diagnostics(page)).dirty).toBe(true);expect(submitted.length).toBe(1) const first=(await call(`tran/v1/content/projects/${id}`,token)).data expect(first.currentVersion.content.objects.find((o:any)=>o.sceneId===truck.sceneId).name).toBe('提交中的快照A') await shot(page,info,'62-new-edits-preserved-during-save');await page.unroute(endpoint);await save(page) await page.reload();await ready(page) expect((await diagnostics(page)).objects.find((o:any)=>o.sceneId===truck.sceneId).name).toBe('提交之后的新修改B') await expect(page.locator('.el-message-box')).toHaveCount(0) evidence('save-concurrency',{projectId:id,firstVersion:first.project.version,submissionsBeforeRetry:submitted.length,reopened:await diagnostics(page)}) })