article-management.spec.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. /**
  2. * ================================================================
  3. * 场景:文章管理 E2E 测试
  4. * ================================================================
  5. * 目标:管理员通过 cfc-web Element UI 管理后台管理文章
  6. * 部署地址:http://localhost:8082
  7. *
  8. * 路由映射:
  9. * 文章列表 → /article-manage
  10. * 文章分类 → /article-categories
  11. * 知识标签 → /knowledge-tags
  12. * ================================================================
  13. *
  14. * v11 更新:文章工作流状态机
  15. * draft → submitForReview → pending → published
  16. * published → withdraw → withdrawn
  17. * rejected → reDraft → draft
  18. */
  19. const { test, expect } = require('@playwright/test');
  20. async function ensureLoggedIn(page) {
  21. const sidebar = page.locator('.sidebar-container, .el-menu-vertical-demo, .el-aside');
  22. if (await sidebar.isVisible({ timeout: 3000 }).catch(() => false)) {
  23. return;
  24. }
  25. await page.goto('/login');
  26. await page.waitForSelector('.el-input__inner', { timeout: 15000 });
  27. await page.fill('.el-input__inner', 'admin', { timeout: 5000 });
  28. const passwordInputs = page.locator('.el-input__inner').filter({ hasPlaceholder: /密码/ });
  29. if (await passwordInputs.count() > 0) {
  30. await passwordInputs.first().fill('admin123');
  31. } else {
  32. const inputs = page.locator('.el-input__inner');
  33. const count = await inputs.count();
  34. if (count >= 2) {
  35. await inputs.nth(1).fill('admin123');
  36. }
  37. }
  38. await page.click('.el-button--primary.login-btn, .el-button[type="primary"]:not(.skip)');
  39. await page.waitForURL(/\/dashboard|\/admin\/dashboard/, { timeout: 15000 });
  40. }
  41. test.describe('【场景流程】文章管理全流程', () => {
  42. test('[场景1] 管理员查看文章列表 - 期望返回文章列表', async ({ page }) => {
  43. await ensureLoggedIn(page);
  44. await page.goto('/article-manage');
  45. await page.waitForLoadState('networkidle');
  46. await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
  47. const rows = page.locator('.el-table__body tr');
  48. const count = await rows.count();
  49. expect(count).toBeGreaterThanOrEqual(0);
  50. });
  51. test('[场景2] 管理员管理文章分类 - 期望分类页面可用', async ({ page }) => {
  52. await ensureLoggedIn(page);
  53. await page.goto('/article-categories');
  54. await page.waitForLoadState('networkidle');
  55. await expect(page.locator('.el-table, .category-list')).toBeVisible({ timeout: 10000 });
  56. });
  57. test('[场景3] 管理员查看知识标签', async ({ page }) => {
  58. await ensureLoggedIn(page);
  59. await page.goto('/knowledge-tags');
  60. await page.waitForLoadState('networkidle');
  61. await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
  62. });
  63. // ================================================================
  64. // v11 新增:文章工作流测试(需前端运行后执行)
  65. // ================================================================
  66. test('[场景4] 创建草稿文章 → 提交审核 → 发布(需审核流程完成)', async ({ page }) => {
  67. await ensureLoggedIn(page);
  68. await page.goto('/article-edit');
  69. await page.waitForLoadState('networkidle');
  70. // 填写文章基本信息
  71. const titleInput = page.locator('input').first();
  72. await titleInput.fill('E2E_自动测试文章_' + Date.now());
  73. // 点击保存为草稿(不发布)
  74. const saveBtn = page.locator('button').filter({ hasText: /保存|发布/ }).first();
  75. await saveBtn.click();
  76. await page.waitForTimeout(1000);
  77. // 找到提交审核按钮并点击(如果有)
  78. const submitBtn = page.locator('button').filter({ hasText: /提交审核/ });
  79. if (await submitBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
  80. await submitBtn.click();
  81. await page.waitForTimeout(1000);
  82. }
  83. // 验证页面无报错
  84. await expect(page.locator('.el-message--error, .el-form-item__error')).not.toBeVisible();
  85. });
  86. test('[场景5] 查看已发布文章 → 撤回(withdraw)按钮可用', async ({ page }) => {
  87. await ensureLoggedIn(page);
  88. await page.goto('/article-manage');
  89. await page.waitForLoadState('networkidle');
  90. // 查找已发布状态的文章行
  91. const publishedRows = page.locator('.el-table__row').filter({ hasText: 'published' });
  92. if (await publishedRows.count() > 0) {
  93. // 撤回按钮应该可见
  94. const withdrawBtn = page.locator('button').filter({ hasText: /撤回/ });
  95. const isVisible = await withdrawBtn.isVisible({ timeout: 2000 }).catch(() => false);
  96. if (isVisible) {
  97. await withdrawBtn.first().click();
  98. await page.waitForTimeout(1000);
  99. }
  100. }
  101. // 只要页面不报错即通过
  102. await expect(page.locator('.el-message--error')).not.toBeVisible();
  103. });
  104. });