337 lines
15 KiB
TypeScript
337 lines
15 KiB
TypeScript
import { test, expect } from "../fixtures";
|
||
import { DashboardPage } from "../pages/dashboard.page";
|
||
|
||
async function hasProjects(page: import("@playwright/test").Page): Promise<boolean> {
|
||
// 先导航到 dashboard 页面
|
||
await page.goto("/dashboard");
|
||
await page.waitForLoadState("networkidle");
|
||
|
||
const emptyMsg = page.getByText("开始优化您的AI可见性");
|
||
const errorTitle = page.getByText("数据加载失败");
|
||
const title = page.getByRole("heading", { name: "品牌健康中心" });
|
||
const hasTitle = await title.isVisible({ timeout: 15000 }).catch(() => false);
|
||
if (!hasTitle) return false;
|
||
const isEmpty = await emptyMsg.isVisible().catch(() => false);
|
||
const isError = await errorTitle.isVisible().catch(() => false);
|
||
return !isEmpty && !isError;
|
||
}
|
||
|
||
test.describe("内容工坊 - 页面加载测试", () => {
|
||
test("内容工坊页面标题正确显示", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
await expect(authenticatedPage.getByRole("heading", { name: "内容工坊" })).toBeVisible({ timeout: 15000 });
|
||
});
|
||
|
||
test("内容工坊页面副标题正确显示", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
await expect(authenticatedPage.getByText("AI驱动的内容生产流水线")).toBeVisible({ timeout: 15000 });
|
||
});
|
||
|
||
test("内容工坊页面显示AI生成新内容按钮", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const generateBtn = authenticatedPage.getByRole("button", { name: /AI生成新内容/ });
|
||
await expect(generateBtn.first()).toBeVisible({ timeout: 15000 });
|
||
});
|
||
});
|
||
|
||
test.describe("内容工坊 - 内容列表测试", () => {
|
||
test("有内容时显示内容卡片列表", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
// 检查是否有空状态
|
||
const emptyState = authenticatedPage.getByText("还没有内容");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 10000 }).catch(() => false);
|
||
if (hasEmpty) { test.skip(); return; }
|
||
|
||
// 检查是否有错误
|
||
const errorBanner = authenticatedPage.getByText(/加载失败/);
|
||
const hasError = await errorBanner.isVisible({ timeout: 3000 }).catch(() => false);
|
||
if (hasError) { test.skip(); return; }
|
||
|
||
// 使用更宽泛的选择器匹配内容卡片
|
||
const contentCards = authenticatedPage.locator("[class*='bg-white'][class*='rounded-xl'][class*='border']");
|
||
const count = await contentCards.count();
|
||
if (count === 0) { test.skip(); return; }
|
||
expect(count).toBeGreaterThanOrEqual(1);
|
||
});
|
||
|
||
test("内容卡片显示状态标签", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有内容");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 10000 }).catch(() => false);
|
||
if (hasEmpty) { test.skip(); return; }
|
||
|
||
const errorBanner = authenticatedPage.getByText(/加载失败/);
|
||
const hasError = await errorBanner.isVisible({ timeout: 3000 }).catch(() => false);
|
||
if (hasError) { test.skip(); return; }
|
||
|
||
const statusBadge = authenticatedPage.getByText(/草稿|待审核|已审核|已发布|已归档/).first();
|
||
const hasBadge = await statusBadge.isVisible({ timeout: 5000 }).catch(() => false);
|
||
if (!hasBadge) { test.skip(); return; }
|
||
await expect(statusBadge).toBeVisible();
|
||
});
|
||
|
||
test("内容卡片显示类型标签", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有内容");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 10000 }).catch(() => false);
|
||
if (hasEmpty) { test.skip(); return; }
|
||
|
||
const errorBanner = authenticatedPage.getByText(/加载失败/);
|
||
const hasError = await errorBanner.isVisible({ timeout: 3000 }).catch(() => false);
|
||
if (hasError) { test.skip(); return; }
|
||
|
||
const typeBadge = authenticatedPage.getByText(/文章|问答|知识库|社媒/).first();
|
||
const hasBadge = await typeBadge.isVisible({ timeout: 5000 }).catch(() => false);
|
||
if (!hasBadge) { test.skip(); return; }
|
||
await expect(typeBadge).toBeVisible();
|
||
});
|
||
|
||
test("空状态时显示引导文案和AI生成按钮", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const emptyState = authenticatedPage.getByText("还没有内容");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 10000 }).catch(() => false);
|
||
if (!hasEmpty) { test.skip(); return; }
|
||
|
||
await expect(emptyState).toBeVisible();
|
||
const generateBtn = authenticatedPage.getByRole("button", { name: /AI生成新内容/ });
|
||
await expect(generateBtn.first()).toBeVisible();
|
||
});
|
||
});
|
||
|
||
test.describe("内容工坊 - 内容生成测试", () => {
|
||
test("点击AI生成新内容打开生成对话框", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const generateBtn = authenticatedPage.getByRole("button", { name: /AI生成新内容/ }).first();
|
||
await generateBtn.click();
|
||
|
||
await expect(authenticatedPage.getByRole("dialog")).toBeVisible({ timeout: 10000 });
|
||
});
|
||
|
||
test("生成对话框包含目标关键词输入框", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const generateBtn = authenticatedPage.getByRole("button", { name: /AI生成新内容/ }).first();
|
||
await generateBtn.click();
|
||
|
||
await expect(authenticatedPage.locator("#keyword")).toBeVisible({ timeout: 10000 });
|
||
});
|
||
|
||
test("生成对话框包含目标平台选择器", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const generateBtn = authenticatedPage.getByRole("button", { name: /AI生成新内容/ }).first();
|
||
await generateBtn.click();
|
||
|
||
await expect(authenticatedPage.locator("#platform")).toBeVisible({ timeout: 10000 });
|
||
});
|
||
|
||
test("未填写必填项时开始AI生成按钮禁用", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const generateBtn = authenticatedPage.getByRole("button", { name: /AI生成新内容/ }).first();
|
||
await generateBtn.click();
|
||
|
||
const submitBtn = authenticatedPage.getByRole("button", { name: /开始AI生成/ });
|
||
await expect(submitBtn).toBeDisabled({ timeout: 10000 });
|
||
});
|
||
|
||
test("填写关键词和平台后开始AI生成按钮启用", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const generateBtn = authenticatedPage.getByRole("button", { name: /AI生成新内容/ }).first();
|
||
await generateBtn.click();
|
||
|
||
const keywordInput = authenticatedPage.locator("#keyword");
|
||
await keywordInput.fill("AI营销");
|
||
|
||
const platformSelect = authenticatedPage.locator("#platform");
|
||
await platformSelect.click();
|
||
await authenticatedPage.getByText("通用").click();
|
||
|
||
const submitBtn = authenticatedPage.getByRole("button", { name: /开始AI生成/ });
|
||
await expect(submitBtn).toBeEnabled({ timeout: 10000 });
|
||
});
|
||
});
|
||
|
||
test.describe("监测优化 - 页面加载测试", () => {
|
||
test("监测优化页面标题正确显示", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/monitoring");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
await expect(authenticatedPage.getByRole("heading", { name: "监测优化" })).toBeVisible({ timeout: 15000 });
|
||
});
|
||
|
||
test("监测优化页面副标题正确显示", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/monitoring");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
await expect(authenticatedPage.getByText("实时监控品牌AI可见性,及时响应告警通知")).toBeVisible({ timeout: 15000 });
|
||
});
|
||
|
||
test("监测优化页面显示告警配置按钮", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/monitoring");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const settingsBtn = authenticatedPage.getByRole("button", { name: /告警配置/ });
|
||
await expect(settingsBtn).toBeVisible({ timeout: 15000 });
|
||
});
|
||
|
||
test("监测优化页面显示监测记录和告警通知Tab", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/monitoring");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
// 使用 waitFor 等待 tab 出现
|
||
const recordsTab = authenticatedPage.getByRole("tab", { name: "监测记录" });
|
||
const alertsTab = authenticatedPage.getByRole("tab", { name: "告警通知" });
|
||
// 至少一个 tab 应该可见
|
||
await expect(recordsTab.first()).toBeVisible({ timeout: 15000 }).catch(async () => {
|
||
await expect(alertsTab.first()).toBeVisible({ timeout: 5000 });
|
||
});
|
||
});
|
||
});
|
||
|
||
test.describe("监测优化 - 监测记录测试", () => {
|
||
test("无监测记录时显示空状态", async ({ authenticatedPage }) => {
|
||
if (!(await hasProjects(authenticatedPage))) { test.skip(); return; }
|
||
|
||
await authenticatedPage.goto("/dashboard/monitoring");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const emptyState = authenticatedPage.getByText("暂无监测记录");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 10000 }).catch(() => false);
|
||
if (!hasEmpty) { test.skip(); return; }
|
||
|
||
await expect(emptyState).toBeVisible();
|
||
});
|
||
|
||
test("有监测记录时显示记录卡片", async ({ authenticatedPage }) => {
|
||
if (!(await hasProjects(authenticatedPage))) { test.skip(); return; }
|
||
|
||
await authenticatedPage.goto("/dashboard/monitoring");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const emptyState = authenticatedPage.getByText("暂无监测记录");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 10000 }).catch(() => false);
|
||
if (hasEmpty) { test.skip(); return; }
|
||
|
||
const checkBtn = authenticatedPage.getByRole("button", { name: /立即检测/ });
|
||
const count = await checkBtn.count();
|
||
expect(count).toBeGreaterThanOrEqual(1);
|
||
});
|
||
|
||
test("监测记录卡片包含立即检测按钮", async ({ authenticatedPage }) => {
|
||
if (!(await hasProjects(authenticatedPage))) { test.skip(); return; }
|
||
|
||
await authenticatedPage.goto("/dashboard/monitoring");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const emptyState = authenticatedPage.getByText("暂无监测记录");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 10000 }).catch(() => false);
|
||
if (hasEmpty) { test.skip(); return; }
|
||
|
||
const checkBtn = authenticatedPage.getByRole("button", { name: /立即检测/ }).first();
|
||
await expect(checkBtn).toBeVisible({ timeout: 10000 });
|
||
});
|
||
|
||
test("监测记录卡片包含暂停/启用按钮", async ({ authenticatedPage }) => {
|
||
if (!(await hasProjects(authenticatedPage))) { test.skip(); return; }
|
||
|
||
await authenticatedPage.goto("/dashboard/monitoring");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const emptyState = authenticatedPage.getByText("暂无监测记录");
|
||
const hasEmpty = await emptyState.isVisible({ timeout: 10000 }).catch(() => false);
|
||
if (hasEmpty) { test.skip(); return; }
|
||
|
||
const toggleBtn = authenticatedPage.getByRole("button", { name: /暂停|启用/ }).first();
|
||
const hasBtn = await toggleBtn.isVisible({ timeout: 5000 }).catch(() => false);
|
||
if (!hasBtn) { test.skip(); return; }
|
||
await expect(toggleBtn).toBeVisible();
|
||
});
|
||
});
|
||
|
||
test.describe("监测优化 - 告警通知测试", () => {
|
||
test("点击告警通知Tab切换到告警列表", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/monitoring");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
// 滚动到页面顶部确保 Tab 可见
|
||
await authenticatedPage.evaluate(() => window.scrollTo(0, 0));
|
||
|
||
const alertsTab = authenticatedPage.getByRole("tab", { name: /告警通知/ });
|
||
const hasTab = await alertsTab.isVisible({ timeout: 15000 }).catch(() => false);
|
||
if (!hasTab) {
|
||
// 尝试通过文本定位
|
||
const alertsText = authenticatedPage.getByText("告警通知").first();
|
||
const hasText = await alertsText.isVisible({ timeout: 5000 }).catch(() => false);
|
||
if (!hasText) { test.skip(); return; }
|
||
await alertsText.click();
|
||
} else {
|
||
await alertsTab.click();
|
||
}
|
||
|
||
// 分别检查两种可能的状态 — 使用 toBeVisible 确保等待
|
||
let found = false;
|
||
try {
|
||
await expect(authenticatedPage.getByText("告警列表")).toBeVisible({ timeout: 10000 });
|
||
found = true;
|
||
} catch {
|
||
try {
|
||
await expect(authenticatedPage.getByText("暂无告警")).toBeVisible({ timeout: 5000 });
|
||
found = true;
|
||
} catch {
|
||
// 两者都不可见
|
||
}
|
||
}
|
||
expect(found).toBeTruthy();
|
||
});
|
||
|
||
test("告警通知Tab显示统计卡片", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/monitoring");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
|
||
const alertsTab = authenticatedPage.getByRole("tab", { name: /告警通知/ });
|
||
const hasTab = await alertsTab.isVisible({ timeout: 10000 }).catch(() => false);
|
||
if (!hasTab) { test.skip(); return; }
|
||
|
||
await alertsTab.click();
|
||
|
||
const statCards = authenticatedPage.getByText(/未读告警|严重告警|今日新增|已处理/);
|
||
const count = await statCards.count();
|
||
if (count === 0) { test.skip(); return; }
|
||
expect(count).toBeGreaterThanOrEqual(1);
|
||
});
|
||
});
|
||
|
||
test.describe("内容→监测完整流程测试", () => {
|
||
test("从内容工坊导航到监测优化页面", async ({ authenticatedPage }) => {
|
||
await authenticatedPage.goto("/dashboard/content");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
await expect(authenticatedPage.getByRole("heading", { name: "内容工坊" })).toBeVisible({ timeout: 15000 });
|
||
|
||
await authenticatedPage.goto("/dashboard/monitoring");
|
||
await authenticatedPage.waitForLoadState("networkidle");
|
||
await expect(authenticatedPage.getByRole("heading", { name: "监测优化" })).toBeVisible({ timeout: 15000 });
|
||
});
|
||
});
|