import { test, expect } from "../fixtures"; test.describe("竞品分析 - 添加竞品交互测试", () => { test.beforeEach(async ({ authenticatedPage }) => { await authenticatedPage.goto("/dashboard/competitors"); await authenticatedPage.waitForLoadState("networkidle"); }); test("点击添加竞品按钮打开对话框", async ({ authenticatedPage }) => { const addBtn = authenticatedPage.getByRole("button", { name: /添加竞品/ }); await expect(addBtn).toBeVisible({ timeout: 15000 }); await addBtn.click(); const dialog = authenticatedPage.getByRole("dialog"); await expect(dialog).toBeVisible({ timeout: 10000 }); // 验证对话框标题 await expect(dialog.getByRole("heading", { name: "添加竞品" })).toBeVisible(); }); test("添加竞品对话框切换到手动输入Tab", async ({ authenticatedPage }) => { const addBtn = authenticatedPage.getByRole("button", { name: /添加竞品/ }); await expect(addBtn).toBeVisible({ timeout: 15000 }); await addBtn.click(); const dialog = authenticatedPage.getByRole("dialog"); await expect(dialog).toBeVisible({ timeout: 10000 }); // 切换到手动输入Tab const manualTab = dialog.getByRole("tab", { name: "手动输入" }); await expect(manualTab).toBeVisible(); await manualTab.click(); // 验证手动输入表单 const nameInput = dialog.locator("#competitor-name"); const hasInput = await nameInput.isVisible({ timeout: 5000 }).catch(() => false); if (!hasInput) { test.skip(); return; } await expect(nameInput).toBeVisible(); await expect(nameInput).toHaveAttribute("placeholder", "输入竞品名称"); }); test("手动输入竞品名称并添加", async ({ authenticatedPage }) => { const addBtn = authenticatedPage.getByRole("button", { name: /添加竞品/ }); await expect(addBtn).toBeVisible({ timeout: 15000 }); // 检查是否已达上限(5个竞品) const isDisabled = await addBtn.isDisabled(); if (isDisabled) { test.skip(); return; } await addBtn.click(); const dialog = authenticatedPage.getByRole("dialog"); await expect(dialog).toBeVisible({ timeout: 10000 }); // 切换到手动输入Tab const manualTab = dialog.getByRole("tab", { name: "手动输入" }); await manualTab.click(); // 输入竞品名称 const nameInput = dialog.locator("#competitor-name"); const hasInput = await nameInput.isVisible({ timeout: 5000 }).catch(() => false); if (!hasInput) { test.skip(); return; } await nameInput.fill("测试竞品E2E"); // 点击添加按钮 const submitBtn = dialog.getByRole("button", { name: "添加" }); await expect(submitBtn).toBeEnabled(); await submitBtn.click(); // 验证对话框关闭或竞品列表刷新 await authenticatedPage.waitForTimeout(2000); }); test("从推荐选择Tab查看推荐竞品", async ({ authenticatedPage }) => { const addBtn = authenticatedPage.getByRole("button", { name: /添加竞品/ }); await expect(addBtn).toBeVisible({ timeout: 15000 }); const isDisabled = await addBtn.isDisabled(); if (isDisabled) { test.skip(); return; } await addBtn.click(); const dialog = authenticatedPage.getByRole("dialog"); await expect(dialog).toBeVisible({ timeout: 10000 }); // 默认在推荐Tab const recommendTab = dialog.getByRole("tab", { name: "从推荐选择" }); await expect(recommendTab).toBeVisible(); // 验证推荐内容加载(可能是推荐列表或空状态) const loadingSpinner = dialog.locator(".animate-spin"); const emptyState = dialog.getByText("暂无推荐竞品"); const recommendItem = dialog.locator("div.grid.gap-2 > div.flex.items-center"); const hasLoading = await loadingSpinner.isVisible({ timeout: 3000 }).catch(() => false); const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false); const hasItems = await recommendItem.first().isVisible({ timeout: 5000 }).catch(() => false); expect(hasLoading || hasEmpty || hasItems).toBeTruthy(); }); }); test.describe("竞品分析 - 竞品对比数据交互测试", () => { test.beforeEach(async ({ authenticatedPage }) => { await authenticatedPage.goto("/dashboard/competitors"); await authenticatedPage.waitForLoadState("networkidle"); }); test("竞品列表显示已添加的竞品", async ({ authenticatedPage }) => { // 等待竞品列表加载 await authenticatedPage.waitForTimeout(2000); const emptyState = authenticatedPage.getByText("暂无竞品"); const hasEmpty = await emptyState.isVisible({ timeout: 10000 }).catch(() => false); if (hasEmpty) { test.skip(); return; } // 验证竞品卡片存在 const competitorCards = authenticatedPage.locator("div.grid.gap-3 > div.flex.items-center.justify-between"); const cardCount = await competitorCards.count(); if (cardCount === 0) { test.skip(); return; } expect(cardCount).toBeGreaterThan(0); }); test("竞品列表显示数量Badge", async ({ authenticatedPage }) => { const countBadge = authenticatedPage.locator("span.text-xs").filter({ hasText: /^\d\/5$/ }); const hasBadge = await countBadge.isVisible({ timeout: 10000 }).catch(() => false); if (!hasBadge) { test.skip(); return; } const badgeText = await countBadge.textContent(); expect(badgeText).toMatch(/^\d\/5$/); }); test("选择竞品和分析类型后可执行分析", async ({ authenticatedPage }) => { await authenticatedPage.waitForTimeout(2000); // 验证竞品列表非空 const emptyState = authenticatedPage.getByText("暂无竞品"); const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false); if (hasEmpty) { test.skip(); return; } // 查找"选择竞品"下拉框 const competitorSelectLabel = authenticatedPage.getByText("选择竞品"); const hasSelectLabel = await competitorSelectLabel.isVisible({ timeout: 10000 }).catch(() => false); if (!hasSelectLabel) { test.skip(); return; } // 点击选择竞品下拉框 const competitorSelectTrigger = competitorSelectLabel.locator("..").locator("button"); const hasTrigger = await competitorSelectTrigger.isVisible({ timeout: 3000 }).catch(() => false); if (!hasTrigger) { test.skip(); return; } await competitorSelectTrigger.click(); // 选择第一个竞品选项 const selectContent = authenticatedPage.locator("[data-radix-popper-content-wrapper], [role='listbox']").first(); const hasOptions = await selectContent.isVisible({ timeout: 5000 }).catch(() => false); if (!hasOptions) { test.skip(); return; } const firstOption = selectContent.locator("[role='option'], [data-radix-collection-item]").first(); const hasFirstOption = await firstOption.isVisible({ timeout: 3000 }).catch(() => false); if (hasFirstOption) { await firstOption.click(); } // 选择分析类型 const analysisTypeLabel = authenticatedPage.getByText("分析类型"); const hasTypeLabel = await analysisTypeLabel.isVisible({ timeout: 5000 }).catch(() => false); if (!hasTypeLabel) { test.skip(); return; } const analysisTypeTrigger = analysisTypeLabel.locator("..").locator("button"); const hasTypeTrigger = await analysisTypeTrigger.isVisible({ timeout: 3000 }).catch(() => false); if (!hasTypeTrigger) { test.skip(); return; } await analysisTypeTrigger.click(); const typeSelectContent = authenticatedPage.locator("[data-radix-popper-content-wrapper], [role='listbox']").first(); const hasTypeOptions = await typeSelectContent.isVisible({ timeout: 5000 }).catch(() => false); if (hasTypeOptions) { const firstTypeOption = typeSelectContent.locator("[role='option'], [data-radix-collection-item]").first(); const hasFirstType = await firstTypeOption.isVisible({ timeout: 3000 }).catch(() => false); if (hasFirstType) { await firstTypeOption.click(); } } // 验证"开始分析"按钮 const analyzeBtn = authenticatedPage.getByRole("button", { name: /开始分析/ }); const hasAnalyzeBtn = await analyzeBtn.isVisible({ timeout: 3000 }).catch(() => false); if (!hasAnalyzeBtn) { test.skip(); return; } // 按钮应该可点击 await expect(analyzeBtn).toBeEnabled(); }); test("竞品雷达图或空状态显示", async ({ authenticatedPage }) => { await authenticatedPage.waitForTimeout(2000); const radarChart = authenticatedPage.locator(".recharts-radar"); const emptyRadar = authenticatedPage.getByText("暂无对比数据"); const hasChart = await radarChart.isVisible({ timeout: 10000 }).catch(() => false); const hasEmpty = await emptyRadar.isVisible({ timeout: 5000 }).catch(() => false); if (!hasChart && !hasEmpty) { test.skip(); return; } expect(hasChart || hasEmpty).toBeTruthy(); }); test("差距评分区域显示", async ({ authenticatedPage }) => { await authenticatedPage.waitForTimeout(2000); const gapTitle = authenticatedPage.getByText("差距评分"); const hasTitle = await gapTitle.isVisible({ timeout: 10000 }).catch(() => false); if (!hasTitle) { test.skip(); return; } const emptyGap = authenticatedPage.getByText("暂无差距评分"); const gapItem = authenticatedPage.locator("div.rounded-lg.border.p-4.space-y-2"); const hasEmpty = await emptyGap.isVisible({ timeout: 5000 }).catch(() => false); const hasItems = await gapItem.first().isVisible({ timeout: 5000 }).catch(() => false); expect(hasEmpty || hasItems).toBeTruthy(); }); }); test.describe("竞品分析 - 删除竞品交互测试", () => { test.beforeEach(async ({ authenticatedPage }) => { await authenticatedPage.goto("/dashboard/competitors"); await authenticatedPage.waitForLoadState("networkidle"); }); test("竞品卡片显示删除按钮", async ({ authenticatedPage }) => { await authenticatedPage.waitForTimeout(2000); const emptyState = authenticatedPage.getByText("暂无竞品"); const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false); if (hasEmpty) { test.skip(); return; } // 查找竞品卡片中的删除按钮(Trash2 图标按钮) const deleteButtons = authenticatedPage.locator("button").filter({ has: authenticatedPage.locator("svg.lucide-trash-2") }); const count = await deleteButtons.count(); if (count === 0) { test.skip(); return; } expect(count).toBeGreaterThan(0); }); test("点击删除按钮移除竞品", async ({ authenticatedPage }) => { await authenticatedPage.waitForTimeout(2000); const emptyState = authenticatedPage.getByText("暂无竞品"); const hasEmpty = await emptyState.isVisible({ timeout: 5000 }).catch(() => false); if (hasEmpty) { test.skip(); return; } // 查找竞品卡片 const competitorCards = authenticatedPage.locator("div.grid.gap-3 > div.flex.items-center.justify-between"); const cardCount = await competitorCards.count(); if (cardCount === 0) { test.skip(); return; } // 记录删除前的竞品数量 const countBefore = cardCount; // 点击第一个竞品的删除按钮 const firstDeleteBtn = competitorCards.first().locator("button").filter({ has: authenticatedPage.locator("svg.lucide-trash-2") }); const hasDeleteBtn = await firstDeleteBtn.isVisible({ timeout: 3000 }).catch(() => false); if (!hasDeleteBtn) { test.skip(); return; } await firstDeleteBtn.click(); // 等待竞品列表刷新 await authenticatedPage.waitForTimeout(2000); // 验证竞品数量减少或列表刷新 const newCardCount = await competitorCards.count(); // 删除后数量应减少(或列表为空时显示空状态) expect(newCardCount).toBeLessThanOrEqual(countBefore); }); });