项目从单体结构重构为 pnpm monorepo (shared/backend/frontend), 新增 YouTube、Instagram、Twitter/X、哔哩哔哩、微博 5 个平台适配器, 包含完整的单元测试和 E2E 测试覆盖。 - 完成 T-031~T-044: 5 个适配器实现、注册、配置和测试 - 重构前后端分离: Hono 后端 + Next.js 前端 - 151 个单元测试 + 21 个 Mock E2E + 25 个真实 E2E - 适配器基于真实 TikHub API 响应结构实现 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
97 lines
3.1 KiB
TypeScript
97 lines
3.1 KiB
TypeScript
import { test, expect, type Page } from "@playwright/test";
|
|
import { mockContentList, mockDetailItem } from "./fixtures";
|
|
|
|
async function mockAPIs(page: Page) {
|
|
await page.route(/\/api\/tikhub\//, async (route) => {
|
|
const url = route.request().url();
|
|
if (url.includes("/detail")) {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ data: mockDetailItem }),
|
|
});
|
|
} else {
|
|
const match = url.match(/\/api\/tikhub\/(\w+)/);
|
|
const platform = match?.[1];
|
|
const items = mockContentList.filter((i) => i.platform === platform);
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ data: items }),
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
test.describe("详情页流程", () => {
|
|
test("详情加载", async ({ page }) => {
|
|
await mockAPIs(page);
|
|
await page.goto("/detail/douyin/dy-001");
|
|
|
|
// Verify title
|
|
await expect(page.getByText("创意摄影技巧")).toBeVisible();
|
|
// Verify author
|
|
await expect(page.getByText("摄影师小明")).toBeVisible();
|
|
// Verify stats panel
|
|
await expect(page.getByText("播放")).toBeVisible();
|
|
await expect(page.getByText("点赞")).toBeVisible();
|
|
});
|
|
|
|
test("标签显示", async ({ page }) => {
|
|
await mockAPIs(page);
|
|
await page.goto("/detail/douyin/dy-001");
|
|
|
|
// mockDetailItem has tags: ['摄影', '创意', '技巧']
|
|
await expect(page.getByText("#摄影")).toBeVisible();
|
|
await expect(page.getByText("#创意")).toBeVisible();
|
|
await expect(page.getByText("#技巧")).toBeVisible();
|
|
});
|
|
|
|
test("查看原文按钮", async ({ page }) => {
|
|
await mockAPIs(page);
|
|
await page.goto("/detail/douyin/dy-001");
|
|
|
|
await expect(page.getByTestId("view-original")).toBeVisible();
|
|
await expect(page.getByTestId("view-original")).toContainText("查看原文");
|
|
});
|
|
|
|
test("返回导航", async ({ page }) => {
|
|
await mockAPIs(page);
|
|
// Navigate to home first to build browser history
|
|
await page.goto("/");
|
|
await expect(page.getByTestId("content-grid")).toBeVisible();
|
|
|
|
// Then navigate to detail
|
|
await page.goto("/detail/douyin/dy-001");
|
|
await expect(page.getByText("创意摄影技巧")).toBeVisible();
|
|
|
|
// Click back button
|
|
await page.getByTestId("detail-back").click();
|
|
await expect(page).toHaveURL("/");
|
|
});
|
|
|
|
test("详情加载失败", async ({ page }) => {
|
|
await page.route(/\/api\/tikhub\//, async (route) => {
|
|
const url = route.request().url();
|
|
if (url.includes("/detail")) {
|
|
await route.fulfill({
|
|
status: 500,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ error: "服务器错误" }),
|
|
});
|
|
} else {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ data: [] }),
|
|
});
|
|
}
|
|
});
|
|
|
|
await page.goto("/detail/douyin/dy-001");
|
|
await expect(page.getByText("加载失败")).toBeVisible({ timeout: 15_000 });
|
|
await expect(page.getByText("重试")).toBeVisible();
|
|
await expect(page.getByText("返回首页")).toBeVisible();
|
|
});
|
|
});
|