forked from zhongyidian/yx-react-template
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
192 lines
6.3 KiB
TypeScript
192 lines
6.3 KiB
TypeScript
import { expect, type Locator, type Page, test } from "@playwright/test";
|
|
|
|
async function dragWithPointer(
|
|
page: Page,
|
|
source: Locator,
|
|
target: Locator,
|
|
): Promise<void> {
|
|
const sourceBox = await source.boundingBox();
|
|
const targetBox = await target.boundingBox();
|
|
|
|
if (!sourceBox || !targetBox) {
|
|
throw new Error("Drag source or target is not visible");
|
|
}
|
|
|
|
const sourcePosition = {
|
|
x: sourceBox.x + sourceBox.width / 2,
|
|
y: sourceBox.y + sourceBox.height / 2,
|
|
};
|
|
const targetPosition = {
|
|
x: targetBox.x + targetBox.width / 2,
|
|
y: targetBox.y + targetBox.height / 2,
|
|
};
|
|
|
|
await page.mouse.move(sourcePosition.x, sourcePosition.y);
|
|
await page.mouse.down();
|
|
await page.mouse.move(sourcePosition.x + 10, sourcePosition.y, { steps: 5 });
|
|
await page.waitForTimeout(150);
|
|
await page.mouse.move(targetPosition.x, targetPosition.y, { steps: 20 });
|
|
await page.waitForTimeout(150);
|
|
await page.mouse.up();
|
|
}
|
|
|
|
function getResource(page: Page, title: string): Locator {
|
|
return page.getByRole("button", { name: new RegExp(title) });
|
|
}
|
|
|
|
function getCategory(page: Page, title: string): Locator {
|
|
return page.getByRole("heading", { name: title, exact: true }).locator("..");
|
|
}
|
|
|
|
async function expectDialogContentCentered(page: Page): Promise<void> {
|
|
const dialogContent = page
|
|
.getByRole("dialog")
|
|
.locator(":scope > div")
|
|
.first();
|
|
const box = await dialogContent.boundingBox();
|
|
const viewport = page.viewportSize();
|
|
|
|
if (!box || !viewport) {
|
|
throw new Error("Dialog content or viewport is not visible");
|
|
}
|
|
|
|
const contentCenter = box.x + box.width / 2;
|
|
expect(Math.abs(contentCenter - viewport.width / 2)).toBeLessThan(2);
|
|
}
|
|
|
|
test.describe("CourseDemo connected flow", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto("/#/course-demo");
|
|
await page.waitForLoadState("networkidle");
|
|
});
|
|
|
|
test("renders header and progress", async ({ page }) => {
|
|
await expect(page.locator("text=学生资助政策 · 互动学习")).toBeVisible();
|
|
await expect(page.locator("text=建立你的课程资源地图")).toBeVisible();
|
|
await expect(page.locator('[data-guide="course-progress"]')).toBeVisible();
|
|
await expect(page.locator("text=本节进度")).toBeVisible();
|
|
});
|
|
|
|
test("starts guide dialog and navigates steps", async ({ page }) => {
|
|
await page.click("text=进入课程导入");
|
|
await page.waitForTimeout(500);
|
|
|
|
// First step should appear
|
|
await expect(page.locator("text=欢迎进入学生资助政策微课")).toBeVisible();
|
|
await expect(page.locator("text=1 / 4")).toBeVisible();
|
|
await expectDialogContentCentered(page);
|
|
|
|
// Click next
|
|
await page.click("button:has-text('了解任务')");
|
|
await page.waitForTimeout(300);
|
|
await expect(page.locator("text=选择你的学习方式")).toBeVisible();
|
|
await expect(page.locator("text=2 / 4")).toBeVisible();
|
|
|
|
// Choose direct path
|
|
await page.click("button:has-text('直接开始分类')");
|
|
await page.waitForTimeout(300);
|
|
await expect(page.locator("text=准备开始")).toBeVisible();
|
|
await expect(page.locator("text=4 / 4")).toBeVisible();
|
|
|
|
// Finish
|
|
await page.click("button:has-text('开始挑战')");
|
|
await page.waitForTimeout(300);
|
|
|
|
// Dialog should close
|
|
await expect(
|
|
page.locator("text=欢迎进入学生资助政策微课"),
|
|
).not.toBeVisible();
|
|
});
|
|
|
|
test("starts spotlight guide and highlights elements", async ({ page }) => {
|
|
await page.click("text=查看操作引导");
|
|
await page.waitForTimeout(500);
|
|
|
|
// First spotlight step
|
|
await expect(page.locator("text=先阅读资源卡片")).toBeVisible();
|
|
await page.waitForTimeout(300);
|
|
|
|
// Next step
|
|
await page.click("button:has-text('下一步')");
|
|
await page.waitForTimeout(500);
|
|
await expect(page.locator("text=放入对应学习阶段")).toBeVisible();
|
|
|
|
// Next step
|
|
await page.click("button:has-text('下一步')");
|
|
await page.waitForTimeout(500);
|
|
await expect(page.locator("text=自动记录学习进度")).toBeVisible();
|
|
|
|
// Finish
|
|
await page.click("button:has-text('开始分类')");
|
|
await page.waitForTimeout(300);
|
|
await expect(page.locator("text=先阅读资源卡片")).not.toBeVisible();
|
|
});
|
|
|
|
test("drag and drop resource classification", async ({ page }) => {
|
|
const policyResource = getResource(page, "资助政策微课");
|
|
const policyZone = getCategory(page, "政策学习");
|
|
|
|
await expect(policyResource).toBeVisible();
|
|
await expect(policyZone).toBeVisible();
|
|
await dragWithPointer(page, policyResource, policyZone);
|
|
|
|
await expect(page.getByText("分类正确:资助政策微课")).toBeVisible();
|
|
await expect(page.getByText("已完成 1/6")).toBeVisible();
|
|
await expect(policyResource).not.toBeVisible();
|
|
});
|
|
|
|
test("wrong category shows error toast", async ({ page }) => {
|
|
const policyResource = getResource(page, "资助政策微课");
|
|
const wrongZone = getCategory(page, "互动实践");
|
|
|
|
await expect(policyResource).toBeVisible();
|
|
await expect(wrongZone).toBeVisible();
|
|
await dragWithPointer(page, policyResource, wrongZone);
|
|
|
|
await expect(
|
|
page.getByText("“资助政策微课”不属于这个分类,再想一想。"),
|
|
).toBeVisible();
|
|
await expect(page.getByText("已完成 0/6")).toBeVisible();
|
|
await expect(policyResource).toBeVisible();
|
|
});
|
|
|
|
test("completion overlay appears after all resources placed", async ({
|
|
page,
|
|
}) => {
|
|
const placements = [
|
|
["资助政策微课", "政策学习"],
|
|
["申请流程图", "政策学习"],
|
|
["诚信申请案例", "互动实践"],
|
|
["材料自查清单", "互动实践"],
|
|
["常见问题答疑", "学习支持"],
|
|
["校园咨询渠道", "学习支持"],
|
|
] as const;
|
|
|
|
for (const [resourceTitle, categoryTitle] of placements) {
|
|
await dragWithPointer(
|
|
page,
|
|
getResource(page, resourceTitle),
|
|
getCategory(page, categoryTitle),
|
|
);
|
|
}
|
|
|
|
await expect(page.getByText("资源地图已建立")).toBeVisible();
|
|
await expect(page.getByText("已完成 6/6")).toBeVisible();
|
|
await expect(page.getByText("100%")).toBeVisible();
|
|
});
|
|
|
|
test("persists progress with session fallback", async ({ page }) => {
|
|
await dragWithPointer(
|
|
page,
|
|
getResource(page, "资助政策微课"),
|
|
getCategory(page, "政策学习"),
|
|
);
|
|
await expect(page.getByText("已完成 1/6")).toBeVisible();
|
|
|
|
await page.reload();
|
|
await expect(page.getByText("进度已恢复")).toBeVisible();
|
|
await expect(page.getByText("已完成 1/6")).toBeVisible();
|
|
await expect(getResource(page, "资助政策微课")).not.toBeVisible();
|
|
});
|
|
});
|