import { expect, test } from "@playwright/test"; import { installAppMocks } from "./helpers/mockApp"; test("login redirects into dashboard with mocked auth", async ({ page }) => { await installAppMocks(page, { authenticated: false, authMeNullResponsesAfterLogin: 1, }); await page.goto("/login"); await expect(page.getByTestId("login-title")).toBeVisible(); await page.getByTestId("login-username-input").fill("TestPlayer"); await page.getByTestId("login-submit-button").click(); await expect(page).toHaveURL(/\/dashboard$/); await expect(page.getByTestId("dashboard-title")).toContainText("TestPlayer"); }); test("training page shows plan generation flow", async ({ page }) => { await installAppMocks(page, { authenticated: true }); await page.goto("/training"); await expect(page.getByTestId("training-title")).toBeVisible(); const generateButton = page.getByRole("button", { name: "生成训练计划" }).last(); await expect(generateButton).toBeVisible(); await generateButton.click(); await expect(page).toHaveURL(/\/training$/); }); test("videos page renders video library items", async ({ page }) => { await installAppMocks(page, { authenticated: true }); await page.goto("/videos"); await expect(page.getByTestId("videos-title")).toBeVisible(); await expect(page.getByTestId("video-card")).toHaveCount(1); }); test("videos page opens lightweight clip editor", async ({ page }) => { await installAppMocks(page, { authenticated: true }); await page.goto("/videos"); await page.getByRole("button", { name: "轻剪辑" }).click(); await expect(page.getByText("PC 轻剪辑工作台")).toBeVisible(); await expect(page.locator("text=建议保留:正手启动").first()).toBeVisible(); await expect(page.getByRole("button", { name: "导出草稿" })).toBeVisible(); }); test("live camera page exposes camera startup controls", async ({ page }) => { await installAppMocks(page, { authenticated: true }); await page.goto("/live-camera"); await expect(page.getByTestId("live-camera-start-button")).toBeVisible(); }); test("live camera starts analysis and produces scores", async ({ page }) => { await installAppMocks(page, { authenticated: true }); await page.goto("/live-camera"); await page.getByRole("button", { name: "下一步" }).click(); await page.getByRole("button", { name: "下一步" }).click(); await page.getByRole("button", { name: "下一步" }).click(); await page.getByRole("button", { name: /启用摄像头/ }).click(); await expect(page.getByTestId("live-camera-analyze-button")).toBeVisible(); await page.getByTestId("live-camera-analyze-button").click(); await expect(page.getByTestId("live-camera-score-overall")).toBeVisible(); }); test("live camera switches into viewer mode when another device already owns analysis", async ({ page }) => { await installAppMocks(page, { authenticated: true, liveViewerMode: true }); await page.goto("/live-camera"); await expect(page.getByText("同步观看模式")).toBeVisible(); await expect(page.getByText(/同步观看|重新同步/).first()).toBeVisible(); await expect(page.getByText("当前设备已锁定为观看模式")).toBeVisible(); await expect(page.getByTestId("live-camera-viewer-sync-card")).toContainText("其他设备实时分析"); await expect(page.getByTestId("live-camera-viewer-sync-card")).toContainText("移动端"); await expect(page.getByTestId("live-camera-viewer-sync-card")).toContainText("均衡模式"); await expect(page.getByTestId("live-camera-viewer-sync-card")).toContainText("猩猩"); await expect(page.getByTestId("live-camera-score-overall")).toBeVisible(); }); test("live camera recovers mojibake viewer titles before rendering", async ({ page }) => { const state = await installAppMocks(page, { authenticated: true, liveViewerMode: true }); const mojibakeTitle = Buffer.from("服务端同步烟雾测试", "utf8").toString("latin1"); if (state.liveRuntime.runtimeSession) { state.liveRuntime.runtimeSession.title = mojibakeTitle; state.liveRuntime.runtimeSession.snapshot = { ...state.liveRuntime.runtimeSession.snapshot, title: mojibakeTitle, }; } await page.goto("/live-camera"); await expect(page.getByRole("heading", { name: "服务端同步烟雾测试" })).toBeVisible(); await expect(page.getByText(mojibakeTitle)).toHaveCount(0); }); test("live camera no longer opens viewer peer retries when server relay is active", async ({ page }) => { const state = await installAppMocks(page, { authenticated: true, liveViewerMode: true, viewerSignalConflictOnce: true, }); await page.goto("/live-camera"); await expect(page.getByText("同步观看模式")).toBeVisible(); await expect.poll(() => state.viewerSignalConflictRemaining).toBe(1); await expect.poll(() => state.mediaSession?.viewerCount ?? 0).toBe(0); await expect(page.locator('img[alt="同步中的实时分析画面"]')).toBeVisible(); }); test("live camera archives overlay videos into the library after analysis stops", async ({ page }) => { await installAppMocks(page, { authenticated: true, videos: [] }); await page.goto("/live-camera"); await page.getByRole("button", { name: "下一步" }).click(); await page.getByRole("button", { name: "下一步" }).click(); await page.getByRole("button", { name: "下一步" }).click(); await page.getByRole("button", { name: /启用摄像头/ }).click(); await expect(page.getByTestId("live-camera-analyze-button")).toBeVisible(); await page.getByTestId("live-camera-analyze-button").click(); await expect(page.getByTestId("live-camera-score-overall")).toBeVisible(); await page.getByRole("button", { name: "结束分析" }).click(); await expect(page.getByText("分析结果已保存")).toBeVisible({ timeout: 8_000 }); await page.goto("/videos"); await expect(page.getByTestId("video-card")).toHaveCount(1); await expect(page.getByText("实时分析录像").first()).toBeVisible(); await expect(page.getByText("实时分析").first()).toBeVisible(); }); test("recorder flow archives a session and exposes it in videos", async ({ page }) => { await installAppMocks(page, { authenticated: true, videos: [] }); await page.setViewportSize({ width: 390, height: 844 }); await page.goto("/recorder"); await expect(page.getByTestId("recorder-title")).toBeVisible(); await page.getByTestId("recorder-mobile-focus-button").click(); const focusShell = page.getByTestId("recorder-mobile-focus-shell"); await expect(focusShell).toBeVisible(); await focusShell.getByTestId("recorder-start-camera-button").click(); await expect(focusShell.getByTestId("recorder-start-recording-button")).toBeVisible(); await focusShell.getByTestId("recorder-start-recording-button").click(); await expect(focusShell.getByTestId("recorder-marker-button")).toBeVisible(); await focusShell.getByTestId("recorder-marker-button").click(); await expect(page.getByText("手动标记")).toBeVisible(); await focusShell.getByTestId("recorder-finish-button").click(); await expect(focusShell.getByTestId("recorder-reset-button")).toBeVisible({ timeout: 8_000 }); await page.goto("/videos"); await expect(page.getByTestId("video-card")).toHaveCount(1); await expect(page.getByText("E2E 录制")).toBeVisible(); }); test("recorder blocks local camera when another device owns live analysis", async ({ page }) => { await installAppMocks(page, { authenticated: true, liveViewerMode: true }); await page.goto("/recorder"); await expect(page.getByText("当前账号已有其他设备正在实时分析")).toBeVisible(); await expect(page.getByTestId("recorder-start-camera-button")).toBeDisabled(); });