08ea5fbe98
添加用户管理视图、API和状态管理文件
405 lines
15 KiB
TypeScript
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);
|
|
});
|
|
});
|
|
});
|