428 lines
14 KiB
TypeScript
428 lines
14 KiB
TypeScript
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;
|
||
}
|
||
}
|
||
});
|
||
});
|