ソースを参照

test: 修复故事010.007租户后台UI交互E2E测试

- 优化登录页面选择器
- 修复广告管理页面元素定位
- 增强E2E测试稳定性

🤖 Generated with [Claude Code](https://claude.com/claude-code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 2 週間 前
コミット
fcd53aaf25

+ 3 - 0
pnpm-lock.yaml

@@ -5323,6 +5323,9 @@ importers:
       '@d8d/tenant-management-ui':
         specifier: workspace:*
         version: link:../packages/tenant-management-ui
+      '@d8d/unified-advertisement-management-ui':
+        specifier: workspace:*
+        version: link:../packages/unified-advertisement-management-ui
       '@d8d/user-management-ui-mt':
         specifier: workspace:*
         version: link:../packages/user-management-ui-mt

+ 1 - 0
web/package.json

@@ -67,6 +67,7 @@
     "@d8d/credit-balance-management-ui-mt": "workspace:*",
     "@d8d/feie-printer-management-ui-mt": "workspace:*",
     "@d8d/tenant-management-ui": "workspace:*",
+    "@d8d/unified-advertisement-management-ui": "workspace:*",
     "@d8d/user-module": "workspace:*",
     "@heroicons/react": "^2.2.0",
     "@hono/node-server": "^1.17.1",

+ 5 - 5
web/tests/e2e/pages/tenant/tenant-advertisement.page.ts

@@ -49,7 +49,7 @@ export class TenantAdvertisementPage {
     this.modal = this.page.getByTestId('modal-title').locator('..').locator('..');
     this.modalTitle = this.page.getByTestId('modal-title');
     this.titleInput = this.page.getByTestId('title-input');
-    this.typeSelectTrigger = this.page.getByTestId('advertisement-type-select-trigger');
+    this.typeSelectTrigger = this.page.getByTestId('type-selector-trigger');
     this.codeInput = this.page.getByTestId('code-input');
     this.urlInput = this.page.getByTestId('url-input');
     this.actionTypeSelect = this.page.getByTestId('action-type-select');
@@ -57,10 +57,10 @@ export class TenantAdvertisementPage {
     this.statusSelect = this.page.getByTestId('status-select');
     this.submitButton = this.page.locator('[data-testid="create-submit-button"], [data-testid="update-submit-button"]');
 
-    // 删除对话框元素
-    this.deleteDialog = this.page.getByTestId('delete-dialog');
+    // 删除对话框元素 - 删除对话框使用text内容定位,因为没有data-testid
+    this.deleteDialog = this.page.getByText('确定要删除这个广告吗');
     this.deleteDialogTitle = this.page.getByTestId('delete-dialog-title');
-    this.deleteCancelButton = this.page.getByTestId('delete-cancel-button');
+    this.deleteCancelButton = this.page.getByRole('button', { name: '取消' }).filter({ has: this.deleteDialog });
     this.deleteConfirmButton = this.page.getByTestId('confirm-delete-button');
   }
 
@@ -95,7 +95,7 @@ export class TenantAdvertisementPage {
 
     if (data.typeId) {
       await this.typeSelectTrigger.click();
-      const typeOption = this.page.getByTestId(`advertisement-type-select-item-${data.typeId}`);
+      const typeOption = this.page.getByTestId(`type-selector-item-${data.typeId}`);
       await typeOption.click();
     }
 

+ 9 - 17
web/tests/e2e/pages/tenant/tenant-login.page.ts

@@ -19,32 +19,21 @@ export class TenantLoginPage {
     this.usernameInput = page.getByPlaceholder('请输入用户名');
     this.passwordInput = page.getByPlaceholder('请输入密码');
     this.submitButton = page.getByRole('button', { name: '登录' });
-    this.pageTitle = page.getByRole('heading', { name: /租户.*登录|登录/i });
+    this.pageTitle = page.getByText('管理后台登录');
     this.initializingText = page.getByText('应用初始化中');
   }
 
   async goto() {
     await this.page.goto('/tenant/login');
 
-    // 等待应用初始化完成
-    try {
-      await expect(this.initializingText).not.toBeVisible({ timeout: 30000 });
-    } catch {
-      // 如果初始化文本没有出现,继续
-    }
+    // 等待页面加载完成 - 先等待输入框出现(更可靠)
+    await this.page.waitForLoadState('domcontentloaded');
 
-    // 等待登录表单可见
-    await expect(this.pageTitle).toBeVisible({ timeout: 30000 });
+    // 等待用户名输入框可见作为页面已加载的标志
+    await expect(this.usernameInput).toBeVisible({ timeout: 30000 });
   }
 
   async login(username: string, password: string) {
-    // 确保应用已初始化
-    try {
-      await expect(this.initializingText).not.toBeVisible({ timeout: 10000 });
-    } catch {
-      // 继续尝试
-    }
-
     // 等待输入框可见
     await expect(this.usernameInput).toBeVisible({ timeout: 10000 });
     await expect(this.passwordInput).toBeVisible({ timeout: 10000 });
@@ -52,7 +41,10 @@ export class TenantLoginPage {
     await this.usernameInput.fill(username);
     await this.passwordInput.fill(password);
     await this.submitButton.click();
-    await this.page.waitForLoadState('networkidle');
+
+    // 等待登录后跳转(等待URL变化或dashboard出现)
+    // 不使用networkidle因为后台可能持续有请求
+    await this.page.waitForURL(/\/tenant\/dashboard/, { timeout: 15000 });
   }
 
   async expectLoginSuccess() {

+ 12 - 3
web/tests/e2e/specs/tenant-advertisement-ui.spec.ts

@@ -30,7 +30,7 @@ import testAdvertisements from '../fixtures/test-advertisements.json' with { typ
 
 // 测试配置
 const BASE_URL = process.env.E2E_BASE_URL || 'http://localhost:8080';
-const TEST_USERNAME = process.env.TEST_USERNAME || 'admin';
+const TEST_USERNAME = process.env.TEST_USERNAME || 'superadmin';
 const TEST_PASSWORD = process.env.TEST_PASSWORD || 'admin123';
 
 test.describe('租户后台统一广告管理UI交互测试', () => {
@@ -38,13 +38,22 @@ test.describe('租户后台统一广告管理UI交互测试', () => {
   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();