Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer, web workflow engine and protocol, tooling, docs and task records. Large generated evidence, user-testing archives, build snapshots and imported test assets remain untracked. Co-Authored-By: Claude Code <noreply@anthropic.com>
48 lines
5.1 KiB
TypeScript
48 lines
5.1 KiB
TypeScript
import {expect,test} from '@playwright/test';
|
|
import path from 'node:path';
|
|
|
|
const fixture=path.resolve(import.meta.dirname,'../../app/public/robot-assets/abb-irb120/start-scene.blend');
|
|
test('desktop header menus add, undo, rename and export a real Main scene',async({page},testInfo)=>{
|
|
await page.goto('/');await page.getByTestId('blend-file-input').setInputFiles(fixture);
|
|
await expect(page.getByTestId('engine-status')).toContainText('SceneIR');
|
|
await expect(page.getByRole('button',{name:'添加立方体',exact:true})).not.toBeVisible();
|
|
const menu=async(label:string,...items:string[])=>{await page.getByRole('button',{name:label,exact:true}).click();for(const name of items)await page.getByRole('menuitem',{name,exact:true}).click();};
|
|
await menu('添加','网格','立方体');await expect(page.getByTestId('scene-stats')).toContainText('Objects 4');
|
|
await menu('编辑','撤销');await expect(page.getByTestId('scene-stats')).toContainText('Objects 3');
|
|
await menu('编辑','重做');await expect(page.getByTestId('scene-stats')).toContainText('Objects 4');
|
|
await menu('物体','重命名');await page.getByLabel('新对象名称').fill('Menu Regression Cube');await page.getByRole('button',{name:'确认',exact:true}).click();
|
|
await expect(page.getByRole('treeitem',{name:'Menu Regression Cube',exact:true})).toBeVisible();
|
|
const download=page.waitForEvent('download');
|
|
await menu('文件','导出','glTF 2.0 (.glb)');
|
|
await expect(page.getByTestId('engine-status')).toContainText('GLB:');
|
|
if((await page.getByTestId('engine-status').textContent())?.includes('blocked')){
|
|
const diagnostic=page.waitForEvent('download');await page.getByTestId('export-diagnostics').click();const file=await diagnostic;await testInfo.attach('export-diagnostic',{path:(await file.path())!,contentType:'application/json'});
|
|
}
|
|
await expect(page.getByTestId('engine-status')).toContainText('GLB: exported');
|
|
const file=await download;expect(file.suggestedFilename()).toBe('blender-web.glb');
|
|
await testInfo.attach('edited-menu-scene.glb',{path:(await file.path())!,contentType:'model/gltf-binary'});
|
|
});
|
|
|
|
test('desktop mode menu gates object types and changes mesh editor menus',async({page})=>{
|
|
await page.goto('/');await page.getByTestId('blend-file-input').setInputFiles(fixture);await expect(page.getByTestId('engine-status')).toContainText('SceneIR');
|
|
await page.getByRole('treeitem',{name:'ParityCamera',exact:true}).click();await page.getByRole('button',{name:'Object Mode',exact:true}).click();await expect(page.getByRole('menuitem',{name:'编辑模式',exact:true})).toBeDisabled();await page.keyboard.press('Escape');
|
|
await page.getByRole('treeitem',{name:'ParityCube',exact:true}).click();await page.getByRole('button',{name:'Object Mode',exact:true}).click();await page.getByRole('menuitem',{name:'编辑模式',exact:true}).click();
|
|
await expect(page.getByRole('button',{name:'面',exact:true})).toBeVisible();await page.getByRole('button',{name:'面选择',exact:true}).click();
|
|
await page.getByRole('button',{name:'选择',exact:true}).click();await page.getByRole('menuitem',{name:'全部',exact:true}).click();
|
|
await page.getByRole('button',{name:'面',exact:true}).click();await page.getByRole('menuitem',{name:'三角化',exact:true}).click();await expect(page.getByTestId('scene-stats')).toContainText('Faces 12');
|
|
});
|
|
|
|
test('desktop menus stay reachable on mobile and unsupported commands stay disabled',async({page})=>{
|
|
await page.setViewportSize({width:390,height:844});await page.goto('/');await expect(page.getByTestId('engine-status')).toContainText('ready');
|
|
await page.getByRole('button',{name:'文件',exact:true}).click();await expect(page.getByRole('menuitem',{name:'保存',exact:true})).toBeDisabled();await page.getByRole('menuitem',{name:'导出',exact:true}).click();
|
|
const panel=page.getByRole('menu',{name:'导出',exact:true});await expect(panel.getByRole('menuitem',{name:'FBX (.fbx)',exact:true})).toBeDisabled();const rect=await panel.boundingBox();expect(rect!.x).toBeGreaterThanOrEqual(0);expect(rect!.x+rect!.width).toBeLessThanOrEqual(390);
|
|
await page.keyboard.press('Escape');await expect(page.getByRole('button',{name:'文件',exact:true})).toBeFocused();
|
|
});
|
|
|
|
test('render menu executes scene-engine image rendering and preserves explicit range limits',async({page})=>{
|
|
test.setTimeout(120000);await page.goto('/');await page.getByTestId('blend-file-input').setInputFiles(fixture);await expect(page.getByTestId('engine-status')).toContainText('SceneIR');
|
|
await page.getByRole('button',{name:'渲染',exact:true}).click();await expect(page.getByRole('menuitem',{name:'导出 GLB',exact:true})).toHaveCount(0);await page.getByRole('menuitem',{name:'渲染图像',exact:true}).click();
|
|
await expect(page.locator('.native-render-panel .native-workflow-output')).toContainText('SUCCEEDED',{timeout:90000});await expect(page.locator('.native-render-panel img')).toBeVisible();
|
|
await page.getByRole('button',{name:'渲染',exact:true}).click();await page.getByRole('menuitem',{name:'渲染动画',exact:true}).click();await expect(page.locator('.native-render-panel .native-workflow-output')).toContainText('超过当前单次 240 帧上限');
|
|
});
|