health-report-audit.spec.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /**
  2. * ================================================================
  3. * 场景:健康报告审核 E2E 测试
  4. * ================================================================
  5. * 目标:管理员通过 cfc-web HealthReportAudit 审核健康报告草稿
  6. * 部署地址:http://cfc.iwintrue.com
  7. *
  8. * 测试场景:
  9. * 1. 报告草稿列表加载
  10. * 2. 列表列信息显示(草稿ID、用户、类型、文件名、状态、时间、操作)
  11. * 3. 草稿状态标签(待审核)
  12. * 4. 分页控件存在
  13. * 5. 查看详情弹窗
  14. * 6. 详情弹窗中的指标tab
  15. * 7. 详情弹窗中的菌群tab
  16. * 8. 详情弹窗中的疾病风险tab
  17. * 9. 确认审核操作
  18. * 10. 丢弃草稿操作
  19. *
  20. * 前置条件:已登录管理员账号,有测试数据
  21. * ================================================================
  22. */
  23. const { test, expect } = require('@playwright/test');
  24. /**
  25. * 确保已登录到管理后台
  26. */
  27. async function ensureLoggedIn(page) {
  28. const sidebar = page.locator('.sidebar-container, .el-menu-vertical-demo, .el-aside, #app > div');
  29. try {
  30. await sidebar.waitFor({ state: 'visible', timeout: 5000 });
  31. return;
  32. } catch {
  33. // 未登录,继续执行登录流程
  34. }
  35. await page.goto('/login', { waitUntil: 'networkidle', timeout: 30000 });
  36. await page.waitForTimeout(2000);
  37. // 尝试多种输入框选择器
  38. const inputSelectors = [
  39. '.el-input__inner',
  40. 'input[type="text"]',
  41. 'input[placeholder*="账号"]',
  42. 'input[placeholder*="用户名"]',
  43. 'input[name="username"]',
  44. '.el-input input',
  45. ];
  46. let inputFound = false;
  47. for (const sel of inputSelectors) {
  48. try {
  49. const el = page.locator(sel).first();
  50. if (await el.isVisible({ timeout: 3000 })) {
  51. await el.fill('admin');
  52. inputFound = true;
  53. break;
  54. }
  55. } catch {
  56. // 继续尝试下一个选择器
  57. }
  58. }
  59. if (!inputFound) {
  60. const allInputs = page.locator('input');
  61. const count = await allInputs.count();
  62. if (count > 0) {
  63. await allInputs.first().fill('admin');
  64. if (count > 1) {
  65. await allInputs.nth(1).fill('admin123');
  66. }
  67. }
  68. }
  69. const pwdInputs = page.locator('input[type="password"]');
  70. if (await pwdInputs.count() > 0) {
  71. await pwdInputs.first().fill('admin123');
  72. } else {
  73. const allInputs = page.locator('input');
  74. const count = await allInputs.count();
  75. if (count >= 2) {
  76. await allInputs.nth(1).fill('admin123');
  77. }
  78. }
  79. const btnSelectors = [
  80. '.el-button--primary',
  81. 'button[type="submit"]',
  82. '.login-btn',
  83. '.el-button:has-text("登录")',
  84. '.el-button:has-text("登 录")',
  85. ];
  86. for (const sel of btnSelectors) {
  87. try {
  88. const btn = page.locator(sel).first();
  89. if (await btn.isVisible({ timeout: 2000 })) {
  90. await btn.click();
  91. break;
  92. }
  93. } catch {
  94. // 继续尝试
  95. }
  96. }
  97. await page.waitForTimeout(3000);
  98. try {
  99. await sidebar.waitFor({ state: 'visible', timeout: 10000 });
  100. } catch {
  101. // 超时仍继续
  102. }
  103. }
  104. test.describe('【场景流程】健康报告审核 HealthReportAudit', () => {
  105. test('[场景1] 报告草稿列表加载 - 期望页面正常加载', async ({ page }) => {
  106. await ensureLoggedIn(page);
  107. await page.goto('/health-report-audit', { waitUntil: 'networkidle' });
  108. await page.waitForTimeout(3000);
  109. await page.screenshot({ path: 'test-artifacts/health-report-audit.png' }).catch(() => {});
  110. const pageHeader = page.locator('.admin-page-title, h2');
  111. await expect(pageHeader).toBeVisible({ timeout: 15000 });
  112. await expect(pageHeader).toContainText('报告草稿审核');
  113. });
  114. test('[场景2] 表格列信息 - 期望显示草稿ID、用户、类型、文件名、状态、时间、操作列', async ({ page }) => {
  115. await ensureLoggedIn(page);
  116. await page.goto('/health-report-audit', { waitUntil: 'networkidle' });
  117. await page.waitForTimeout(2000);
  118. const table = page.locator('.el-table');
  119. await expect(table).toBeVisible({ timeout: 15000 });
  120. // 验证表头单元格包含各列名
  121. const headerCells = page.locator('.el-table__header th .cell');
  122. const headerText = await headerCells.allTextContents();
  123. const allText = headerText.join(' ');
  124. expect(allText).toMatch(/草稿ID|上传用户|报告类型|文件名|状态|上传时间|操作/i);
  125. });
  126. test('[场景3] 草稿状态标签 - 期望状态列显示"待审核"标签', async ({ page }) => {
  127. await ensureLoggedIn(page);
  128. await page.goto('/health-report-audit', { waitUntil: 'networkidle' });
  129. await page.waitForTimeout(2000);
  130. const table = page.locator('.el-table');
  131. await expect(table).toBeVisible({ timeout: 15000 });
  132. // 如果有数据行,验证状态标签
  133. const rows = page.locator('.el-table__body tr');
  134. const rowCount = await rows.count();
  135. if (rowCount > 0) {
  136. const statusTag = page.locator('.el-tag--warning').first();
  137. const isVisible = await statusTag.isVisible().catch(() => false);
  138. if (isVisible) {
  139. const tagText = await statusTag.textContent();
  140. expect(tagText).toMatch(/待审核/i);
  141. }
  142. }
  143. });
  144. test('[场景4] 分页控件 - 期望列表底部有分页组件', async ({ page }) => {
  145. await ensureLoggedIn(page);
  146. await page.goto('/health-report-audit', { waitUntil: 'networkidle' });
  147. await page.waitForTimeout(2000);
  148. const pagination = page.locator('.el-pagination');
  149. const count = await pagination.count();
  150. // 分页可能存在(数据量超过pagesize时),也可能隐藏(数据少时)
  151. if (count > 0) {
  152. await expect(pagination.first()).toBeVisible();
  153. }
  154. });
  155. test('[场景5] 查看详情弹窗 - 期望点击"查看详情"后弹出详情弹窗', async ({ page }) => {
  156. await ensureLoggedIn(page);
  157. await page.goto('/health-report-audit', { waitUntil: 'networkidle' });
  158. await page.waitForTimeout(2000);
  159. const viewBtn = page.locator('.el-button--primary:has-text("查看详情")');
  160. const visible = await viewBtn.isVisible({ timeout: 5000 }).catch(() => false);
  161. if (!visible) {
  162. test.skip();
  163. return;
  164. }
  165. await viewBtn.first().click();
  166. await page.waitForTimeout(2000);
  167. const dialog = page.locator('.el-dialog');
  168. await expect(dialog).toBeVisible({ timeout: 10000 });
  169. const dialogTitle = page.locator('.el-dialog__title, .el-dialog__header');
  170. await expect(dialogTitle).toContainText('草稿详情');
  171. });
  172. test('[场景6] 详情弹窗指标Tab - 期望指标Tab能正常显示', async ({ page }) => {
  173. await ensureLoggedIn(page);
  174. await page.goto('/health-report-audit', { waitUntil: 'networkidle' });
  175. await page.waitForTimeout(2000);
  176. const viewBtn = page.locator('.el-button--primary:has-text("查看详情")');
  177. const visible = await viewBtn.isVisible({ timeout: 5000 }).catch(() => false);
  178. if (!visible) {
  179. test.skip();
  180. return;
  181. }
  182. await viewBtn.first().click();
  183. await page.waitForTimeout(2000);
  184. const indicatorsTab = page.locator('.el-tabs__item:has-text("指标")');
  185. await expect(indicatorsTab).toBeVisible({ timeout: 5000 });
  186. // 点击指标Tab
  187. await indicatorsTab.click();
  188. await page.waitForTimeout(1000);
  189. // 验证指标表格存在
  190. const indicatorTable = page.locator('.el-table').last();
  191. await expect(indicatorTable).toBeVisible({ timeout: 5000 });
  192. });
  193. test('[场景7] 详情弹窗菌群Tab - 期望菌群Tab能正常切换', async ({ page }) => {
  194. await ensureLoggedIn(page);
  195. await page.goto('/health-report-audit', { waitUntil: 'networkidle' });
  196. await page.waitForTimeout(2000);
  197. const viewBtn = page.locator('.el-button--primary:has-text("查看详情")');
  198. const visible = await viewBtn.isVisible({ timeout: 5000 }).catch(() => false);
  199. if (!visible) {
  200. test.skip();
  201. return;
  202. }
  203. await viewBtn.first().click();
  204. await page.waitForTimeout(2000);
  205. const gutFloraTab = page.locator('.el-tabs__item:has-text("菌群")');
  206. await expect(gutFloraTab).toBeVisible({ timeout: 5000 });
  207. // 点击菌群Tab
  208. await gutFloraTab.click();
  209. await page.waitForTimeout(1000);
  210. });
  211. test('[场景8] 确认审核操作 - 期望点击确认后触发确认弹窗', async ({ page }) => {
  212. await ensureLoggedIn(page);
  213. await page.goto('/health-report-audit', { waitUntil: 'networkidle' });
  214. await page.waitForTimeout(2000);
  215. const confirmBtn = page.locator('.el-button--success:has-text("确认")');
  216. const visible = await confirmBtn.isVisible({ timeout: 5000 }).catch(() => false);
  217. if (!visible) {
  218. test.skip();
  219. return;
  220. }
  221. await confirmBtn.first().click();
  222. await page.waitForTimeout(2000);
  223. // 确认弹窗(El Message Box)
  224. const msgBox = page.locator('.el-message-box');
  225. await expect(msgBox).toBeVisible({ timeout: 5000 });
  226. // 取消操作,不真正执行
  227. const cancelBtn = msgBox.locator('.el-button--default, .el-message-box__btns .el-button:not(.el-button--primary)');
  228. if (await cancelBtn.isVisible().catch(() => false)) {
  229. await cancelBtn.click();
  230. } else {
  231. await page.keyboard.press('Escape');
  232. }
  233. });
  234. });