article-management.spec.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /**
  2. * ================================================================
  3. * 场景:文章管理全流程 E2E 测试
  4. * ================================================================
  5. * 用户故事:
  6. * - 管理员:创建/发布/编辑/删除/精选文章
  7. * - 用户:浏览文章列表,查看文章详情,获取每日贴士
  8. *
  9. * 流程步骤:
  10. * 1. 管理员创建文章并发布
  11. * 2. 管理员管理文章分类
  12. * 3. 管理员设置精选文章
  13. * 4. 用户浏览文章列表
  14. * 5. 用户查看文章详情
  15. *
  16. * 关键里程碑:
  17. * - [Milestone-1] 文章发布成功
  18. * - [Milestone-2] 文章精选设置成功
  19. * - [Milestone-3] 用户成功浏览文章
  20. * ================================================================
  21. *
  22. * 运行:npx playwright test tests/e2e/article-management.spec.js
  23. */
  24. const { test, expect } = require('@playwright/test');
  25. test.describe('【场景流程】文章管理全流程', () => {
  26. // ===== 场景1:管理员创建并发布文章 =====
  27. test('[场景1] 管理员创建并发布文章 - 期望发布成功', async ({ page }) => {
  28. // ========== Given:管理员进入文章管理页面 ==========
  29. await page.goto('/#/pages/admin/article-list');
  30. await page.waitForLoadState('networkidle');
  31. // ========== When:创建并发布文章 ==========
  32. const createBtn = page.locator('.create-btn, .add-article-btn');
  33. if (await createBtn.isVisible()) {
  34. await createBtn.click();
  35. } else {
  36. await page.goto('/#/pages/admin/article-create');
  37. await page.waitForLoadState('networkidle');
  38. }
  39. await page.waitForSelector('.article-form, .create-form', { timeout: 5000 });
  40. // 填写标题
  41. const titleInput = page.locator('input[name="title"], .title-input');
  42. if (await titleInput.isVisible()) {
  43. await titleInput.fill('测试文章_' + Date.now());
  44. }
  45. // 填写摘要
  46. const summaryInput = page.locator('textarea[name="summary"], .summary-input');
  47. if (await summaryInput.isVisible()) {
  48. await summaryInput.fill('这是文章摘要内容,描述文章的核心要点。');
  49. }
  50. // 填写内容
  51. const contentInput = page.locator('textarea[name="content"], .content-input');
  52. if (await contentInput.isVisible()) {
  53. await contentInput.fill('这是文章的正文内容,包含详细的信息和说明。');
  54. }
  55. // 选择分类
  56. const categorySelect = page.locator('.category-picker, select[name="category"]');
  57. if (await categorySelect.isVisible()) {
  58. await categorySelect.click();
  59. await page.waitForSelector('.option-item', { timeout: 3000 });
  60. await page.locator('.option-item').first().click();
  61. }
  62. // 发布文章
  63. await page.click('.publish-btn, .submit-btn');
  64. // ========== Then:Milestone-1 - 发布成功 ========
  65. await page.waitForSelector('.success-tip, .publish-success, .toast-success', { timeout: 10000 });
  66. const successTip = page.locator('.success-tip, .publish-success');
  67. expect(await successTip.isVisible()).toBeTruthy();
  68. });
  69. // ===== 场景2:管理员管理文章分类 =====
  70. test('[场景2] 管理员管理文章分类 - 期望分类创建成功', async ({ page }) => {
  71. // ========== Given:管理员进入分类管理页面 ==========
  72. await page.goto('/#/pages/admin/category-list');
  73. await page.waitForLoadState('networkidle');
  74. // ========== When:创建新分类 ==========
  75. const addBtn = page.locator('.add-btn, .create-category-btn');
  76. if (await addBtn.isVisible()) {
  77. await addBtn.click();
  78. }
  79. await page.waitForSelector('.category-form, .create-form', { timeout: 5000 });
  80. const nameInput = page.locator('input[name="name"], .category-name-input');
  81. if (await nameInput.isVisible()) {
  82. await nameInput.fill('测试分类_' + Date.now());
  83. }
  84. await page.click('.submit-btn, .save-btn');
  85. // ========== Then:分类创建成功 ========
  86. await page.waitForSelector('.success-tip, .create-success', { timeout: 10000 });
  87. const successTip = page.locator('.success-tip, .create-success');
  88. expect(await successTip.isVisible()).toBeTruthy();
  89. });
  90. // ===== 场景3:管理员设置精选文章 =====
  91. test('[场景3] 管理员设置精选文章 - 期望精选状态切换成功', async ({ page }) => {
  92. // ========== Given:管理员进入文章列表 ==========
  93. await page.goto('/#/pages/admin/article-list');
  94. await page.waitForLoadState('networkidle');
  95. // ========== When:设置精选 ==========
  96. await page.waitForSelector('.article-item', { timeout: 10000 });
  97. const firstArticle = page.locator('.article-item').first();
  98. const featuredBtn = firstArticle.locator('.featured-btn, .toggle-featured');
  99. if (await featuredBtn.isVisible()) {
  100. await featuredBtn.click();
  101. }
  102. // ========== Then:Milestone-2 - 精选设置成功 ========
  103. await page.waitForTimeout(500);
  104. // 验证精选标识变化
  105. const featuredTag = firstArticle.locator('.featured-tag, .is-featured');
  106. const isFeatured = await featuredTag.isVisible();
  107. expect(isFeatured).toBeTruthy();
  108. });
  109. // ===== 场景4:用户浏览文章列表 =====
  110. test('[场景4] 用户浏览文章列表 - 期望返回已发布文章', async ({ page }) => {
  111. // ========== Given:用户进入文章列表页 ==========
  112. await page.goto('/#/pages/mind/articles');
  113. await page.waitForLoadState('networkidle');
  114. // ========== When:浏览文章列表 ==========
  115. await page.waitForSelector('.article-list, .article-item', { timeout: 10000 });
  116. // ========== Then:Milestone-3 - 返回文章列表 ========
  117. const articleItems = page.locator('.article-item, .article-card');
  118. const count = await articleItems.count();
  119. expect(count).toBeGreaterThan(0);
  120. // 验证文章包含标题
  121. const firstArticle = articleItems.first();
  122. const title = firstArticle.locator('.article-title, .title');
  123. if (await title.isVisible()) {
  124. const titleText = await title.textContent();
  125. expect(titleText.length).toBeGreaterThan(0);
  126. }
  127. });
  128. // ===== 场景5:用户查看文章详情 =====
  129. test('[场景5] 用户查看文章详情 - 期望返回完整文章内容', async ({ page }) => {
  130. // ========== Given:用户进入文章详情页 ==========
  131. await page.goto('/#/pages/mind/articles');
  132. await page.waitForLoadState('networkidle');
  133. // ========== When:点击文章查看详情 ==========
  134. await page.waitForSelector('.article-item', { timeout: 10000 });
  135. await page.locator('.article-item').first().click();
  136. // ========== Then:返回完整文章详情 ========
  137. await page.waitForSelector('.article-detail, .detail-content, .article-content', { timeout: 10000 });
  138. const detailContent = page.locator('.article-detail, .detail-content');
  139. expect(await detailContent.isVisible()).toBeTruthy();
  140. // 验证包含正文内容
  141. const articleBody = page.locator('.article-body, .content');
  142. if (await articleBody.isVisible()) {
  143. const bodyText = await articleBody.textContent();
  144. expect(bodyText.length).toBeGreaterThan(0);
  145. }
  146. });
  147. });