platform-functional.mjs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import assert from 'node:assert/strict';
  2. import { readFile } from 'node:fs/promises';
  3. // 使用全新的浏览器 context 调用,仅通过用户可以使用的页面入口和表单检查功能。
  4. export async function checkPlatform(page, baseUrl) {
  5. const credentials = JSON.parse(await readFile(new URL('../../.work/platform-accounts.json', import.meta.url), 'utf8'));
  6. const results = [];
  7. const content = page.locator('.platform-pages');
  8. const navigation = page.getByRole('navigation', { name: '系统导航' });
  9. const dialog = page.getByRole('dialog');
  10. const visible = async (locator, message) => {
  11. await locator.waitFor({ state: 'visible', timeout: 15000 });
  12. assert.equal(await locator.isVisible(), true, message);
  13. results.push({ name: message, passed: true });
  14. };
  15. const check = (condition, message) => { assert.ok(condition, message); results.push({ name: message, passed: true }); };
  16. const open = async (name, title) => {
  17. await navigation.getByRole('button', { name, exact: true }).click();
  18. await visible(page.locator('.admin-title').getByRole('heading', { name: title, exact: true }), `平台页面:${title}`);
  19. await visible(content.locator('.data-source'), `${title} 展示数据来源`);
  20. await content.getByRole('status').waitFor({ state: 'hidden' });
  21. };
  22. const confirm = async (title) => {
  23. await visible(dialog.getByRole('heading', { name: title, exact: true }), `${title} 需要确认`);
  24. await dialog.getByRole('button', { name: '确认操作', exact: true }).click();
  25. await dialog.waitFor({ state: 'hidden' });
  26. };
  27. const login = async () => {
  28. await page.getByLabel('员工账户', { exact: true }).fill(credentials.username);
  29. await page.getByLabel('密码', { exact: true }).fill(credentials.password);
  30. await page.getByRole('button', { name: '登录', exact: true }).click();
  31. };
  32. await page.goto(`${baseUrl.replace(/\/$/, '')}/#platform/tenants`);
  33. await visible(page.getByRole('heading', { name: '平台员工登录', exact: true }), '未登录访问商家管理进入平台登录');
  34. check(await navigation.count() === 0, '未登录隐藏管理菜单');
  35. await page.getByLabel('员工账户', { exact: true }).fill('platform-admin');
  36. await page.getByLabel('密码', { exact: true }).fill('wrong-password');
  37. await page.getByRole('button', { name: '登录', exact: true }).click();
  38. await visible(page.getByRole('alert'), '错误密码显示登录失败');
  39. check(await page.getByLabel('密码', { exact: true }).inputValue() === '', '登录失败清空密码');
  40. check(await page.getByLabel('员工账户', { exact: true }).inputValue() === 'platform-admin', '登录失败保留账户');
  41. await login();
  42. await visible(page.locator('.admin-title').getByRole('heading', { name: '商家接入与运行状态', exact: true }), '登录成功返回原管理页面');
  43. await open('平台概览', '平台概览');
  44. check(await content.locator('.metric').count() === 4, '平台概览提供四项指标区域');
  45. await visible(content.getByRole('button', { name: '异常投递', exact: true }), '概览提供异常投递入口');
  46. await open('应用接入', '应用接入');
  47. await visible(content.getByText('应用、渠道与授权范围', { exact: true }), '应用接入显示批准范围');
  48. await open('模型服务', '模型服务');
  49. await visible(content.getByText('模型配置、供应商与运行状态', { exact: true }), '模型服务显示批准范围');
  50. await open('权限管理', '权限管理');
  51. await visible(content.getByText('平台人员与管理权限', { exact: true }), '权限管理显示批准范围');
  52. await open('系统设置', '系统设置');
  53. await visible(content.getByText('通知渠道与系统运行配置', { exact: true }), '系统设置显示批准范围');
  54. await open('运行监控', '运行监控与异常处理');
  55. await visible(content.getByRole('button', { name: '查看失败投递', exact: true }), '监控提供投递处理入口');
  56. await visible(content.getByRole('button', { name: '查看额度', exact: true }), '监控提供额度处理入口');
  57. // 浏览器实际断开网络后检查失败状态,恢复连接后由页面重新请求真实服务。
  58. await page.context().setOffline(true);
  59. try {
  60. await open('商家管理', '商家接入与运行状态');
  61. await visible(content.getByRole('alert').filter({ hasText: '网络连接失败' }), '断开网络显示读取失败');
  62. check(await content.getByRole('button', { name: '保存接入登记', exact: true }).count() === 0, '读取失败隐藏业务提交表单');
  63. } finally {
  64. await page.context().setOffline(false);
  65. }
  66. await content.getByRole('button', { name: '重新加载', exact: true }).click();
  67. await visible(content.getByRole('button', { name: '保存接入登记', exact: true }), '恢复网络后重新读取管理数据');
  68. await open('商家管理', '商家接入与运行状态');
  69. const tenantName = `功能检查商家 ${Date.now()}`;
  70. await content.getByLabel('商家名称', { exact: true }).fill(tenantName);
  71. await content.getByLabel('接入负责人', { exact: true }).fill('功能检查负责人');
  72. await content.getByRole('button', { name: '保存接入登记', exact: true }).click();
  73. const registered = content.getByRole('row').filter({ hasText: tenantName });
  74. await visible(registered.getByText('待接入', { exact: true }), '新商家登记为待接入');
  75. check(await registered.getByRole('button').count() === 0, '待接入商家不开放启用操作');
  76. await content.getByLabel('商家名称', { exact: true }).fill(tenantName);
  77. await content.getByLabel('接入负责人', { exact: true }).fill('功能检查负责人');
  78. await content.getByRole('button', { name: '保存接入登记', exact: true }).click();
  79. await visible(content.getByRole('alert').filter({ hasText: '该商家已登记' }), '重复商家登记被拒绝');
  80. check(await registered.count() === 1, '重复登记不增加商家记录');
  81. const merchantA = content.getByRole('row').filter({ has: page.getByRole('cell', { name: credentials.tenant_name, exact: true }) });
  82. await merchantA.getByRole('button', { name: '停用商家', exact: true }).click();
  83. await visible(dialog.getByRole('heading', { name: '停用商家', exact: true }), '商家停用展示确认窗口');
  84. await dialog.getByRole('button', { name: '返回检查', exact: true }).click();
  85. await visible(merchantA.getByText('启用', { exact: true }), '取消确认保持商家启用');
  86. await merchantA.getByRole('button', { name: '停用商家', exact: true }).click();
  87. await confirm('停用商家');
  88. await visible(merchantA.getByText('停用', { exact: true }), '确认后商家停用');
  89. await merchantA.getByRole('button', { name: '启用商家', exact: true }).click();
  90. await confirm('启用商家');
  91. await visible(merchantA.getByText('启用', { exact: true }), '确认后商家恢复启用');
  92. await open('配额管理', '商家额度与资源分配');
  93. await content.locator('.el-select__wrapper').filter({ has: page.getByLabel('商家', { exact: true }) }).click();
  94. await page.getByRole('option', { name: tenantName, exact: true }).click();
  95. await content.getByLabel('交互并发上限', { exact: true }).fill('4');
  96. await content.getByLabel('交互预留份额', { exact: true }).fill('8');
  97. await content.getByLabel('修改原因', { exact: true }).fill('验证预留总量限制');
  98. await content.getByRole('button', { name: '检查并保存额度', exact: true }).click();
  99. // 确认页面要求的预留上限通过原生表单约束拦截,接口限制另由真实数据库测试覆盖。
  100. check(await content.getByLabel('交互预留份额', { exact: true }).evaluate(element => !element.validity.valid), '超出输入范围的预留被表单拒绝');
  101. await content.getByLabel('交互预留份额', { exact: true }).fill('0');
  102. await content.getByLabel('交互并发上限', { exact: true }).fill('3');
  103. await content.getByLabel('后台并发上限', { exact: true }).fill('1');
  104. await content.getByLabel('每日 token 上限', { exact: true }).fill('900000');
  105. await content.getByLabel('修改原因', { exact: true }).fill('功能检查资源配置');
  106. await content.getByRole('button', { name: '检查并保存额度', exact: true }).click();
  107. await visible(page.getByRole('status').filter({ hasText: '额度已保存' }), '有效额度保存成功');
  108. await open('平台概览', '平台概览');
  109. await open('配额管理', '商家额度与资源分配');
  110. await content.locator('.el-select__wrapper').filter({ has: page.getByLabel('商家', { exact: true }) }).click();
  111. await page.getByRole('option', { name: tenantName, exact: true }).click();
  112. check(await content.getByLabel('交互并发上限', { exact: true }).inputValue() === '3', '再次访问显示已保存交互额度');
  113. check(await content.getByLabel('每日 token 上限', { exact: true }).inputValue() === '900000', '再次访问显示已保存 token 额度');
  114. await open('消息投递', '结果投递与失败重试');
  115. const failed = content.getByRole('row').filter({ has: page.getByRole('cell', { name: '投递失败', exact: true }) }).first();
  116. if (await failed.count()) {
  117. const event = await failed.getByRole('cell').nth(1).innerText();
  118. await failed.getByRole('button', { name: '重试本次投递', exact: true }).click();
  119. await confirm('重试结果投递');
  120. const delivery = content.getByRole('row').filter({ hasText: event });
  121. await visible(delivery.getByRole('cell', { name: '已排队,等待下一次投递', exact: true }), '重试后进入排队状态');
  122. check(await delivery.getByRole('button', { name: '重试本次投递', exact: true }).isDisabled(), '排队中的投递禁止重复重试');
  123. } else {
  124. check(await content.getByText('暂无结果投递记录。', { exact: true }).count() > 0 || await content.getByRole('table').count() > 0, '真实投递列表或空内容正确展示');
  125. }
  126. await open('操作审计', '操作审计');
  127. await visible(content.getByRole('row').filter({ hasText: tenantName }).filter({ hasText: '登记商家' }), '登记产生审计记录');
  128. await visible(content.getByRole('cell', { name: '调整商家额度:功能检查资源配置', exact: true }).first(), '配额修改原因进入审计记录');
  129. await page.getByRole('button', { name: '退出登录', exact: true }).click();
  130. await confirm('退出登录');
  131. await visible(page.getByRole('heading', { name: '平台员工登录', exact: true }), '确认退出后返回平台登录');
  132. check(await navigation.count() === 0, '退出后隐藏受保护管理内容');
  133. await page.goto(`${baseUrl.replace(/\/$/, '')}/#platform/audit`);
  134. await visible(page.getByRole('heading', { name: '平台员工登录', exact: true }), '退出后直接访问审计仍需登录');
  135. await login();
  136. await visible(content.getByRole('row').filter({ hasText: tenantName }).filter({ hasText: '登记商家' }), '重新登录可以读取持久化审计');
  137. return results;
  138. }