geo/frontend/e2e/tests/error-states.spec.ts

340 lines
12 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, mockApi, mockApiError, clearApiMocks } from "../fixtures";
test.describe("错误状态 - API 500 错误测试", () => {
test.afterEach(async ({ authenticatedPage }) => {
await clearApiMocks(authenticatedPage);
});
test("健康评分页面API 500错误显示错误状态和重试按钮", async ({ authenticatedPage }) => {
// 拦截健康评分API返回500错误
await mockApiError(authenticatedPage, /\/api\/v1\/brands\//, 500);
await mockApiError(authenticatedPage, /\/api\/v1\/scoring\//, 500);
await authenticatedPage.goto("/dashboard/health-score");
await authenticatedPage.waitForLoadState("networkidle");
// 验证错误状态显示
const errorTitle = authenticatedPage.getByText("数据加载失败");
const hasError = await errorTitle.isVisible({ timeout: 15000 }).catch(() => false);
if (!hasError) {
// 可能显示其他错误提示
const errorIcon = authenticatedPage.locator("svg.lucide-alert-circle");
const hasErrorIcon = await errorIcon.isVisible({ timeout: 5000 }).catch(() => false);
if (!hasErrorIcon) {
test.skip();
return;
}
}
// 验证错误消息可见
await expect(errorTitle).toBeVisible();
// 验证重试按钮存在
const retryBtn = authenticatedPage.getByRole("button", { name: /重试/ });
const hasRetry = await retryBtn.isVisible({ timeout: 5000 }).catch(() => false);
expect(hasRetry).toBeTruthy();
});
test("竞品分析页面API 500错误显示错误状态", async ({ authenticatedPage }) => {
await mockApiError(authenticatedPage, /\/api\/v1\/brands\//, 500);
await mockApiError(authenticatedPage, /\/api\/v1\/competitors\//, 500);
await authenticatedPage.goto("/dashboard/competitors");
await authenticatedPage.waitForLoadState("networkidle");
// 验证错误状态
const errorTitle = authenticatedPage.getByText("数据加载失败");
const hasError = await errorTitle.isVisible({ timeout: 15000 }).catch(() => false);
if (!hasError) {
test.skip();
return;
}
await expect(errorTitle).toBeVisible();
// 验证重试按钮
const retryBtn = authenticatedPage.getByRole("button", { name: /重试/ });
const hasRetry = await retryBtn.isVisible({ timeout: 5000 }).catch(() => false);
expect(hasRetry).toBeTruthy();
});
test("引用记录页面API 500错误显示错误提示", async ({ authenticatedPage }) => {
await mockApiError(authenticatedPage, /\/api\/v1\/citations\//, 500);
await authenticatedPage.goto("/dashboard/citations");
await authenticatedPage.waitForLoadState("networkidle");
// 引用页面可能在统计区域或列表区域显示错误
const errorTitle = authenticatedPage.getByText("数据加载失败");
const errorMessage = authenticatedPage.getByText(/Internal Server Error|加载失败/);
const hasError = await errorTitle.isVisible({ timeout: 15000 }).catch(() => false);
const hasErrorMessage = await errorMessage.isVisible({ timeout: 5000 }).catch(() => false);
if (!hasError && !hasErrorMessage) {
test.skip();
return;
}
expect(hasError || hasErrorMessage).toBeTruthy();
});
});
test.describe("错误状态 - 网络离线模拟测试", () => {
test.afterEach(async ({ authenticatedPage }) => {
await clearApiMocks(authenticatedPage);
});
test("所有API请求失败时显示错误状态", async ({ authenticatedPage }) => {
// 拦截所有API请求模拟网络离线
await authenticatedPage.route(/\/api\/v1\//, async (route) => {
await route.abort("failed");
});
await authenticatedPage.goto("/dashboard/health-score");
await authenticatedPage.waitForLoadState("networkidle");
// 验证错误状态或加载失败提示
const errorTitle = authenticatedPage.getByText("数据加载失败");
const loadingSpinner = authenticatedPage.locator(".animate-pulse, .animate-spin");
const hasError = await errorTitle.isVisible({ timeout: 15000 }).catch(() => false);
const isLoading = await loadingSpinner.isVisible({ timeout: 5000 }).catch(() => false);
// 网络错误时应该显示错误状态或仍在加载
expect(hasError || isLoading).toBeTruthy();
});
test("Dashboard页面网络错误时显示错误状态", async ({ authenticatedPage }) => {
await authenticatedPage.route(/\/api\/v1\//, async (route) => {
await route.abort("failed");
});
await authenticatedPage.goto("/dashboard");
await authenticatedPage.waitForLoadState("networkidle");
const errorTitle = authenticatedPage.getByText("数据加载失败");
const hasError = await errorTitle.isVisible({ timeout: 15000 }).catch(() => false);
if (!hasError) {
// Dashboard可能有骨架屏持续加载
const loadingState = authenticatedPage.locator(".animate-pulse");
const isLoading = await loadingState.isVisible({ timeout: 5000 }).catch(() => false);
if (!isLoading) {
test.skip();
return;
}
}
expect(hasError).toBeTruthy();
});
});
test.describe("错误状态 - 空数据测试", () => {
test.afterEach(async ({ authenticatedPage }) => {
await clearApiMocks(authenticatedPage);
});
test("引用记录API返回空数组时显示空状态", async ({ authenticatedPage }) => {
// 模拟引用记录API返回空数据
await mockApi(authenticatedPage, /\/api\/v1\/citations\/\?/, { items: [] });
await mockApi(authenticatedPage, /\/api\/v1\/citations\/stats/, {
citation_rate: null,
avg_position: null,
total_citations: 0,
platform_distribution: [],
trend: [],
});
await mockApi(authenticatedPage, /\/api\/v1\/queries\//, { items: [] });
await authenticatedPage.goto("/dashboard/citations");
await authenticatedPage.waitForLoadState("networkidle");
// 验证空状态显示
const emptyState = authenticatedPage.getByText("暂无引用记录");
const hasEmpty = await emptyState.isVisible({ timeout: 15000 }).catch(() => false);
if (!hasEmpty) {
test.skip();
return;
}
await expect(emptyState).toBeVisible();
// 验证引导文案
const guidanceText = authenticatedPage.getByText("添加查询词并执行查询后将在此显示结果");
const hasGuidance = await guidanceText.isVisible({ timeout: 5000 }).catch(() => false);
expect(hasGuidance).toBeTruthy();
});
test("竞品分析API返回空数组时显示空状态", async ({ authenticatedPage }) => {
// 模拟竞品API返回空数据
await mockApi(authenticatedPage, /\/api\/v1\/brands\//, {
items: [{ id: "test-brand-id", name: "测试品牌" }],
});
await mockApi(authenticatedPage, /\/api\/v1\/brands\/[^/]+\/competitors\//, {
items: [],
total: 0,
});
await mockApi(authenticatedPage, /\/api\/v1\/brands\/[^/]+\/competitors\/recommendations\//, []);
await mockApi(authenticatedPage, /\/api\/v1\/competitor\//, []);
await authenticatedPage.goto("/dashboard/competitors");
await authenticatedPage.waitForLoadState("networkidle");
// 验证空状态
const emptyState = authenticatedPage.getByText("暂无竞品");
const hasEmpty = await emptyState.isVisible({ timeout: 15000 }).catch(() => false);
if (!hasEmpty) {
test.skip();
return;
}
await expect(emptyState).toBeVisible();
// 验证引导文案
const guidanceText = authenticatedPage.getByText(/点击上方.*添加竞品.*按钮开始/);
const hasGuidance = await guidanceText.isVisible({ timeout: 5000 }).catch(() => false);
expect(hasGuidance).toBeTruthy();
});
test("知识库API返回空数组时显示空状态", async ({ authenticatedPage }) => {
// 模拟知识库API返回空数据
await mockApi(authenticatedPage, /\/api\/v1\/knowledge\/bases\//, []);
await authenticatedPage.goto("/dashboard/knowledge");
await authenticatedPage.waitForLoadState("networkidle");
// 验证空状态
const emptyState = authenticatedPage.getByText("还没有知识库");
const hasEmpty = await emptyState.isVisible({ timeout: 15000 }).catch(() => false);
if (!hasEmpty) {
test.skip();
return;
}
await expect(emptyState).toBeVisible();
// 验证引导文案
const guidanceText = authenticatedPage.getByText("创建您的第一个知识库");
const hasGuidance = await guidanceText.isVisible({ timeout: 5000 }).catch(() => false);
expect(hasGuidance).toBeTruthy();
});
test("健康评分API返回空数据时显示空状态", async ({ authenticatedPage }) => {
// 模拟品牌API返回空数据
await mockApi(authenticatedPage, /\/api\/v1\/brands\//, { items: [] });
await authenticatedPage.goto("/dashboard/health-score");
await authenticatedPage.waitForLoadState("networkidle");
// 验证空状态或加载状态
const emptyState = authenticatedPage.getByText("暂无健康评分数据");
const loadingState = authenticatedPage.locator(".animate-pulse");
const hasEmpty = await emptyState.isVisible({ timeout: 15000 }).catch(() => false);
const isLoading = await loadingState.isVisible({ timeout: 5000 }).catch(() => false);
if (!hasEmpty && !isLoading) {
test.skip();
return;
}
expect(hasEmpty || isLoading).toBeTruthy();
});
});
test.describe("错误状态 - 认证过期(401)测试", () => {
test.afterEach(async ({ authenticatedPage }) => {
await clearApiMocks(authenticatedPage);
});
test("API返回401时重定向到登录页面", async ({ authenticatedPage }) => {
// 拦截所有API请求返回401
await mockApiError(authenticatedPage, /\/api\/v1\//, 401);
await authenticatedPage.goto("/dashboard/health-score");
// 等待可能的重定向
await authenticatedPage.waitForTimeout(5000);
// 验证是否重定向到登录页面
const currentUrl = authenticatedPage.url();
const isLoginPage = currentUrl.includes("/login") || currentUrl.includes("/auth");
const hasLoginError = await authenticatedPage.getByText(/登录|认证|权限/).isVisible({ timeout: 5000 }).catch(() => false);
// 401应该触发重定向或显示认证错误
expect(isLoginPage || hasLoginError).toBeTruthy();
});
test("Dashboard页面401错误重定向到登录", async ({ authenticatedPage }) => {
await mockApiError(authenticatedPage, /\/api\/v1\/dashboard/, 401);
await authenticatedPage.goto("/dashboard");
await authenticatedPage.waitForTimeout(5000);
const currentUrl = authenticatedPage.url();
const isLoginPage = currentUrl.includes("/login") || currentUrl.includes("/auth");
// 验证重定向或错误提示
if (!isLoginPage) {
const authError = authenticatedPage.getByText(/登录|认证|权限|过期/);
const hasAuthError = await authError.isVisible({ timeout: 5000 }).catch(() => false);
if (!hasAuthError) {
test.skip();
return;
}
}
expect(isLoginPage).toBeTruthy();
});
});
test.describe("错误状态 - 重试按钮交互测试", () => {
test.afterEach(async ({ authenticatedPage }) => {
await clearApiMocks(authenticatedPage);
});
test("点击重试按钮后重新请求数据", async ({ authenticatedPage }) => {
// 先模拟错误
await mockApiError(authenticatedPage, /\/api\/v1\/brands\//, 500);
await mockApiError(authenticatedPage, /\/api\/v1\/scoring\//, 500);
await authenticatedPage.goto("/dashboard/health-score");
await authenticatedPage.waitForLoadState("networkidle");
// 等待错误状态
const errorTitle = authenticatedPage.getByText("数据加载失败");
const hasError = await errorTitle.isVisible({ timeout: 15000 }).catch(() => false);
if (!hasError) {
test.skip();
return;
}
// 清除错误mock恢复正常响应
await clearApiMocks(authenticatedPage);
// 模拟正常响应
await mockApi(authenticatedPage, /\/api\/v1\/brands\//, {
items: [{ id: "test-brand-id", name: "测试品牌" }],
});
// 点击重试按钮
const retryBtn = authenticatedPage.getByRole("button", { name: /重试/ });
const hasRetry = await retryBtn.isVisible({ timeout: 5000 }).catch(() => false);
if (!hasRetry) {
test.skip();
return;
}
await retryBtn.click();
// 验证错误状态消失(页面重新加载)
await authenticatedPage.waitForTimeout(3000);
});
});