文件
hyb-oa/client/tests/e2e/mobile-approvals-offline.spec.ts
T

129 行
5.8 KiB
TypeScript

import { devices, expect, test } from "@playwright/test";
import { createAuthenticatedApiClient, MANAGER, type E2EUser } from "./helpers/auth";
import { createSubmittedTravelRequestViaApi } from "./helpers/travel";
import { approveWorkflowTaskViaApi, waitForWorkflowTask } from "./helpers/workflow";
async function loginMobile(page: import("@playwright/test").Page, user: E2EUser) {
await page.goto("/login");
await page.getByTestId("login-identifier").fill(user.identifier);
await page.getByTestId("login-password").fill(user.password);
await page.getByTestId("login-submit").click();
await expect(page).toHaveURL(/\/mobile$/);
}
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<IDBDatabase>((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<unknown>((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 loginMobile(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 loginMobile(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()]);
}
});