| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- import { Page, Locator, expect } from '@playwright/test';
- export class RouteManagementPage {
- readonly page: Page;
- readonly pageTitle: Locator;
- readonly createRouteButton: Locator;
- readonly searchInput: Locator;
- readonly routeTable: Locator;
- readonly editButtons: Locator;
- readonly deleteButtons: Locator;
- readonly statusToggleButtons: Locator;
- readonly pagination: Locator;
- readonly vehicleTypeFilter: Locator;
- constructor(page: Page) {
- this.page = page;
- this.pageTitle = page.locator('[data-testid="route-management-title"]');
- this.createRouteButton = page.locator('[data-testid="create-route-button"]');
- this.searchInput = page.locator('[data-testid="route-search-input"]');
- this.routeTable = page.locator('[data-testid="route-table"]');
- this.editButtons = page.locator('[data-testid^="edit-route-"]');
- this.deleteButtons = page.locator('[data-testid^="delete-route-"]');
- this.statusToggleButtons = page.locator('[data-testid^="toggle-route-"]');
- this.pagination = page.locator('[data-slot="pagination"]');
- this.vehicleTypeFilter = page.locator('[data-testid="route-vehicle-type-filter"]');
- }
- async goto() {
- // 直接导航到路线管理页面
- await this.page.goto('/admin/routes');
- // 等待页面完全加载
- await this.page.waitForLoadState('domcontentloaded');
- // 等待路线管理标题出现
- await this.page.waitForSelector('[data-testid="route-management-title"]', { state: 'visible', timeout: 15000 });
- // 等待表格数据加载完成
- await this.page.waitForSelector('[data-testid="route-table"] tbody tr', { state: 'visible', timeout: 20000 });
- await this.expectToBeVisible();
- }
- async expectToBeVisible() {
- // 等待页面完全加载
- await expect(this.pageTitle).toBeVisible({ timeout: 15000 });
- await expect(this.createRouteButton).toBeVisible({ timeout: 10000 });
- // 等待至少一行路线数据加载完成
- await expect(this.routeTable.locator('tbody tr').first()).toBeVisible({ timeout: 20000 });
- }
- async searchRoutes(keyword: string) {
- await this.searchInput.fill(keyword);
- // 防抖搜索,等待网络请求完成
- await this.page.waitForTimeout(500); // 等待防抖延迟
- await this.page.waitForLoadState('networkidle');
- }
- async filterByVehicleType(type: '大巴' | '中巴' | '小车') {
- await this.vehicleTypeFilter.click();
- await this.page.getByRole('option', { name: type }).click();
- await this.page.waitForLoadState('networkidle');
- }
- async createRoute(routeData: {
- name: string;
- startPoint: string;
- endPoint: string;
- vehicleType: 'bus' | 'van' | 'car';
- price: number;
- seatCount: number;
- departureTime: string;
- activityId: number;
- }) {
- await this.createRouteButton.click();
- // 填写路线表单
- await this.page.getByLabel('路线名称').fill(routeData.name);
- await this.page.getByLabel('出发地').fill(routeData.startPoint);
- await this.page.getByLabel('目的地').fill(routeData.endPoint);
- // 选择车型
- await this.page.getByLabel('车型').click();
- const vehicleTypeMap = {
- 'bus': '大巴',
- 'van': '中巴',
- 'car': '小车'
- };
- await this.page.getByRole('option', { name: vehicleTypeMap[routeData.vehicleType] }).click();
- // 填写价格和座位数
- await this.page.getByLabel('价格').fill(routeData.price.toString());
- await this.page.getByLabel('座位数').fill(routeData.seatCount.toString());
- // 填写出发时间
- await this.page.getByLabel('出发时间').fill(routeData.departureTime);
- // 选择活动
- await this.page.getByLabel('关联活动').click();
- await this.page.getByRole('option').first().click();
- // 提交表单 - 使用模态框中的创建按钮
- await this.page.locator('[role="dialog"]').getByRole('button', { name: '创建路线' }).click();
- await this.page.waitForLoadState('networkidle');
- // 等待路线创建结果提示
- try {
- await Promise.race([
- this.page.waitForSelector('text=创建成功', { timeout: 10000 }),
- this.page.waitForSelector('text=创建失败', { timeout: 10000 })
- ]);
- // 检查是否有错误提示
- const errorVisible = await this.page.locator('text=创建失败').isVisible().catch(() => false);
- if (errorVisible) {
- return;
- }
- // 如果是创建成功,刷新页面
- await this.page.waitForTimeout(1000);
- await this.page.reload();
- await this.page.waitForLoadState('networkidle');
- await this.expectToBeVisible();
- } catch (error) {
- // 如果没有提示出现,继续执行
- console.log('创建操作没有显示提示信息,继续执行');
- await this.page.reload();
- await this.page.waitForLoadState('networkidle');
- await this.expectToBeVisible();
- }
- }
- async getRouteCount(): Promise<number> {
- const rows = await this.routeTable.locator('tbody tr').count();
- // 如果只有一行且包含"暂无路线数据",则返回0
- if (rows === 1) {
- const firstRow = this.routeTable.locator('tbody tr').first();
- const rowText = await firstRow.textContent();
- if (rowText && rowText.includes('暂无路线数据')) {
- return 0;
- }
- }
- return rows;
- }
- async getRouteByName(name: string): Promise<Locator | null> {
- const routeRow = this.routeTable.locator('tbody tr').filter({ hasText: name }).first();
- return (await routeRow.count()) > 0 ? routeRow : null;
- }
- async routeExists(name: string): Promise<boolean> {
- const routeRow = this.routeTable.locator('tbody tr').filter({ hasText: name }).first();
- return (await routeRow.count()) > 0;
- }
- async editRoute(name: string, updates: {
- name?: string;
- startPoint?: string;
- endPoint?: string;
- vehicleType?: 'bus' | 'van' | 'car';
- price?: number;
- seatCount?: number;
- departureTime?: string;
- }) {
- const routeRow = await this.getRouteByName(name);
- if (!routeRow) throw new Error(`Route ${name} not found`);
- // 编辑按钮
- const editButton = routeRow.locator('[data-testid^="edit-route-"]');
- await editButton.waitFor({ state: 'visible', timeout: 10000 });
- await editButton.click();
- // 等待编辑模态框出现
- await this.page.waitForSelector('[role="dialog"]', { state: 'visible', timeout: 10000 });
- // 更新字段
- if (updates.name) {
- await this.page.getByLabel('路线名称').fill(updates.name);
- }
- if (updates.startPoint) {
- await this.page.getByLabel('出发地').fill(updates.startPoint);
- }
- if (updates.endPoint) {
- await this.page.getByLabel('目的地').fill(updates.endPoint);
- }
- if (updates.vehicleType) {
- await this.page.getByLabel('车型').click();
- const vehicleTypeMap = {
- 'bus': '大巴',
- 'van': '中巴',
- 'car': '小车'
- };
- await this.page.getByRole('option', { name: vehicleTypeMap[updates.vehicleType] }).click();
- }
- if (updates.price) {
- await this.page.getByLabel('价格').fill(updates.price.toString());
- }
- if (updates.seatCount) {
- await this.page.getByLabel('座位数').fill(updates.seatCount.toString());
- }
- if (updates.departureTime) {
- await this.page.getByLabel('出发时间').fill(updates.departureTime);
- }
- // 提交更新
- await this.page.locator('[role="dialog"]').getByRole('button', { name: '更新路线' }).click();
- await this.page.waitForLoadState('networkidle');
- // 等待操作完成
- await this.page.waitForTimeout(1000);
- }
- async deleteRoute(name: string) {
- const routeRow = await this.getRouteByName(name);
- if (!routeRow) throw new Error(`Route ${name} not found`);
- // 删除按钮
- const deleteButton = routeRow.locator('[data-testid^="delete-route-"]');
- await deleteButton.waitFor({ state: 'visible', timeout: 10000 });
- await deleteButton.click();
- // 确认删除对话框
- await this.page.getByRole('button', { name: '删除' }).click();
- // 等待删除操作完成
- try {
- await Promise.race([
- this.page.waitForSelector('text=删除成功', { timeout: 10000 }),
- this.page.waitForSelector('text=删除失败', { timeout: 10000 })
- ]);
- const errorVisible = await this.page.locator('text=删除失败').isVisible().catch(() => false);
- if (errorVisible) {
- throw new Error('删除操作失败:前端显示删除失败提示');
- }
- } catch (error) {
- console.log('删除操作没有显示提示信息,继续执行');
- }
- // 刷新页面确认路线是否被删除
- await this.page.reload();
- await this.page.waitForLoadState('networkidle');
- await this.expectToBeVisible();
- }
- async toggleRouteStatus(name: string) {
- const routeRow = await this.getRouteByName(name);
- if (!routeRow) throw new Error(`Route ${name} not found`);
- // 状态切换按钮
- const statusButton = routeRow.locator('[data-testid^="toggle-route-"]');
- await statusButton.waitFor({ state: 'visible', timeout: 10000 });
- const currentStatus = await statusButton.textContent();
- await statusButton.click();
- // 确认状态切换对话框
- await this.page.getByRole('button', { name: '确认' }).click();
- // 等待操作完成
- await this.page.waitForLoadState('networkidle');
- await this.page.waitForTimeout(1000);
- return currentStatus;
- }
- async expectRouteExists(name: string) {
- const exists = await this.routeExists(name);
- expect(exists).toBe(true);
- }
- async expectRouteNotExists(name: string) {
- const exists = await this.routeExists(name);
- expect(exists).toBe(false);
- }
- async getRouteStatus(name: string): Promise<string | null> {
- const routeRow = await this.getRouteByName(name);
- if (!routeRow) return null;
- const statusButton = routeRow.locator('[data-testid^="toggle-route-"]');
- return await statusButton.textContent();
- }
- }
|