import { devices, expect, test } from "@playwright/test"; import { createAuthenticatedApiClient, login, MANAGER } from "./helpers/auth"; import { createSubmittedTravelRequestViaApi } from "./helpers/travel"; import { approveWorkflowTaskViaApi, waitForWorkflowTask } from "./helpers/workflow"; async function waitForWorkflowTaskCache(page: import("@playwright/test").Page, taskId: string) { await expect .poll( async () => { return page.evaluate(async (currentTaskId) => { const request = window.indexedDB.open("hyb-oa-mobile-workflow", 1); const db = await new Promise((resolve, reject) => { request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error ?? new Error("indexeddb_open_failed")); }); try { if (!db.objectStoreNames.contains("workflow_task_cache")) { return false; } const store = db.transaction("workflow_task_cache", "readonly").objectStore("workflow_task_cache"); const detail = await new Promise((resolve, reject) => { const query = store.get(currentTaskId); query.onsuccess = () => resolve(query.result ?? null); query.onerror = () => reject(query.error ?? new Error("indexeddb_read_failed")); }); return Boolean(detail); } finally { db.close(); } }, taskId); }, { timeout: 15_000 }, ) .toBe(true); } test.use({ ...devices["iPhone 13"], serviceWorkers: "block", userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 wxwork/4.1.20 MicroMessenger/8.0.1", }); test("E2E 移动审批:deep link 分流 + 离线草稿重放", async ({ page }) => { const created = await createSubmittedTravelRequestViaApi({ purposePrefix: "Playwright移动审批通过", amount: "999.00", }); const managerApi = await createAuthenticatedApiClient(MANAGER); try { const task = await waitForWorkflowTask(managerApi.api, { entityId: created.travelRequest.requestId, entityType: "travel_request", }); await login(page, MANAGER); await page.goto(`/workflow/tasks/${task.id}`); await expect(page).toHaveURL(new RegExp(`/mobile/approvals\\?taskId=${task.id}`)); await expect(page.getByTestId("mobile-approvals-detail")).toBeVisible({ timeout: 30_000 }); await expect(page.getByTestId("mobile-approvals-detail")).toContainText(created.travelRequest.requestNo); await waitForWorkflowTaskCache(page, task.id); await page.context().setOffline(true); await expect(page.getByText("当前离线")).toBeVisible(); await expect(page.getByTestId("mobile-approvals-approve")).toBeEnabled({ timeout: 15_000 }); await page.getByTestId("mobile-approvals-approve").click(); await expect(page.getByTestId("mobile-approvals-queue")).toContainText("待处理 1 项"); await expect(page.getByTestId("mobile-approvals-queue")).toContainText("通过"); await page.context().setOffline(false); await page.evaluate(() => window.dispatchEvent(new Event("online"))); await expect(page.getByTestId("mobile-approvals-queue")).toContainText("无待处理项", { timeout: 30_000 }); await expect(page.getByRole("main").getByText(/草稿同步完成:成功 1,失败 0,冲突 0/)).toBeVisible({ timeout: 30_000 }); } finally { await Promise.all([created.client.dispose(), managerApi.dispose()]); } }); test("E2E 移动审批:离线草稿冲突态可见", async ({ page }) => { const created = await createSubmittedTravelRequestViaApi({ purposePrefix: "Playwright移动审批冲突", amount: "1099.00", }); const managerApi = await createAuthenticatedApiClient(MANAGER); try { const task = await waitForWorkflowTask(managerApi.api, { entityId: created.travelRequest.requestId, entityType: "travel_request", }); await login(page, MANAGER); await page.goto(`/workflow/tasks/${task.id}`); await expect(page).toHaveURL(new RegExp(`/mobile/approvals\\?taskId=${task.id}`)); await expect(page.getByTestId("mobile-approvals-detail")).toBeVisible({ timeout: 30_000 }); await expect(page.getByTestId("mobile-approvals-detail")).toContainText(created.travelRequest.requestNo); await waitForWorkflowTaskCache(page, task.id); await page.context().setOffline(true); await expect(page.getByTestId("mobile-approvals-reject")).toBeEnabled({ timeout: 15_000 }); await page.getByTestId("mobile-approvals-comment").fill("离线驳回意见:请补充差旅安排说明"); await page.getByTestId("mobile-approvals-reject").click(); await expect(page.getByTestId("mobile-approvals-queue")).toContainText("待处理 1 项"); await expect(page.getByTestId("mobile-approvals-queue")).toContainText("驳回"); await approveWorkflowTaskViaApi(managerApi.api, task.id); await page.context().setOffline(false); await page.evaluate(() => window.dispatchEvent(new Event("online"))); await expect(page.getByTestId("mobile-approvals-queue")).toContainText("存在冲突", { timeout: 30_000 }); await expect(page.getByTestId("mobile-approvals-queue")).toContainText("最近错误:", { timeout: 30_000 }); await expect(page.getByRole("button", { name: "查看任务" })).toBeVisible(); await expect(page.getByRole("button", { name: "丢弃草稿" })).toBeVisible(); } finally { await Promise.all([created.client.dispose(), managerApi.dispose()]); } });