项目从单体结构重构为 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>
105 lines
3.3 KiB
TypeScript
105 lines
3.3 KiB
TypeScript
import { test, expect, type Page } from "@playwright/test";
|
|
import { mockContentList } from "./fixtures";
|
|
|
|
async function mockTrendingAPI(page: Page) {
|
|
await page.route(/\/api\/tikhub\//, async (route) => {
|
|
const match = route.request().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 mockTrendingAPI(page);
|
|
await page.goto("/");
|
|
await expect(page.getByTestId("content-grid")).toBeVisible();
|
|
|
|
// First favorite button should be "收藏" (not favorited)
|
|
const firstFavBtn = page.getByTestId("favorite-btn").first();
|
|
await expect(firstFavBtn).toHaveAttribute("aria-label", "收藏");
|
|
|
|
// Click to favorite
|
|
await firstFavBtn.click();
|
|
|
|
// Should now be "取消收藏" (favorited)
|
|
await expect(firstFavBtn).toHaveAttribute("aria-label", "取消收藏");
|
|
});
|
|
|
|
test("收藏页面显示收藏内容", async ({ page }) => {
|
|
await mockTrendingAPI(page);
|
|
await page.goto("/");
|
|
await expect(page.getByTestId("content-grid")).toBeVisible();
|
|
|
|
// Favorite the first card
|
|
await page.getByTestId("favorite-btn").first().click();
|
|
|
|
// Navigate to favorites
|
|
await page.goto("/favorites");
|
|
|
|
// Verify count
|
|
await expect(page.getByTestId("favorites-count")).toContainText("1 个内容");
|
|
// Verify the card is shown
|
|
await expect(page.getByTestId("content-card")).toHaveCount(1);
|
|
});
|
|
|
|
test("取消收藏", async ({ page }) => {
|
|
await mockTrendingAPI(page);
|
|
await page.goto("/");
|
|
await expect(page.getByTestId("content-grid")).toBeVisible();
|
|
|
|
// Add favorite
|
|
await page.getByTestId("favorite-btn").first().click();
|
|
await expect(page.getByTestId("favorite-btn").first()).toHaveAttribute(
|
|
"aria-label",
|
|
"取消收藏"
|
|
);
|
|
|
|
// Navigate to favorites
|
|
await page.goto("/favorites");
|
|
await expect(page.getByTestId("content-card")).toHaveCount(1);
|
|
|
|
// Remove favorite
|
|
await page.getByTestId("favorite-btn").first().click();
|
|
|
|
// Card should be gone, empty state shown
|
|
await expect(page.getByTestId("content-card")).toHaveCount(0);
|
|
await expect(page.getByText("还没有收藏")).toBeVisible();
|
|
});
|
|
|
|
test("空收藏状态", async ({ page }) => {
|
|
await page.goto("/favorites");
|
|
|
|
await expect(page.getByText("还没有收藏")).toBeVisible();
|
|
await expect(page.getByText("去发现")).toBeVisible();
|
|
});
|
|
|
|
test("收藏持久化", async ({ page }) => {
|
|
await mockTrendingAPI(page);
|
|
await page.goto("/");
|
|
await expect(page.getByTestId("content-grid")).toBeVisible();
|
|
|
|
// Add favorite
|
|
await page.getByTestId("favorite-btn").first().click();
|
|
await expect(page.getByTestId("favorite-btn").first()).toHaveAttribute(
|
|
"aria-label",
|
|
"取消收藏"
|
|
);
|
|
|
|
// Reload the page — localStorage should persist
|
|
await page.reload();
|
|
await expect(page.getByTestId("content-grid")).toBeVisible();
|
|
|
|
// Verify favorite persisted
|
|
await expect(page.getByTestId("favorite-btn").first()).toHaveAttribute(
|
|
"aria-label",
|
|
"取消收藏"
|
|
);
|
|
});
|
|
});
|