| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- import { test, expect } from '@playwright/test';
- import { TenantLoginPage } from '../pages/tenant/tenant-login.page';
- import { TenantAdvertisementPage, TenantAdvertisementTypePage } from '../pages/tenant/tenant-advertisement.page';
- import testAdvertisements from '../fixtures/test-advertisements.json' with { type: 'json' };
- /**
- * E2E测试:租户后台统一广告管理UI交互
- *
- * 目的:验证租户后台的广告管理功能在实际浏览器环境中能够正常工作
- * 覆盖:登录、导航、CRUD操作、表单验证、分页、搜索等所有交互场景
- *
- * ## 测试前置条件
- *
- * 1. 数据库中存在测试租户(tenant_id=1)
- * 2. 数据库中存在测试超级管理员(username=admin, password=admin123)
- * 3. 测试环境可访问(http://localhost:8080 或 E2E_BASE_URL 指定的环境)
- *
- * ## 测试数据准备
- *
- * ```sql
- * -- 创建测试租户
- * INSERT INTO tenant_mt (id, name, code, status, created_at, updated_at)
- * VALUES (1, '测试租户', 'test-tenant', 1, NOW(), NOW());
- *
- * -- 创建测试超级管理员 (密码: admin123)
- * INSERT INTO users_mt (id, tenant_id, username, password, registration_source, is_disabled, is_deleted, created_at, updated_at)
- * VALUES (1, 1, 'admin', '$2b$10$x3t2kofPmACnk6y6lfL6ouU836LBEuZE9BinQ3ZzA4Xd04izyY42K', 'web', 0, 0, NOW(), NOW());
- * ```
- */
- // 测试配置
- const BASE_URL = process.env.E2E_BASE_URL || 'http://localhost:8080';
- const TEST_USERNAME = process.env.TEST_USERNAME || 'superadmin';
- const TEST_PASSWORD = process.env.TEST_PASSWORD || 'admin123';
- test.describe('租户后台统一广告管理UI交互测试', () => {
- let loginPage: TenantLoginPage;
- let advertisementPage: TenantAdvertisementPage;
- let typePage: TenantAdvertisementTypePage;
- // 每个测试前清除状态并登录
- test.beforeEach(async ({ page }) => {
- // 清除localStorage和cookies,确保干净的测试状态
- await page.context().clearCookies();
- await page.goto('/tenant/login');
- await page.evaluate(() => {
- localStorage.clear();
- sessionStorage.clear();
- });
- loginPage = new TenantLoginPage(page);
- advertisementPage = new TenantAdvertisementPage(page);
- typePage = new TenantAdvertisementTypePage(page);
- // 导航到登录页并登录
- await page.goto('/tenant/login');
- await loginPage.goto();
- await loginPage.login(TEST_USERNAME, TEST_PASSWORD);
- await loginPage.expectLoginSuccess();
- });
- test.describe('任务2: 登录流程测试', () => {
- test('应该成功登录并跳转到租户控制台', async ({ page }) => {
- await expect(page).toHaveURL(/\/tenant\/dashboard/);
- await expect(page.getByRole('heading', { name: /租户控制台|仪表盘|Dashboard/i })).toBeVisible();
- });
- test('应该显示错误提示当密码错误时', async ({ page }) => {
- // 登出
- await page.goto('/tenant/login');
- const loginPage2 = new TenantLoginPage(page);
- await loginPage2.login(TEST_USERNAME, 'wrongpassword');
- await loginPage2.expectLoginError();
- });
- });
- test.describe('任务3: 导航测试', () => {
- test('应该能够导航到广告管理页面', async ({ page }) => {
- // 点击广告管理菜单项
- const advertisementMenu = page.getByRole('link', { name: /广告管理/ }).or(
- page.getByText('广告管理').locator('..')
- );
- await advertisementMenu.click();
- await page.waitForLoadState('networkidle');
- await expect(page).toHaveURL(/\/tenant\/unified-advertisements/);
- await advertisementPage.pageTitle.toBeVisible();
- });
- test('应该能够导航到广告类型管理页面', async ({ page }) => {
- // 点击广告类型管理菜单项
- const typeMenu = page.getByRole('link', { name: /广告类型管理/ }).or(
- page.getByText('广告类型管理').locator('..')
- );
- await typeMenu.click();
- await page.waitForLoadState('networkidle');
- await expect(page).toHaveURL(/\/tenant\/unified-advertisement-types/);
- await typePage.pageTitle.toBeVisible();
- });
- });
- test.describe('任务4: 广告列表测试', () => {
- test.beforeEach(async ({ page }) => {
- await page.goto('/tenant/unified-advertisements');
- });
- test('应该正确显示广告列表页面', async () => {
- await expect(advertisementPage.pageTitle).toBeVisible();
- await expect(advertisementPage.createButton).toBeVisible();
- await expect(advertisementPage.searchInput).toBeVisible();
- });
- test('应该正确显示广告列表数据', async () => {
- const rowCount = await advertisementPage.getRowCount();
- // 至少应该显示表头,即使没有数据
- expect(rowCount).toBeGreaterThanOrEqual(0);
- });
- });
- test.describe('任务5: 创建广告测试', () => {
- test.beforeEach(async ({ page }) => {
- await page.goto('/tenant/unified-advertisements');
- });
- test('应该能够打开创建对话框', async () => {
- await advertisementPage.clickCreate();
- await advertisementPage.expectModalVisible(true);
- await expect(advertisementPage.modalTitle).toHaveText(/创建|新建/i);
- });
- test('应该能够成功创建广告', async () => {
- const timestamp = Date.now();
- const testData = {
- ...testAdvertisements.testAdvertisement,
- title: `${testAdvertisements.testAdvertisement.title}_${timestamp}`,
- code: `${testAdvertisements.testAdvertisement.code}_${timestamp}`
- };
- await advertisementPage.clickCreate();
- await advertisementPage.fillForm(testData);
- await advertisementPage.submitForm();
- await advertisementPage.expectModalVisible(false);
- });
- });
- test.describe('任务6: 编辑广告测试', () => {
- test('应该能够打开编辑对话框并更新广告', async ({ page }) => {
- await page.goto('/tenant/unified-advertisements');
- // 获取第一行的ID(如果存在)
- const firstRow = page.locator('tbody tr').first();
- const hasData = await firstRow.count() > 0;
- if (hasData) {
- // 查找编辑按钮
- const editButton = firstRow.getByTestId(/edit-button-\d+/);
- const editCount = await editButton.count();
- if (editCount > 0) {
- await editButton.first().click();
- await advertisementPage.expectModalVisible(true);
- // 修改标题
- const timestamp = Date.now();
- await advertisementPage.titleInput.fill(`更新标题_${timestamp}`);
- await advertisementPage.submitForm();
- await advertisementPage.expectModalVisible(false);
- } else {
- test.skip(true, '没有可编辑的广告');
- }
- } else {
- test.skip(true, '没有广告数据');
- }
- });
- });
- test.describe('任务7: 删除广告测试', () => {
- test('应该能够删除广告', async ({ page }) => {
- await page.goto('/tenant/unified-advertisements');
- const firstRow = page.locator('tbody tr').first();
- const hasData = await firstRow.count() > 0;
- if (hasData) {
- const deleteButton = firstRow.getByTestId(/delete-button-\d+/);
- const deleteCount = await deleteButton.count();
- if (deleteCount > 0) {
- const rowCountBefore = await advertisementPage.getRowCount();
- await deleteButton.first().click();
- await advertisementPage.expectDeleteDialogVisible(true);
- await advertisementPage.confirmDelete();
- // 等待对话框关闭
- await page.waitForTimeout(1000);
- // 验证删除后数量减少(或至少对话框关闭)
- await advertisementPage.expectDeleteDialogVisible(false);
- } else {
- test.skip(true, '没有可删除的广告');
- }
- } else {
- test.skip(true, '没有广告数据');
- }
- });
- test('应该能够取消删除操作', async ({ page }) => {
- await page.goto('/tenant/unified-advertisements');
- const firstRow = page.locator('tbody tr').first();
- const hasData = await firstRow.count() > 0;
- if (hasData) {
- const deleteButton = firstRow.getByTestId(/delete-button-\d+/);
- const deleteCount = await deleteButton.count();
- if (deleteCount > 0) {
- await deleteButton.first().click();
- await advertisementPage.expectDeleteDialogVisible(true);
- await advertisementPage.cancelDelete();
- await advertisementPage.expectDeleteDialogVisible(false);
- } else {
- test.skip(true, '没有可删除的广告');
- }
- } else {
- test.skip(true, '没有广告数据');
- }
- });
- });
- test.describe('任务8: 广告类型管理测试', () => {
- test.beforeEach(async ({ page }) => {
- await page.goto('/tenant/unified-advertisement-types');
- });
- test('应该正确显示广告类型列表页面', async () => {
- await expect(typePage.pageTitle).toBeVisible();
- await expect(typePage.createButton).toBeVisible();
- await expect(typePage.searchInput).toBeVisible();
- });
- test('应该能够创建广告类型', async () => {
- const timestamp = Date.now();
- const testData = {
- ...testAdvertisements.testAdvertisementType,
- name: `${testAdvertisements.testAdvertisementType.name}_${timestamp}`,
- code: `${testAdvertisements.testAdvertisementType.code}_${timestamp}`
- };
- await typePage.clickCreate();
- await typePage.expectModalVisible(true);
- await typePage.fillForm(testData);
- await typePage.submitForm();
- await typePage.expectModalVisible(false);
- });
- });
- test.describe('任务9: 分页功能测试', () => {
- test('应该显示分页组件', async ({ page }) => {
- await page.goto('/tenant/unified-advertisements');
- // 检查是否有分页组件(取决于数据量)
- const pagination = page.locator('[data-testid="pagination"]');
- const hasPagination = await pagination.count() > 0;
- if (hasPagination) {
- await expect(pagination).toBeVisible();
- } else {
- test.skip(true, '数据量不足,无分页组件');
- }
- });
- });
- test.describe('任务10: 搜索功能测试', () => {
- test('应该能够按标题搜索广告', async ({ page }) => {
- await page.goto('/tenant/unified-advertisements');
- // 输入搜索关键词
- await advertisementPage.search('测试');
- await page.waitForTimeout(1000);
- // 搜索应该执行(可能没有结果,但不会出错)
- const searchValue = await advertisementPage.searchInput.inputValue();
- expect(searchValue).toBe('测试');
- });
- test('应该能够清空搜索', async ({ page }) => {
- await page.goto('/tenant/unified-advertisements');
- await advertisementPage.search('测试');
- await page.waitForTimeout(500);
- await advertisementPage.searchInput.clear();
- await page.waitForTimeout(500);
- const searchValue = await advertisementPage.searchInput.inputValue();
- expect(searchValue).toBe('');
- });
- });
- test.describe('任务11: 表单验证测试', () => {
- test.beforeEach(async ({ page }) => {
- await page.goto('/tenant/unified-advertisements');
- });
- test('应该验证必填字段', async () => {
- await advertisementPage.clickCreate();
- // 尝试提交空表单
- await advertisementPage.submitForm();
- // 应该显示验证错误(具体错误信息取决于UI实现)
- await page.waitForTimeout(500);
- });
- test('应该要求输入广告标题', async () => {
- await advertisementPage.clickCreate();
- // 不填写标题,直接提交
- await advertisementPage.submitForm();
- // 标题输入框应该显示错误状态
- await page.waitForTimeout(500);
- });
- });
- test.describe('任务13: 响应式布局测试', () => {
- test('应该在桌面视图正常显示', async ({ page }) => {
- // 设置桌面视口
- await page.setViewportSize({ width: 1920, height: 1080 });
- await page.goto('/tenant/unified-advertisements');
- await expect(advertisementPage.pageTitle).toBeVisible();
- await expect(advertisementPage.createButton).toBeVisible();
- });
- test('应该在移动端视图正常显示', async ({ page }) => {
- // 设置移动端视口
- await page.setViewportSize({ width: 375, height: 667 });
- await page.goto('/tenant/unified-advertisements');
- await expect(advertisementPage.pageTitle).toBeVisible();
- // 移动端可能需要点击菜单按钮
- const menuButton = page.getByTestId('mobile-menu-button');
- const hasMenuButton = await menuButton.count() > 0;
- if (hasMenuButton) {
- await expect(menuButton).toBeVisible();
- }
- });
- });
- test.describe('完整用户流程测试', () => {
- test('应该完成完整的广告CRUD流程', async ({ page }) => {
- const timestamp = Date.now();
- // 1. 导航到广告管理
- await page.goto('/tenant/unified-advertisements');
- await expect(page).toHaveURL(/\/tenant\/unified-advertisements/);
- // 2. 创建新广告
- const testData = {
- ...testAdvertisements.testAdvertisement,
- title: `完整流程测试_${timestamp}`,
- code: `COMPLETE_FLOW_${timestamp}`
- };
- await advertisementPage.clickCreate();
- await advertisementPage.expectModalVisible(true);
- await advertisementPage.fillForm(testData);
- await advertisementPage.submitForm();
- await advertisementPage.expectModalVisible(false);
- // 3. 验证广告创建成功(搜索刚创建的广告)
- await page.waitForTimeout(1000);
- await advertisementPage.search(testData.title);
- await page.waitForTimeout(1000);
- // 4. 清空搜索
- await advertisementPage.searchInput.clear();
- await page.waitForTimeout(500);
- });
- });
- });
|