94 lines
9.2 KiB
JavaScript
94 lines
9.2 KiB
JavaScript
// Isolated browser context against the designated deployed server only.
|
|
const {chromium}=require('playwright-core');
|
|
const assert=require('node:assert/strict');
|
|
const fs=require('node:fs/promises');
|
|
(async()=>{
|
|
const base=process.env.BASE_URL||'http://8.211.154.134:18131';
|
|
assert.equal(base,'http://8.211.154.134:18131');
|
|
const browser=await chromium.launch(process.env.CHROMIUM_PATH?{executablePath:process.env.CHROMIUM_PATH,headless:true,args:['--no-sandbox','--disable-dev-shm-usage']}:{channel:'chrome',headless:true});
|
|
try{
|
|
const context=await browser.newContext({viewport:{width:1600,height:1100},storageState:process.env.STORAGE_STATE||undefined});
|
|
const page=await context.newPage(),errors=[];let choosers=0;
|
|
page.on('pageerror',e=>errors.push(e.message));page.on('filechooser',()=>choosers++);
|
|
const snapshot=()=>page.evaluate(()=>JSON.parse(localStorage.getItem('studio.infinite-canvas.v1')));
|
|
const node=id=>page.locator(`[data-node-id="${id}"]`);
|
|
const content=id=>node(id).locator('[data-node-content]');
|
|
const waitField=(id,key,value)=>page.waitForFunction(({id,key,value})=>JSON.parse(localStorage.getItem('studio.infinite-canvas.v1')).cards.find(n=>n.id===id)?.[key]===value,{id,key,value});
|
|
const composer=page.getByRole('region',{name:'节点编辑器'});
|
|
const close=(a,b)=>assert.ok(Math.abs(a-b)<2,`${a} ≈ ${b}`);
|
|
async function drag(handle,dx,dy){const b=await handle.boundingBox();assert.ok(b);await page.mouse.move(b.x+b.width/2,b.y+b.height/2);await page.mouse.down();await page.mouse.move(b.x+b.width/2+dx,b.y+b.height/2+dy,{steps:12});await page.mouse.up()}
|
|
await page.goto(base+'/infinite_canvas');
|
|
await page.getByRole('button',{name:'从示例开始 ↗',exact:true}).click();
|
|
await page.getByRole('button',{name:'适应画布',exact:true}).click();
|
|
await content('brief').click({position:{x:20,y:20}});
|
|
assert.equal(await node('brief').getAttribute('data-selected'),'true');assert.equal(await node('brief').getAttribute('data-editing'),'false');
|
|
await page.waitForTimeout(700);
|
|
await content('brief').click({position:{x:20,y:20}});
|
|
await node('brief').getByRole('textbox',{name:'01 / 创意简报 内容'}).fill('选择后再次点击进入编辑');
|
|
await waitField('brief','text','选择后再次点击进入编辑');
|
|
await node('brief').getByRole('button',{name:'加粗',exact:true}).click();
|
|
assert.equal((await snapshot()).cards.find(n=>n.id==='brief').style.bold,true);
|
|
await page.keyboard.press('Escape');
|
|
const before=(await snapshot()).cards.find(n=>n.id==='brief');
|
|
const z=await page.locator('[class*=world]').evaluate(el=>new DOMMatrix(getComputedStyle(el).transform).a);
|
|
await drag(content('brief'),45,25);
|
|
const after=(await snapshot()).cards.find(n=>n.id==='brief');close(after.x-before.x,45/z);close(after.y-before.y,25/z);
|
|
assert.equal(await node('brief').getAttribute('data-editing'),'false');
|
|
|
|
await content('reference').click();assert.equal(await composer.count(),0);
|
|
await page.waitForTimeout(700);await content('reference').click();await composer.waitFor();assert.equal(choosers,0,'Entering image editing must not force a file picker');
|
|
const panelBefore=await composer.boundingBox(),imageBefore=await node('reference').boundingBox();
|
|
await page.getByRole('button',{name:'缩小',exact:true}).click();
|
|
const panelAfter=await composer.boundingBox(),imageAfter=await node('reference').boundingBox();
|
|
close(panelBefore.width,panelAfter.width);close(panelBefore.height,panelAfter.height);assert.ok(imageAfter.width<imageBefore.width);
|
|
const chooser=page.waitForEvent('filechooser');await composer.getByRole('button',{name:'上传图片',exact:true}).click();
|
|
await (await chooser).setFiles({name:'reference.png',mimeType:'image/png',buffer:Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=','base64')});
|
|
await page.waitForFunction(()=>!!JSON.parse(localStorage.getItem('studio.infinite-canvas.v1')).cards.find(n=>n.id==='reference').image);
|
|
await node('reference').getByRole('button',{name:'图片高清 ↗',exact:true}).click();
|
|
const upscale=(await snapshot()).cards.find(n=>n.operation==='upscale');assert.ok(upscale);
|
|
const download=page.waitForEvent('download');await composer.getByRole('button',{name:'导出任务',exact:true}).click();
|
|
const task=JSON.parse(await fs.readFile(await (await download).path(),'utf8'));
|
|
assert.equal(task.target.id,upscale.id);assert.equal(task.references[0].id,'reference');assert.ok(task.references[0].image);
|
|
|
|
await page.keyboard.press('Escape');await page.getByRole('button',{name:'适应画布',exact:true}).click();
|
|
await content('brief').click({position:{x:20,y:20}});
|
|
await node('brief').getByRole('button',{name:'引用 01 / 创意简报 生成',exact:true}).click();
|
|
await page.getByRole('dialog',{name:'画布操作'}).getByRole('button',{name:'脚本',exact:true}).click();
|
|
const script=(await snapshot()).cards.find(n=>n.kind==='script');assert.ok(script);
|
|
await composer.getByRole('textbox',{name:'创作要求',exact:true}).fill('基于简报写六个分镜');
|
|
await waitField(script.id,'instruction','基于简报写六个分镜');assert.equal((await snapshot()).cards.find(n=>n.id===script.id).text,'');
|
|
await composer.getByRole('button',{name:'画布参考',exact:true}).click();
|
|
await composer.getByRole('group',{name:'选择引用节点'}).getByRole('button',{name:'02 / 视觉参考',exact:true}).click();
|
|
assert.ok((await snapshot()).links.some(([a,b])=>a==='reference'&&b===script.id));
|
|
await composer.getByRole('button',{name:'移除引用 01 / 创意简报',exact:true}).click();
|
|
assert.ok(!(await snapshot()).links.some(([a,b])=>a==='brief'&&b===script.id));
|
|
await page.keyboard.press('Escape');
|
|
await page.getByRole('button',{name:'适应画布',exact:true}).click();
|
|
await content('reference').click();await node('reference').getByRole('button',{name:'02 / 视觉参考 操作',exact:true}).click();
|
|
await page.getByRole('button',{name:'删除节点',exact:true}).click();
|
|
assert.ok((await snapshot()).cards.some(n=>n.id===script.id));assert.ok((await snapshot()).links.every(l=>!l.includes('reference')));
|
|
await page.getByRole('button',{name:'撤销',exact:true}).click();assert.ok((await snapshot()).links.some(([a,b])=>a==='reference'&&b===script.id));
|
|
await page.keyboard.press('Escape');
|
|
await content('brief').click({position:{x:20,y:20}});
|
|
const priorWidth=(await snapshot()).cards.find(n=>n.id==='brief').width;
|
|
await drag(node('brief').getByRole('button',{name:'调整 01 / 创意简报 尺寸',exact:true}),50,30);
|
|
assert.ok((await snapshot()).cards.find(n=>n.id==='brief').width>(priorWidth||320));
|
|
await page.getByRole('button',{name:'撤销',exact:true}).click();assert.equal((await snapshot()).cards.find(n=>n.id==='brief').width,priorWidth);
|
|
await page.getByRole('button',{name:'重做',exact:true}).click();
|
|
await page.getByRole('button',{name:'移动画布',exact:true}).click();
|
|
await page.mouse.move(15,300);await page.mouse.down();await page.mouse.move(75,340,{steps:8});await page.mouse.up();
|
|
assert.equal(await page.evaluate(()=>{const path=[...document.querySelectorAll('svg path')].find(p=>p.getAttribute('d')?.startsWith('M '));const d=path.getAttribute('d').match(/-?\d+(?:\.\d+)?/g).map(Number),point=new DOMPoint(d[0],d[1]).matrixTransform(path.getScreenCTM()),card=document.querySelector('[data-node-id="brief"]').getBoundingClientRect();return Math.abs(point.x-card.right)<3&&Math.abs(point.y-(card.top+card.height/2))<3}),true);
|
|
const worldAt=()=>page.locator('[class*=world]').evaluate(el=>{const m=new DOMMatrix(getComputedStyle(el).transform);return {x:(1450-m.e)/m.a,y:(150-m.f)/m.d,z:m.a}});
|
|
const anchorBefore=await worldAt();await page.mouse.move(1450,150);await page.keyboard.down('Control');await page.mouse.wheel(0,-80);await page.keyboard.up('Control');
|
|
await page.waitForFunction(z=>new DOMMatrix(getComputedStyle(document.querySelector('[class*=world]')).transform).a!==z,anchorBefore.z);
|
|
const anchorAfter=await worldAt();close(anchorBefore.x,anchorAfter.x);close(anchorBefore.y,anchorAfter.y);
|
|
const saved=await snapshot();await page.reload();await page.waitForFunction(()=>!!localStorage.getItem('studio.infinite-canvas.v1'));assert.deepEqual(await snapshot(),saved);
|
|
await page.getByRole('button',{name:'切换明暗主题',exact:true}).click();await page.reload();await page.waitForFunction(()=>document.querySelector('main').dataset.theme==='light');
|
|
if(process.env.SCREENSHOT_DIR){await fs.mkdir(process.env.SCREENSHOT_DIR,{recursive:true});await page.screenshot({path:process.env.SCREENSHOT_DIR+'/canvas-nodes-desktop.png'});}
|
|
await page.setViewportSize({width:390,height:844});assert.ok(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth));
|
|
const viewport=await page.locator('[class*=viewport]').boundingBox();assert.equal(viewport.width,390);assert.equal(viewport.height,844);
|
|
if(process.env.SCREENSHOT_DIR)await page.screenshot({path:process.env.SCREENSHOT_DIR+'/canvas-nodes-mobile.png'});
|
|
assert.deepEqual(errors,[]);console.log('Passed: selected-node re-click editing, drag, formatting, fixed composer, upload, references, task export, deletion/undo, reload and mobile bounds');
|
|
}finally{await browser.close()}
|
|
})().catch(e=>{console.error(e);process.exitCode=1});
|