components-functional.mjs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import assert from 'node:assert/strict';
  2. import { readFile } from 'node:fs/promises';
  3. import { pathToFileURL } from 'node:url';
  4. import { chromium } from 'playwright';
  5. // 组件检查通过真实平台账户及独立开发商家的页面操作执行。
  6. export async function checkComponents(page, baseUrl) {
  7. const platformAccount = JSON.parse(await readFile(new URL('../../.work/platform-accounts.json', import.meta.url), 'utf8'));
  8. const results = [];
  9. const record = (condition, name) => { assert.ok(condition, name); results.push({ name, passed: true }); };
  10. const visible = async (locator, name) => { await locator.waitFor({ state: 'visible' }); record(await locator.isVisible(), name); };
  11. const visit = async name => {
  12. if (name === 'login') await page.goto(`${baseUrl.replace(/\/$/, '')}/#/platform/${name}`);
  13. else await page.evaluate(value => { location.hash = `/platform/${value}`; }, name);
  14. await page.waitForURL(new RegExp(`#/?platform/${name}$`));
  15. await page.locator(name === 'login' ? '.login-panel' : '.platform-pages').waitFor();
  16. };
  17. await page.setViewportSize({ width: 1440, height: 1000 });
  18. await visit('login');
  19. record(await page.locator('.login-panel .el-form').count() === 1, '平台登录使用 Element Plus 表单');
  20. record(await page.locator('.login-panel .el-input').count() === 2, '平台账户密码使用 Element Plus 输入');
  21. const account = page.getByLabel('员工账户', { exact: true });
  22. const password = page.getByLabel('密码', { exact: true });
  23. await account.focus();
  24. record(await account.evaluate(node => node === document.activeElement), '账户输入支持键盘聚焦');
  25. await account.fill(platformAccount.username);
  26. await page.keyboard.press('Tab');
  27. record(await password.evaluate(node => node === document.activeElement), 'Tab 按顺序进入密码输入');
  28. record(await password.getAttribute('type') === 'password', '密码默认隐藏');
  29. record(await password.getAttribute('autocomplete') === 'current-password', '密码保持正确的浏览器自动填充语义');
  30. await password.fill(platformAccount.password);
  31. await page.getByRole('button', { name: '登录', exact: true }).click();
  32. await page.locator('.admin-content').waitFor();
  33. await visit('dashboard');
  34. const iconButton = page.locator('.platform-pages').getByRole('button', { name: '商家管理', exact: true });
  35. await visible(iconButton.locator('svg[aria-hidden="true"]'), '业务按钮使用隐藏于辅助阅读的 SVG 图标');
  36. record(await iconButton.locator('svg').count() === 1, '图标与按钮文字共同呈现');
  37. await iconButton.focus();
  38. record(await iconButton.evaluate(node => node === document.activeElement), 'Element Plus 按钮支持键盘聚焦');
  39. await page.keyboard.press('Enter');
  40. await visible(page.locator('.platform-pages .el-table'), '键盘激活管理入口并呈现 Element Plus 表格');
  41. record(await page.locator('.platform-pages .el-tag').count() >= 2, '商家状态使用 Element Plus 标签');
  42. await visit('capacity');
  43. const select = page.locator('.platform-pages .el-select');
  44. await select.click();
  45. await page.getByRole('option', { name: platformAccount.tenant_name, exact: true }).click();
  46. await visible(select.getByText(platformAccount.tenant_name, { exact: true }), '商家选择使用 Element Plus 下拉交互');
  47. await select.click();
  48. const other = page.getByRole('option').filter({ hasNotText: platformAccount.tenant_name }).first();
  49. const otherName = await other.innerText();
  50. await other.click();
  51. await visible(select.getByText(otherName, { exact: true }), '下拉支持切换商家');
  52. const reason = page.getByLabel('修改原因', { exact: true });
  53. record(await reason.getAttribute('maxlength') === '1000', '文本域保留长度限制');
  54. await reason.fill('组件检查输入内容');
  55. record(await reason.inputValue() === '组件检查输入内容', 'Element Plus 文本域支持填写');
  56. await visit('tenants');
  57. const tenantRow = page.locator('tr').filter({ hasText: platformAccount.tenant_name });
  58. await tenantRow.getByRole('button', { name: '停用商家', exact: true }).click();
  59. const dialog = page.getByRole('dialog');
  60. await visible(dialog, '平台二次确认使用可访问对话框');
  61. record(await dialog.locator('.el-dialog__body').count() === 1, '后台确认使用 Element Plus 对话框');
  62. await dialog.getByRole('button', { name: '确认操作', exact: true }).click();
  63. await dialog.waitFor({ state: 'hidden' });
  64. await tenantRow.getByRole('button', { name: '启用商家', exact: true }).waitFor();
  65. record((await tenantRow.innerText()).includes('停用'), '操作后的商家状态来自真实服务');
  66. await tenantRow.getByRole('button', { name: '启用商家', exact: true }).click();
  67. await dialog.getByRole('button', { name: '确认操作', exact: true }).click();
  68. await dialog.waitFor({ state: 'hidden' });
  69. await tenantRow.getByRole('button', { name: '停用商家', exact: true }).waitFor();
  70. record((await tenantRow.innerText()).includes('启用'), '开发商家恢复启用状态');
  71. await page.setViewportSize({ width: 390, height: 900 });
  72. const scroll = page.locator('.platform-pages .el-table .el-scrollbar__wrap');
  73. await visible(scroll, '390px 保留表格滚动容器');
  74. const dimensions = await scroll.evaluate(node => ({ client: node.clientWidth, scroll: node.scrollWidth }));
  75. record(dimensions.scroll > dimensions.client, '390px 宽表格提供横向滚动范围');
  76. await scroll.hover();
  77. await page.mouse.wheel(450, 0);
  78. await page.waitForTimeout(200);
  79. record(await scroll.evaluate(node => node.scrollLeft) > 0, '用户滚动操作能够查看表格右侧');
  80. const documentWidth = await page.locator('html').evaluate(node => ({ client: node.clientWidth, scroll: node.scrollWidth }));
  81. record(documentWidth.scroll <= documentWidth.client + 1, '表格滚动不扩大390px文档宽度');
  82. return results;
  83. }
  84. if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) {
  85. 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' });
  86. try {
  87. const page = await browser.newPage();
  88. const results = await checkComponents(page, process.env.FRONTEND_URL || 'http://127.0.0.1:18762');
  89. console.log(JSON.stringify({ passed: results.length, results }));
  90. } finally { await browser.close(); }
  91. }