import { test, expect } from '@playwright/test'; test.describe('黄历搜索功能E2E测试', () => { let baseUrl: string; test.beforeAll(async () => { baseUrl = 'http://localhost:8081'; }); test.beforeEach(async ({ page }) => { await page.goto(`${baseUrl}/#/pages/almanac-search/index`); await page.waitForLoadState('networkidle'); }); test.describe('TC-SEARCH-001: 搜索条件添加流程', () => { test('应该能够添加搜索条件', async ({ page }) => { const addButton = page.locator('.add-condition-button, [class*="add-condition"], button:has-text("添加条件")'); if (await addButton.isVisible()) { const initialCount = await page.locator('.search-condition-item, [class*="SearchConditionItem"]').count(); await addButton.click(); await page.waitForTimeout(500); const newCount = await page.locator('.search-condition-item, [class*="SearchConditionItem"]').count(); expect(newCount).toBe(initialCount + 1); } else { test.skip(); } }); test('应该能够添加多个搜索条件', async ({ page }) => { const addButton = page.locator('.add-condition-button, [class*="add-condition"], button:has-text("添加条件")'); if (await addButton.isVisible()) { for (let i = 0; i < 3; i++) { await addButton.click(); await page.waitForTimeout(300); } const conditionCount = await page.locator('.search-condition-item, [class*="SearchConditionItem"]').count(); expect(conditionCount).toBeGreaterThanOrEqual(3); } else { test.skip(); } }); }); test.describe('TC-SEARCH-002: 搜索条件删除流程', () => { test('应该能够删除搜索条件', async ({ page }) => { const addButton = page.locator('.add-condition-button, [class*="add-condition"], button:has-text("添加条件")'); if (await addButton.isVisible()) { await addButton.click(); await page.waitForTimeout(500); const deleteButton = page.locator('.delete-button, [class*="delete"], button:has-text("删除")').first(); if (await deleteButton.isVisible()) { const initialCount = await page.locator('.search-condition-item, [class*="SearchConditionItem"]').count(); await deleteButton.click(); await page.waitForTimeout(300); const newCount = await page.locator('.search-condition-item, [class*="SearchConditionItem"]').count(); expect(newCount).toBe(initialCount - 1); } } else { test.skip(); } }); test('删除最后一个条件后应该能够重新添加', async ({ page }) => { const addButton = page.locator('.add-condition-button, [class*="add-condition"], button:has-text("添加条件")'); if (await addButton.isVisible()) { await addButton.click(); await page.waitForTimeout(500); const deleteButton = page.locator('.delete-button, [class*="delete"], button:has-text("删除")').first(); if (await deleteButton.isVisible()) { await deleteButton.click(); await page.waitForTimeout(300); await addButton.click(); await page.waitForTimeout(300); const conditionCount = await page.locator('.search-condition-item, [class*="SearchConditionItem"]').count(); expect(conditionCount).toBeGreaterThan(0); } } else { test.skip(); } }); }); test.describe('TC-SEARCH-003: 搜索条件保存流程', () => { test('应该能够保存搜索条件', async ({ page }) => { const saveButton = page.locator('.save-condition-button, [class*="save-condition"], button:has-text("保存条件")'); if (await saveButton.isVisible()) { await saveButton.click(); await page.waitForTimeout(500); const modal = page.locator('.modal, [class*="modal"], .dialog').first(); if (await modal.isVisible()) { const nameInput = page.locator('input[type="text"], [class*="input"]').first(); if (await nameInput.isVisible()) { await nameInput.fill('测试搜索条件'); const confirmButton = page.locator('button:has-text("确认"), button:has-text("保存")').first(); if (await confirmButton.isVisible()) { await confirmButton.click(); await page.waitForTimeout(500); const savedConditions = page.locator('.saved-condition, [class*="saved-condition"]'); expect(await savedConditions.count()).toBeGreaterThan(0); } } } } else { test.skip(); } }); test('保存的条件应该显示在已保存条件列表中', async ({ page }) => { const saveButton = page.locator('.save-condition-button, [class*="save-condition"], button:has-text("保存条件")'); if (await saveButton.isVisible()) { await saveButton.click(); await page.waitForTimeout(500); const modal = page.locator('.modal, [class*="modal"], .dialog').first(); if (await modal.isVisible()) { const nameInput = page.locator('input[type="text"], [class*="input"]').first(); if (await nameInput.isVisible()) { await nameInput.fill('测试条件'); const confirmButton = page.locator('button:has-text("确认"), button:has-text("保存")').first(); if (await confirmButton.isVisible()) { await confirmButton.click(); await page.waitForTimeout(500); const savedCondition = page.locator('.saved-condition:has-text("测试条件"), [class*="saved-condition"]:has-text("测试条件")'); expect(await savedCondition.isVisible()).toBe(true); } } } } else { test.skip(); } }); }); test.describe('TC-SEARCH-004: 搜索条件加载流程', () => { test('应该能够加载已保存的搜索条件', async ({ page }) => { const savedCondition = page.locator('.saved-condition, [class*="saved-condition"]').first(); if (await savedCondition.isVisible()) { await savedCondition.click(); await page.waitForTimeout(500); const conditionItems = page.locator('.search-condition-item, [class*="SearchConditionItem"]'); expect(await conditionItems.count()).toBeGreaterThan(0); } else { test.skip(); } }); test('加载的条件应该正确显示', async ({ page }) => { const savedCondition = page.locator('.saved-condition, [class*="saved-condition"]').first(); if (await savedCondition.isVisible()) { await savedCondition.click(); await page.waitForTimeout(500); const conditionItems = page.locator('.search-condition-item, [class*="SearchConditionItem"]'); if (await conditionItems.count() > 0) { const firstCondition = conditionItems.first(); expect(await firstCondition.isVisible()).toBe(true); } } else { test.skip(); } }); }); test.describe('TC-SEARCH-005: 搜索执行流程', () => { test('应该能够执行搜索', async ({ page }) => { const searchButton = page.locator('.search-button, [class*="search-button"], button:has-text("搜索")'); if (await searchButton.isVisible()) { await searchButton.click(); await page.waitForTimeout(1000); const loadingIndicator = page.locator('.loading-indicator, [class*="loading"]'); const results = page.locator('.search-result-card, [class*="SearchResultCard"]'); const loadingVisible = await loadingIndicator.isVisible().catch(() => false); const resultsVisible = await results.isVisible().catch(() => false); expect(loadingVisible || resultsVisible).toBe(true); } else { test.skip(); } }); test('搜索过程中应该显示加载状态', async ({ page }) => { const searchButton = page.locator('.search-button, [class*="search-button"], button:has-text("搜索")'); if (await searchButton.isVisible()) { await searchButton.click(); const loadingIndicator = page.locator('.loading-indicator, [class*="loading"]'); expect(await loadingIndicator.isVisible()).toBe(true); } else { test.skip(); } }); }); test.describe('TC-SEARCH-006: 结果排序流程', () => { test('应该能够按日期排序', async ({ page }) => { const sortOption = page.locator('.sort-option[data-sort="date"], [class*="sort-option"]:has-text("日期")'); if (await sortOption.isVisible()) { await sortOption.click(); await page.waitForTimeout(500); const results = page.locator('.search-result-card, [class*="SearchResultCard"]'); if (await results.count() > 0) { expect(await results.first().isVisible()).toBe(true); } } else { test.skip(); } }); test('应该能够按匹配度排序', async ({ page }) => { const sortOption = page.locator('.sort-option[data-sort="matchCount"], [class*="sort-option"]:has-text("匹配度")'); if (await sortOption.isVisible()) { await sortOption.click(); await page.waitForTimeout(500); const results = page.locator('.search-result-card, [class*="SearchResultCard"]'); if (await results.count() > 0) { expect(await results.first().isVisible()).toBe(true); } } else { test.skip(); } }); test('应该能够切换升序/降序', async ({ page }) => { const sortArrow = page.locator('.sort-arrow, [class*="sort-arrow"]'); if (await sortArrow.isVisible()) { await sortArrow.click(); await page.waitForTimeout(500); expect(await sortArrow.isVisible()).toBe(true); } else { test.skip(); } }); }); test.describe('TC-SEARCH-007: 结果加载流程', () => { test('应该能够加载搜索结果', async ({ page }) => { const searchButton = page.locator('.search-button, [class*="search-button"], button:has-text("搜索")'); if (await searchButton.isVisible()) { await searchButton.click(); await page.waitForTimeout(2000); const results = page.locator('.search-result-card, [class*="SearchResultCard"]'); const resultCount = await results.count(); if (resultCount > 0) { expect(await results.first().isVisible()).toBe(true); } } else { test.skip(); } }); test('应该支持无限滚动加载', async ({ page }) => { const searchButton = page.locator('.search-button, [class*="search-button"], button:has-text("搜索")'); if (await searchButton.isVisible()) { await searchButton.click(); await page.waitForTimeout(2000); const initialCount = await page.locator('.search-result-card, [class*="SearchResultCard"]').count(); if (initialCount > 0) { await page.evaluate(() => { window.scrollTo(0, document.body.scrollHeight); }); await page.waitForTimeout(1000); const newCount = await page.locator('.search-result-card, [class*="SearchResultCard"]').count(); expect(newCount).toBeGreaterThanOrEqual(initialCount); } } else { test.skip(); } }); }); test.describe('TC-SEARCH-008: 空状态显示', () => { test('无结果时应该显示空状态', async ({ page }) => { const searchButton = page.locator('.search-button, [class*="search-button"], button:has-text("搜索")'); if (await searchButton.isVisible()) { await searchButton.click(); await page.waitForTimeout(2000); const results = page.locator('.search-result-card, [class*="SearchResultCard"]'); const resultCount = await results.count(); if (resultCount === 0) { const emptyState = page.locator('.empty-state, [class*="EmptyState"]'); expect(await emptyState.isVisible()).toBe(true); } } else { test.skip(); } }); test('空状态应该显示提示信息', async ({ page }) => { const searchButton = page.locator('.search-button, [class*="search-button"], button:has-text("搜索")'); if (await searchButton.isVisible()) { await searchButton.click(); await page.waitForTimeout(2000); const results = page.locator('.search-result-card, [class*="SearchResultCard"]'); const resultCount = await results.count(); if (resultCount === 0) { const emptyState = page.locator('.empty-state, [class*="EmptyState"]'); if (await emptyState.isVisible()) { const text = await emptyState.textContent(); expect(text).toBeTruthy(); expect(text!.length).toBeGreaterThan(0); } } } else { test.skip(); } }); }); test.describe('TC-SEARCH-009: 错误状态处理', () => { test('网络错误时应该显示错误提示', async ({ page }) => { await page.route('**/api/**', route => route.abort()); const searchButton = page.locator('.search-button, [class*="search-button"], button:has-text("搜索")'); if (await searchButton.isVisible()) { await searchButton.click(); await page.waitForTimeout(2000); const errorState = page.locator('.error-state, [class*="error"], .error-message'); const errorVisible = await errorState.isVisible().catch(() => false); expect(errorVisible).toBe(true); } else { test.skip(); } }); }); test.describe('TC-SEARCH-010: 响应式设计', () => { test('应该在移动端正常显示', async ({ page }) => { await page.setViewportSize({ width: 375, height: 667 }); await page.reload(); await page.waitForLoadState('networkidle'); const searchPanel = page.locator('.search-condition-panel, [class*="SearchConditionPanel"]'); expect(await searchPanel.isVisible()).toBe(true); }); test('应该在桌面端正常显示', async ({ page }) => { await page.setViewportSize({ width: 1920, height: 1080 }); await page.reload(); await page.waitForLoadState('networkidle'); const searchPanel = page.locator('.search-condition-panel, [class*="SearchConditionPanel"]'); expect(await searchPanel.isVisible()).toBe(true); }); test('应该在平板端正常显示', async ({ page }) => { await page.setViewportSize({ width: 768, height: 1024 }); await page.reload(); await page.waitForLoadState('networkidle'); const searchPanel = page.locator('.search-condition-panel, [class*="SearchConditionPanel"]'); expect(await searchPanel.isVisible()).toBe(true); }); }); });