import { mkdirSync } from "node:fs"; import path from "node:path"; import { expect, test, type Locator, type Page } from "@playwright/test"; import { getExpectedWecomStatus, loginLiveUser } from "./helpers/liveAuth"; import { fetchWecomConfig } from "./helpers/smokeAssertions"; const screenshotDir = path.resolve(process.cwd(), "../output/playwright/r105"); function screenshotPath(fileName: string) { mkdirSync(screenshotDir, { recursive: true }); return path.join(screenshotDir, fileName); } async function expectNoLegacyDesignCopy(page: Page) { const bodyText = await page.locator("body").innerText(); expect(bodyText).not.toContain("先处理"); expect(bodyText).not.toContain("清爽"); expect(bodyText).not.toContain("低频模块放在这里"); expect(bodyText).not.toContain("/invoice-pool"); expect(bodyText).not.toContain("/purchase-requests"); } async function expectNoDesktopTableOnMobile(page: Page) { await expect(page.locator("table")).toHaveCount(0); } function parseRgb(value: string) { const match = value.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([0-9.]+))?/i); if (!match) { return null; } return { r: Number(match[1]), g: Number(match[2]), b: Number(match[3]), a: match[4] == null ? 1 : Number(match[4]), }; } async function expectNotBlackSurface(locator: Locator) { const backgroundColor = await locator.evaluate((element) => { return window.getComputedStyle(element).backgroundColor; }); const rgb = parseRgb(backgroundColor); if (!rgb) { return; } if (rgb.a === 0) { return; } const average = (rgb.r + rgb.g + rgb.b) / 3; expect(average, `backgroundColor=${backgroundColor}`).toBeGreaterThan(24); } test.describe("R105 pretrial visual acceptance", () => { test.skip(process.env.E2E_MODE !== "live", "仅在公网 live 模式下执行"); test("公网登录与企业微信入口仍默认进入移动工作台", async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); await page.goto("/login"); await expect(page.getByRole("button", { name: /企业微信/ })).toBeVisible(); const wecomConfig = await fetchWecomConfig(page); expect(wecomConfig.status).toBe(200); expect(wecomConfig.data?.status).toBe(getExpectedWecomStatus()); expect(wecomConfig.data?.defaultReturnTo).toBe("/mobile"); expect(wecomConfig.data?.loginUrl).toContain("returnTo=%2Fmobile"); await expectNoLegacyDesignCopy(page); await page.screenshot({ path: screenshotPath("mobile-login-wecom-entry.png"), fullPage: true }); }); test("员工移动首页、票据上传和我的页面保持手机体验", async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); await loginLiveUser(page, "employee"); await page.goto("/mobile"); await expect(page.getByTestId("mobile-nav-home")).toBeVisible(); await expect(page.getByTestId("mobile-nav-reimbursements")).toBeVisible(); await expectNoDesktopTableOnMobile(page); await expectNoLegacyDesignCopy(page); await expectNotBlackSurface(page.getByTestId("mobile-nav-home")); await page.screenshot({ path: screenshotPath("employee-mobile-home.png"), fullPage: true }); await page.goto("/mobile/capture"); await expect(page.getByRole("heading", { name: "票据上传", exact: true })).toBeVisible(); const fileInput = page.getByTestId("mobile-capture-pdf-file"); await expect(fileInput).toHaveAttribute("multiple", ""); await expect(fileInput).toHaveAttribute("accept", ".pdf,application/pdf"); await expect(fileInput).not.toHaveAttribute("capture", /.+/); await expect(page.getByTestId("mobile-capture-upload")).toBeVisible(); await expectNoDesktopTableOnMobile(page); await expectNoLegacyDesignCopy(page); await page.screenshot({ path: screenshotPath("employee-mobile-capture.png"), fullPage: true }); await page.goto("/mobile/me"); await expect(page.getByRole("heading", { name: "我的", exact: true })).toBeVisible(); await expect(page.getByTestId("mobile-more-links")).toBeVisible(); await expectNoDesktopTableOnMobile(page); await expectNoLegacyDesignCopy(page); await page.screenshot({ path: screenshotPath("employee-mobile-me.png"), fullPage: true }); }); test("管理员移动审批页不退回桌面表格体验", async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); await loginLiveUser(page, "admin"); await page.goto("/mobile/approvals"); await expect(page.getByRole("heading", { name: /审批/ })).toBeVisible(); await expect(page.getByText("待处理").first()).toBeVisible(); await expectNoDesktopTableOnMobile(page); await expectNoLegacyDesignCopy(page); await expectNotBlackSurface(page.getByTestId("mobile-nav-approvals")); await page.screenshot({ path: screenshotPath("admin-mobile-approvals.png"), fullPage: true }); }); test("PC Portal、报销和票据保持试运行首页一致性", async ({ page }) => { await page.setViewportSize({ width: 1440, height: 960 }); await loginLiveUser(page, "employee"); await page.goto("/portal"); await expect(page.getByRole("heading", { name: "今日待处理" })).toBeVisible(); await expectNoLegacyDesignCopy(page); await page.screenshot({ path: screenshotPath("desktop-portal.png"), fullPage: true }); await page.goto("/reimbursements"); await expect(page.getByRole("heading", { name: /报销/ })).toBeVisible(); await expectNoLegacyDesignCopy(page); await page.screenshot({ path: screenshotPath("desktop-reimbursements.png"), fullPage: true }); await page.goto("/invoice-pool"); await expect(page.getByRole("heading", { name: "票据", exact: true })).toBeVisible(); await expectNoLegacyDesignCopy(page); await page.screenshot({ path: screenshotPath("desktop-invoice-pool.png"), fullPage: true }); }); });