geo/frontend/e2e/tests/onboarding.spec.ts

428 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { test, expect, describe, Page } from "@playwright/test";
import { LoginPage } from "../pages/login.page";
const TEST_USER = {
email: "admin@example.com",
password: "admin@123",
};
// 引导流程页面对象
class OnboardingPage {
page: Page;
// Step 1: 品牌名称输入
brandNameInput: ReturnType<Page["locator"]>;
startAnalysisButton: ReturnType<Page["getByRole"]>;
skipToDashboardButton: ReturnType<Page["getByRole"]>;
// Step 2: 竞品确认
competitorCheckboxes: ReturnType<Page["locator"]>;
addCompetitorInput: ReturnType<Page["locator"]>;
addCompetitorButton: ReturnType<Page["locator"]>;
nextButton: ReturnType<Page["getByRole"]>;
backButton: ReturnType<Page["getByRole"]>;
skipStepButton: ReturnType<Page["getByRole"]>;
// Step 3: 平台选择
platformCheckboxes: ReturnType<Page["locator"]>;
selectAllButton: ReturnType<Page["getByRole"]>;
queryFrequencyOptions: ReturnType<Page["locator"]>;
// Step 4: 健康报告
healthScore: ReturnType<Page["locator"]>;
healthLevelBadge: ReturnType<Page["locator"]>;
viewActionsButton: ReturnType<Page["getByRole"]>;
// Step 5: 行动建议
completeButton: ReturnType<Page["getByRole"]>;
// 进度指示器
progressSteps: ReturnType<Page["locator"]>;
constructor(page: Page) {
this.page = page;
// Step 1
this.brandNameInput = this.page.locator("#brandName");
this.startAnalysisButton = this.page.getByRole("button", {
name: /开始分析/,
});
this.skipToDashboardButton = this.page.getByRole("button", {
name: /跳过.*Dashboard/,
});
// Step 2
this.competitorCheckboxes = this.page
.locator(".border.rounded-lg")
.filter({ has: this.page.locator(".flex.h-5.w-5") });
this.addCompetitorInput = this.page.locator(
'input[placeholder="输入竞品名称"]',
);
this.addCompetitorButton = this.page
.locator('button:has-text("添加")')
.first();
this.nextButton = this.page.getByRole("button", { name: /继续/ });
this.backButton = this.page.getByRole("button", { name: /上一步/ });
this.skipStepButton = this.page.getByRole("button", { name: /跳过此步骤/ });
// Step 3
this.platformCheckboxes = this.page.locator(".grid.gap-3 button");
this.selectAllButton = this.page.getByRole("button", { name: "全选" });
this.queryFrequencyOptions = this.page.locator(".grid.gap-3 button");
// Step 4
this.healthScore = this.page.locator("text-7xl.font-bold");
this.healthLevelBadge = this.page.locator("text-base.px-4.py-1");
this.viewActionsButton = this.page.getByRole("button", {
name: /查看行动建议/,
});
// Step 5
this.completeButton = this.page.getByRole("button", { name: /完成设置/ });
// 进度指示器 - 使用更精确的选择器
this.progressSteps = this.page.locator(
".flex.items-center.justify-between .flex.flex-1.items-center",
);
}
async goto() {
await this.page.goto("/onboarding");
}
async loginAndGoToOnboarding() {
const loginPage = new LoginPage(this.page);
await loginPage.goto();
await loginPage.login(TEST_USER.email, TEST_USER.password);
// 等待登录完成并跳转到Dashboard
// 使用更长的超时时间和更多的等待条件
try {
await this.page.waitForURL(/\/dashboard/, { timeout: 20000 });
} catch (e) {
// 如果超时检查当前URL
console.log("Current URL after login timeout:", this.page.url());
throw e;
}
await this.page.goto("/onboarding");
}
async fillBrandName(name: string) {
await this.brandNameInput.fill(name);
}
async clickStartAnalysis() {
await this.startAnalysisButton.click();
}
async selectCompetitor(name: string) {
const competitorCard = this.page
.locator(".border.rounded-lg")
.filter({ hasText: name });
await competitorCard.click();
}
async selectPlatform(platformName: string) {
const platformCard = this.page
.locator(".grid.gap-3 button")
.filter({ hasText: platformName });
await platformCard.click();
}
async selectQueryFrequency(frequency: string) {
await this.queryFrequencyOptions.filter({ hasText: frequency }).click();
}
}
describe("新用户引导向导 - 完整流程测试", () => {
test.beforeEach(async ({ page }) => {
// 先登录
const onboardingPage = new OnboardingPage(page);
await onboardingPage.loginAndGoToOnboarding();
});
test("Step 1: 应该显示品牌名称输入页面", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto();
// 验证标题
await expect(
page.getByRole("heading", { name: /输入您的品牌名称/ }),
).toBeVisible();
// 验证输入框存在
await expect(onboardingPage.brandNameInput).toBeVisible();
// 验证按钮存在
await expect(onboardingPage.startAnalysisButton).toBeVisible();
await expect(onboardingPage.skipToDashboardButton).toBeVisible();
// 验证进度指示器
await expect(onboardingPage.progressSteps).toHaveCount(5);
});
test("Step 1: 品牌名称验证 - 太短应显示错误", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto();
// 输入太短的名称
await onboardingPage.fillBrandName("a");
await onboardingPage.startAnalysisButton.click();
// 应该显示错误提示
await expect(page.getByText(/至少需要2个字符/)).toBeVisible();
});
test("Step 1: 正确输入品牌名称应进入下一步", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto();
// 输入有效的品牌名称
await onboardingPage.fillBrandName("华为");
await onboardingPage.startAnalysisButton.click();
// 应该进入Step 2
await expect(page.getByRole("heading", { name: /确认竞品/ })).toBeVisible({
timeout: 5000,
});
});
test("Step 2: 可以选择竞品并继续", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto();
// Step 1
await onboardingPage.fillBrandName("华为");
await onboardingPage.startAnalysisButton.click();
// Step 2 - 等待加载
await expect(page.getByRole("heading", { name: /确认竞品/ })).toBeVisible({
timeout: 5000,
});
// 等待推荐竞品加载
await page.waitForTimeout(2000);
// 选择一个竞品(如果存在的话)
const firstCompetitor = page
.locator(".border.rounded-lg")
.filter({ has: page.locator(".flex.h-5.w-5") })
.first();
if (await firstCompetitor.isVisible()) {
await firstCompetitor.click();
}
// 点击继续
await onboardingPage.nextButton.click();
// 应该进入Step 3
await expect(
page.getByRole("heading", { name: /选择监控平台/ }),
).toBeVisible({ timeout: 5000 });
});
test("Step 2: 跳过应进入下一步", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto();
// Step 1
await onboardingPage.fillBrandName("华为");
await onboardingPage.startAnalysisButton.click();
// Step 2 - 点击跳过
await expect(page.getByRole("heading", { name: /确认竞品/ })).toBeVisible({
timeout: 5000,
});
await onboardingPage.skipStepButton.click();
// 应该进入Step 3
await expect(
page.getByRole("heading", { name: /选择监控平台/ }),
).toBeVisible({ timeout: 5000 });
});
test("Step 3: 平台默认全选", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto();
// Step 1 -> Step 2 -> Step 3
await onboardingPage.fillBrandName("华为");
await onboardingPage.startAnalysisButton.click();
await expect(page.getByRole("heading", { name: /确认竞品/ })).toBeVisible({
timeout: 5000,
});
await onboardingPage.skipStepButton.click();
await expect(
page.getByRole("heading", { name: /选择监控平台/ }),
).toBeVisible({ timeout: 5000 });
// 验证平台数量提示
await expect(page.getByText(/\d+\/\d+ 个平台/)).toBeVisible();
});
test("Step 3: 可以选择不同频率", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto();
// 到达Step 3
await onboardingPage.fillBrandName("华为");
await onboardingPage.startAnalysisButton.click();
await expect(page.getByRole("heading", { name: /确认竞品/ })).toBeVisible({
timeout: 5000,
});
await onboardingPage.skipStepButton.click();
await expect(
page.getByRole("heading", { name: /选择监控平台/ }),
).toBeVisible({ timeout: 5000 });
// 选择每日频率
await onboardingPage.selectQueryFrequency("每日");
// 验证选中状态
await expect(page.locator(".border-primary.bg-primary\\/5")).toBeVisible();
});
test("Step 3: 返回上一步", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto();
// 到达Step 3
await onboardingPage.fillBrandName("华为");
await onboardingPage.startAnalysisButton.click();
await expect(page.getByRole("heading", { name: /确认竞品/ })).toBeVisible({
timeout: 5000,
});
await onboardingPage.skipStepButton.click();
await expect(
page.getByRole("heading", { name: /选择监控平台/ }),
).toBeVisible({ timeout: 5000 });
// 返回
await onboardingPage.backButton.click();
// 应该回到Step 2
await expect(page.getByRole("heading", { name: /确认竞品/ })).toBeVisible({
timeout: 5000,
});
});
test("Step 4: 跳过直接进入Dashboard", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto();
// Step 1 -> 跳过
await expect(
page.getByRole("heading", { name: /输入您的品牌名称/ }),
).toBeVisible();
await onboardingPage.skipToDashboardButton.click();
// 应该跳转到Dashboard
await expect(page).toHaveURL(/\/dashboard/, { timeout: 10000 });
});
});
describe("新用户引导向导 - 响应式设计测试", () => {
test("移动端视图应该正常显示", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
const onboardingPage = new OnboardingPage(page);
await onboardingPage.loginAndGoToOnboarding();
// 验证主要内容可见
await expect(
page.getByRole("heading", { name: /输入您的品牌名称/ }),
).toBeVisible();
// 验证按钮可点击
await expect(onboardingPage.startAnalysisButton).toBeVisible();
});
test("平板视图应该正常显示", async ({ page }) => {
await page.setViewportSize({ width: 768, height: 1024 });
const onboardingPage = new OnboardingPage(page);
await onboardingPage.loginAndGoToOnboarding();
// 验证主要内容可见
await expect(
page.getByRole("heading", { name: /输入您的品牌名称/ }),
).toBeVisible();
});
});
describe("新用户引导向导 - 进度指示器测试", () => {
test.beforeEach(async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.loginAndGoToOnboarding();
});
test("初始应显示Step 1为当前步骤", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto();
// Step 1 应该显示为当前步骤
const firstStepIndicator = page
.locator(".flex.flex-col.items-center")
.first();
await expect(
firstStepIndicator.locator(".border-primary.bg-primary\\/10"),
).toBeVisible();
});
test("完成Step 1后Step 2应为当前步骤", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.goto();
await onboardingPage.fillBrandName("华为");
await onboardingPage.startAnalysisButton.click();
// 等待Step 2加载
await expect(page.getByRole("heading", { name: /确认竞品/ })).toBeVisible({
timeout: 5000,
});
// Step 2 应该显示为当前步骤(第二个圆圈)
const secondStepIndicator = page
.locator(".flex.flex-col.items-center")
.nth(1);
await expect(secondStepIndicator.locator(".border-primary")).toBeVisible();
});
});
describe("新用户引导向导 - 健康等级颜色测试", () => {
test("健康等级应使用正确的颜色", async ({ page }) => {
const onboardingPage = new OnboardingPage(page);
await onboardingPage.loginAndGoToOnboarding();
// 到达Step 3
await onboardingPage.fillBrandName("华为");
await onboardingPage.startAnalysisButton.click();
await expect(page.getByRole("heading", { name: /确认竞品/ })).toBeVisible({
timeout: 5000,
});
await onboardingPage.skipStepButton.click();
await expect(
page.getByRole("heading", { name: /选择监控平台/ }),
).toBeVisible({ timeout: 5000 });
// 选择一个平台next按钮需要至少选择一个平台才能点击
await onboardingPage.selectPlatform("文心一言");
// 等待按钮可用
await expect(onboardingPage.nextButton).toBeEnabled({ timeout: 5000 });
await onboardingPage.nextButton.click();
// 等待健康报告加载
await page.waitForTimeout(2000);
// 验证健康等级标签存在(优秀/良好/及格/危险之一)
const healthLabels = ["优秀", "良好", "及格", "危险"];
for (const label of healthLabels) {
const labelElement = page.getByText(label);
if (await labelElement.isVisible()) {
break;
}
}
});
});