import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import { pathToFileURL } from 'node:url'; import { chromium } from 'playwright'; // 组件检查通过真实平台账户及独立开发商家的页面操作执行。 export async function checkComponents(page, baseUrl) { const platformAccount = JSON.parse(await readFile(new URL('../../.work/platform-accounts.json', import.meta.url), 'utf8')); const results = []; const record = (condition, name) => { assert.ok(condition, name); results.push({ name, passed: true }); }; const visible = async (locator, name) => { await locator.waitFor({ state: 'visible' }); record(await locator.isVisible(), name); }; const visit = async name => { if (name === 'login') await page.goto(`${baseUrl.replace(/\/$/, '')}/#/platform/${name}`); else await page.evaluate(value => { location.hash = `/platform/${value}`; }, name); await page.waitForURL(new RegExp(`#/?platform/${name}$`)); await page.locator(name === 'login' ? '.login-panel' : '.platform-pages').waitFor(); }; await page.setViewportSize({ width: 1440, height: 1000 }); await visit('login'); record(await page.locator('.login-panel .el-form').count() === 1, '平台登录使用 Element Plus 表单'); record(await page.locator('.login-panel .el-input').count() === 2, '平台账户密码使用 Element Plus 输入'); const account = page.getByLabel('员工账户', { exact: true }); const password = page.getByLabel('密码', { exact: true }); await account.focus(); record(await account.evaluate(node => node === document.activeElement), '账户输入支持键盘聚焦'); await account.fill(platformAccount.username); await page.keyboard.press('Tab'); record(await password.evaluate(node => node === document.activeElement), 'Tab 按顺序进入密码输入'); record(await password.getAttribute('type') === 'password', '密码默认隐藏'); record(await password.getAttribute('autocomplete') === 'current-password', '密码保持正确的浏览器自动填充语义'); await password.fill(platformAccount.password); await page.getByRole('button', { name: '登录', exact: true }).click(); await page.locator('.admin-content').waitFor(); await visit('dashboard'); const iconButton = page.locator('.platform-pages').getByRole('button', { name: '商家管理', exact: true }); await visible(iconButton.locator('svg[aria-hidden="true"]'), '业务按钮使用隐藏于辅助阅读的 SVG 图标'); record(await iconButton.locator('svg').count() === 1, '图标与按钮文字共同呈现'); await iconButton.focus(); record(await iconButton.evaluate(node => node === document.activeElement), 'Element Plus 按钮支持键盘聚焦'); await page.keyboard.press('Enter'); await visible(page.locator('.platform-pages .el-table'), '键盘激活管理入口并呈现 Element Plus 表格'); record(await page.locator('.platform-pages .el-tag').count() >= 2, '商家状态使用 Element Plus 标签'); await visit('capacity'); const select = page.locator('.platform-pages .el-select'); await select.click(); await page.getByRole('option', { name: platformAccount.tenant_name, exact: true }).click(); await visible(select.getByText(platformAccount.tenant_name, { exact: true }), '商家选择使用 Element Plus 下拉交互'); await select.click(); const other = page.getByRole('option').filter({ hasNotText: platformAccount.tenant_name }).first(); const otherName = await other.innerText(); await other.click(); await visible(select.getByText(otherName, { exact: true }), '下拉支持切换商家'); const reason = page.getByLabel('修改原因', { exact: true }); record(await reason.getAttribute('maxlength') === '1000', '文本域保留长度限制'); await reason.fill('组件检查输入内容'); record(await reason.inputValue() === '组件检查输入内容', 'Element Plus 文本域支持填写'); await visit('tenants'); const tenantRow = page.locator('tr').filter({ hasText: platformAccount.tenant_name }); await tenantRow.getByRole('button', { name: '停用商家', exact: true }).click(); const dialog = page.getByRole('dialog'); await visible(dialog, '平台二次确认使用可访问对话框'); record(await dialog.locator('.el-dialog__body').count() === 1, '后台确认使用 Element Plus 对话框'); await dialog.getByRole('button', { name: '确认操作', exact: true }).click(); await dialog.waitFor({ state: 'hidden' }); await tenantRow.getByRole('button', { name: '启用商家', exact: true }).waitFor(); record((await tenantRow.innerText()).includes('停用'), '操作后的商家状态来自真实服务'); await tenantRow.getByRole('button', { name: '启用商家', exact: true }).click(); await dialog.getByRole('button', { name: '确认操作', exact: true }).click(); await dialog.waitFor({ state: 'hidden' }); await tenantRow.getByRole('button', { name: '停用商家', exact: true }).waitFor(); record((await tenantRow.innerText()).includes('启用'), '开发商家恢复启用状态'); await page.setViewportSize({ width: 390, height: 900 }); const scroll = page.locator('.platform-pages .el-table .el-scrollbar__wrap'); await visible(scroll, '390px 保留表格滚动容器'); const dimensions = await scroll.evaluate(node => ({ client: node.clientWidth, scroll: node.scrollWidth })); record(dimensions.scroll > dimensions.client, '390px 宽表格提供横向滚动范围'); await scroll.hover(); await page.mouse.wheel(450, 0); await page.waitForTimeout(200); record(await scroll.evaluate(node => node.scrollLeft) > 0, '用户滚动操作能够查看表格右侧'); const documentWidth = await page.locator('html').evaluate(node => ({ client: node.clientWidth, scroll: node.scrollWidth })); record(documentWidth.scroll <= documentWidth.client + 1, '表格滚动不扩大390px文档宽度'); return results; } if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) { const browser = await chromium.launch({ headless: true, executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE || 'C:/Users/Administrator/AppData/Local/ms-playwright/chromium-1228/chrome-win64/chrome.exe' }); try { const page = await browser.newPage(); const results = await checkComponents(page, process.env.FRONTEND_URL || 'http://127.0.0.1:18762'); console.log(JSON.stringify({ passed: results.length, results })); } finally { await browser.close(); } }