import fs from 'node:fs' import path from 'node:path' import { createRequire } from 'node:module' import { fileURLToPath } from 'node:url' import assert from 'node:assert/strict' import { login } from './role-permissions-live.mjs' const { chromium, expect } = createRequire(new URL('../package.json', import.meta.url))('@playwright/test') const report = fileURLToPath(new URL('../reports/guide-style-20260909/', import.meta.url)) fs.mkdirSync(report, { recursive:true }) const browser = await chromium.launch({ channel:'msedge', headless:true, args:['--enable-unsafe-swiftshader'] }) const checks = [], errors = [] const session = await login('root') const context = await browser.newContext({ viewport:{ width:1920, height:911 } }) await context.addInitScript(({ accessToken, refreshToken }) => { sessionStorage.setItem('unreal-tran:web:access-token:v1', accessToken) sessionStorage.setItem('unreal-tran:web:refresh-token:v1', refreshToken) }, session) const page = await context.newPage() page.on('pageerror', error => errors.push(error.message)) async function shot(name) { const overflow = await page.locator('.legacy-guide-page').evaluate(el => el.scrollWidth > el.clientWidth + 2) assert.equal(overflow, false, `${name}: page has no horizontal overflow`) await page.screenshot({ path:path.join(report, `${name}.png`), animations:'disabled' }) checks.push({ name, result:'PASS', screenshot:`${name}.png` }) } async function theme(mode) { await page.evaluate(async mode => (await import('/src/stores/preferences.ts')).usePreferencesStore().setThemeMode(mode), mode) } try { await page.goto('http://127.0.0.1:6180/content/ofd') await expect(page.locator('.guide-project-list')).toBeVisible({ timeout:60000 }) await theme('military') // Fixtures exist only in this disposable browser's local guide repository. await page.evaluate(async () => { const { legacyGuideContentStore } = await import('/src/features/guide-legacy/runtime.ts') for (let i=1;i<=12;i++) legacyGuideContentStore.createOFDProject({ id:`STYLE-${i}`, code:`STYLE-${String(i).padStart(3,'0')}`, title:`液压系统检查作业指导书 · 样式验证 ${i}`, author:'样式验证' }) }) await expect(page.locator('.guide-project-title')).toHaveCount(10) console.log('List loaded', await page.locator('.guide-project-title').count()) await shot('01-list') const count = await page.locator('.guide-project-title').count() await page.getByRole('textbox', { name:'搜索指导书', exact:true }).fill('NO-MATCH-GUIDE-STYLE') assert.equal(await page.locator('.guide-project-title').count(), count, 'Search waits for explicit submission') await page.locator('.guide-project-list').getByRole('button', { name:'搜索', exact:true }).click() await expect(page.locator('.guide-project-title')).toHaveCount(0) await page.getByRole('button', { name:'重置', exact:true }).click() await expect(page.locator('.guide-project-title')).toHaveCount(count) await page.locator('.system-pagination .btn-next').click() await expect(page.locator('.guide-project-title')).toHaveCount(2) await page.locator('.system-pagination .btn-prev').click() await expect(page.locator('.guide-project-title')).toHaveCount(10) if (count) { await page.locator('.guide-project-actions').first().getByRole('button', { name:'更多' }).click() await expect(page.getByRole('menuitem', { name:'项目详情', exact:true })).toBeVisible() await shot('02-list-actions') await page.getByRole('menuitem', { name:'项目详情', exact:true }).click() await expect(page.getByRole('button', { name:'关闭项目详情' })).toBeVisible() await page.getByRole('button', { name:'关闭项目详情' }).click() await page.locator('.guide-project-actions').first().getByRole('button', { name:'进入编制', exact:true }).click() await expect(page.locator('.ofd-view-editor')).toBeVisible({ timeout:60000 }) await shot('03-editor') await theme('dark') await shot('03-editor-dark') await theme('military') await page.locator('.ofd-top-actions [data-action="open-preview"]').click() await expect(page.locator('.ofd-workspace-dialog.preview')).toBeVisible() await shot('03-guide-preview') console.log('Editor loaded', page.url()) } await page.goto('http://127.0.0.1:6180/content/ofd/templates') await expect(page.locator('.guide-tpl-card')).toHaveCount(4, { timeout:30000 }) await page.locator('[data-guide-tpl-action="select"]').nth(1).click() await expect(page.locator('.guide-tpl-card').nth(1)).toHaveClass(/is-active/) await shot('04-templates') await page.locator('.guide-tpl-card').nth(1).getByRole('button', { name:'预览', exact:true }).click() await expect(page.locator('.guide-tpl-modal')).toBeVisible() await shot('05-template-preview') await page.goto('http://127.0.0.1:6180/content/ofd/api') await expect(page.locator('.ofd-api-layout')).toBeVisible({ timeout:30000 }) await shot('06-api') await page.getByRole('button', { name:'执行调用', exact:true }).click() await expect(page.locator('.ofd-api-result')).toBeVisible() await expect(page.locator('.ofd-api-result header b')).toHaveText('200') await shot('07-api-result') for (const mode of ['technology','graphite','dark']) { await theme(mode) await shot(`08-api-${mode}`) } await page.setViewportSize({ width:1366, height:768 }) await theme('military') await shot('09-api-laptop') await page.goto('http://127.0.0.1:6180/content/ofd/templates') await expect(page.locator('.guide-tpl-card')).toHaveCount(4) await shot('10-templates-laptop') await theme('dark') await shot('11-templates-dark') await page.goto('http://127.0.0.1:6180/content/ofd') await expect(page.locator('.guide-project-list')).toBeVisible() await shot('12-list-dark') await theme('military') await page.getByRole('button', { name:'新建指导书', exact:true }).click() await expect(page.locator('.ofd-app')).toBeVisible() await shot('13-create-wizard') assert.equal((await context.request.get('http://192.168.31.168:6180/')).status(),200) assert.deepEqual(errors, [], 'No unhandled browser exceptions') checks.push({ name:'搜索/重置、详情、编制入口、模板选择/预览、GET 接口自检、主题切换、局域网入口', result:'PASS' }) } catch(error) { await page.screenshot({ path:path.join(report,'failure.png') }).catch(()=>{}) console.error('Browser errors:',errors) throw error } finally { fs.writeFileSync(path.join(report,'results.json'), JSON.stringify({ checks, errors },null,2)) await browser.close() }