Files
张翔 08ea5fbe98 feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
2026-03-28 14:37:29 +08:00

405 lines
15 KiB
TypeScript

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);
});
});
});