// 运行时让 NODE_PATH 指向随包提供的 Playwright,或使用本地安装。 const { chromium } = require('playwright') const fs = require('node:fs/promises') const path = require('node:path') ;(async () => { const output=path.resolve(process.argv[2] || '../.local-plans/phase2-completion/browser') await fs.mkdir(output,{recursive:true}) const browser=await chromium.launch({channel:'msedge',headless:true}) const results=[] for(const theme of ['light','dark','sepia','paper-moments','ocean-blue','midnight-purple']) { const page=await browser.newPage({viewport:{width:1200,height:1000}}) const errors=[];page.on('pageerror',e=>errors.push(e.message)) await page.goto(`http://127.0.0.1:5187/tests/visual/phase2.html?theme=${theme}`) await page.waitForSelector('.editor-mermaid-preview polyline',{timeout:60000}) await page.waitForSelector('.markdown-mermaid polyline',{timeout:60000}) const zoom=page.locator('.markdown-mermaid').first() await zoom.hover() await zoom.locator('[data-diagram-action="in"]').click() if(Number(await zoom.getAttribute('data-diagram-scale'))<=1) throw Error('zoom failed') await zoom.locator('[data-diagram-action="reset"]').click() await zoom.locator('[data-code-action="source"]').click() if(!(await zoom.locator('.markdown-code-source').isVisible())) throw Error('source toggle failed') await zoom.locator('[data-code-action="source"]').click() await zoom.hover() await zoom.locator('[data-diagram-action="view"]').click() await page.screenshot({path:path.join(output,`${theme}-viewer.png`)}) await page.keyboard.press('Escape') await page.screenshot({path:path.join(output,`${theme}-wide.png`),fullPage:true}) await page.setViewportSize({width:390,height:844}) await page.screenshot({path:path.join(output,`${theme}-narrow.png`),fullPage:true}) if(theme==='light') { await page.setViewportSize({width:1200,height:1000}) await page.getByRole('button',{name:'导出',exact:true}).click() for(const format of ['html','pdf','docx']) { await page.getByLabel('格式',{exact:true}).selectOption(format) await page.getByRole('button',{name:'开始导出',exact:true}).click() const row=page.locator('.export-modal > ul > li').filter({hasText:`phase2-demo.${format}`}).first() await row.getByRole('button',{name:'下载',exact:true}).waitFor({timeout:60000}) const download=page.waitForEvent('download') await row.getByRole('button',{name:'下载',exact:true}).click() await (await download).saveAs(path.join(output,`demo.${format}`)) } await page.screenshot({path:path.join(output,'export-jobs.png')}) } if(theme==='light') { await page.getByRole('button',{name:'关闭',exact:true}).click() await page.getByRole('button',{name:'源码',exact:true}).click() const input=page.getByRole('textbox',{name:'Markdown 源码编辑器'}) const original=await input.inputValue() await input.fill(original.replace('y = x^2','y = cos(x)')) await page.getByRole('button',{name:'写作',exact:true}).click() await page.waitForFunction(()=>document.querySelector('.editor-mermaid-preview svg')?.textContent.includes('cos(x)')) await page.getByRole('button',{name:'测试切换主题'}).click() await page.waitForFunction(()=>document.documentElement.dataset.theme==='dark') await page.waitForFunction(()=>document.querySelector('.editor-mermaid-preview svg rect')?.getAttribute('fill')!=='#ffffff') await page.getByRole('button',{name:'源码',exact:true}).click() await input.fill(original.replace('y = x^2','y = __import__("os")')) await page.getByRole('button',{name:'写作',exact:true}).click() await page.waitForFunction(()=>!document.querySelector('.editor-mermaid-preview polyline')&&!document.querySelector('.markdown-function-plot polyline')) await page.screenshot({path:path.join(output,'invalid-expression.png'),fullPage:true}) await page.getByRole('button',{name:'源码',exact:true}).click() await input.fill(original) await page.getByRole('button',{name:'写作',exact:true}).click() await page.waitForSelector('.editor-mermaid-preview polyline') await page.getByRole('button',{name:'导出',exact:true}).click() await page.getByLabel('格式',{exact:true}).selectOption('pdf') await page.getByRole('button',{name:'开始导出',exact:true}).click() await page.locator('.export-warnings').first().waitFor({timeout:60000}) await page.screenshot({path:path.join(output,'print-warning.png')}) } results.push({theme,errors,plotCount:await page.locator('polyline').count()});await page.close() } const cancellation=await browser.newPage() await cancellation.goto('http://127.0.0.1:5187/tests/visual/phase2.html') await cancellation.getByRole('button',{name:'源码',exact:true}).click() const heavy=Array(13).fill('```function-plot\ny = '+Array(150).fill('(x+x)').join('+')+'\n```').join('\n\n') await cancellation.getByRole('textbox',{name:'Markdown 源码编辑器'}).fill(heavy) await cancellation.getByRole('button',{name:'导出',exact:true}).click() await cancellation.getByLabel('格式',{exact:true}).selectOption('pdf') await cancellation.getByRole('button',{name:'开始导出',exact:true}).click() const cancelRow=cancellation.locator('.export-modal > ul > li').first() await cancelRow.getByRole('button',{name:'取消',exact:true}).click() await cancellation.waitForFunction(()=>document.querySelector('.export-modal > ul > li')?.textContent.includes('已取消'),null,{timeout:60000}) await cancellation.screenshot({path:path.join(output,'export-cancelled.png')}) await cancellation.close() const matrix=await browser.newPage() await matrix.goto('http://127.0.0.1:5187/tests/visual/mermaid-matrix.html') await matrix.waitForFunction(()=>document.documentElement.dataset.complete==='true',null,{timeout:120000}) const mermaid=JSON.parse(await matrix.locator('#status').textContent()) if(mermaid.passed!==36 || mermaid.total!==36) throw Error('Mermaid matrix failed') await fs.writeFile(path.join(output,'mermaid-matrix.json'),JSON.stringify(mermaid,null,2)) await matrix.close() await fs.writeFile(path.join(output,'results.json'),JSON.stringify(results,null,2));console.log(JSON.stringify(results));await browser.close() })().catch(e=>{console.error(e);process.exit(1)})