部署并复检统一页面改造(05-30-deploy-unified-page-design-public-check)
这个提交包含在:
@@ -107,7 +107,7 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
|
||||
### 3. Contracts
|
||||
|
||||
- `assertPortalLoaded(page)` must assert route and page content first:
|
||||
`/portal` URL and the `今日任务` heading.
|
||||
`/portal` URL and the `今日待处理` heading.
|
||||
- Navigation-label assertions for the workbench link must accept both `工作台`
|
||||
and `Portal` while older mocks still use English labels.
|
||||
- Navigation assertions must be role-aware. Admin/manager smoke may assert
|
||||
@@ -121,11 +121,11 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Page reaches `/portal`, `今日任务` is visible, but nav label is `工作台` ->
|
||||
- Page reaches `/portal`, `今日待处理` is visible, but nav label is `工作台` ->
|
||||
smoke should pass.
|
||||
- Employee reaches `/portal`, role-aware navigation hides `团队`, and employee
|
||||
pages such as `项目` remain reachable -> smoke should pass.
|
||||
- Page reaches `/portal`, but `今日任务` is missing -> smoke should fail.
|
||||
- Page reaches `/portal`, but `今日待处理` is missing -> smoke should fail.
|
||||
- Page stays on login, `/403`, error fallback or another route -> smoke should
|
||||
fail.
|
||||
- New shell label appears only in one live smoke test -> move the compatibility
|
||||
@@ -133,7 +133,7 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: `assertPortalLoaded` checks `/portal`, `今日任务`, and a workbench link
|
||||
- Good: `assertPortalLoaded` checks `/portal`, `今日待处理`, and a workbench link
|
||||
matching either `工作台` or `Portal`.
|
||||
- Good: `assertMainNavVisible(page, { expectTeam: false })` is used for
|
||||
ordinary employee live smoke.
|
||||
|
||||
@@ -1510,8 +1510,10 @@ onNeedRefresh() {
|
||||
|
||||
### 4. Login Entry Hierarchy
|
||||
|
||||
- Normal browser: account/password login is primary; Enterprise WeChat and OIDC
|
||||
entries belong below the primary submit button as secondary methods.
|
||||
- Normal browser: account/password or email-code login is primary. Enterprise
|
||||
WeChat is shown as a boundary notice or secondary entry that tells users to
|
||||
open OA from the Enterprise WeChat workbench; do not require a clickable OAuth
|
||||
button in ordinary browsers.
|
||||
- Enterprise WeChat WebView: Enterprise WeChat one-click entry is primary;
|
||||
account/password login remains available as the fallback path.
|
||||
- Enterprise WeChat entry changes must remain frontend entry hierarchy changes
|
||||
@@ -1528,7 +1530,7 @@ onNeedRefresh() {
|
||||
- keyboard Tab from the username/email field lands on the password input, not
|
||||
the forgot-password link;
|
||||
- password recovery request-code and reset flow on `/forgot-password`;
|
||||
- normal-browser Enterprise WeChat entry as secondary CTA;
|
||||
- normal-browser Enterprise WeChat boundary notice or secondary entry;
|
||||
- WeCom WebView Enterprise WeChat entry as primary CTA with password fallback.
|
||||
- UI design rules check and Playwright screenshot evidence when the auth layout
|
||||
changes visibly.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{"file": ".trellis/spec/hyb-oa/project-governance.md", "reason": "Verify task metadata, scoped commits, approvals and finish requirements."}
|
||||
{"file": ".trellis/spec/hyb-oa/deployment-trialrun.md", "reason": "Verify public deploy evidence, readiness, smoke and can-test declaration."}
|
||||
{"file": ".trellis/spec/hyb-oa/frontend-react-vite-design.md", "reason": "Verify frontend test and visual baseline expectations."}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"file": ".trellis/spec/hyb-oa/project-governance.md", "reason": "Trellis task, deployment approval, commit and solo-maintenance rules for this deploy/check task."}
|
||||
{"file": ".trellis/spec/hyb-oa/deployment-trialrun.md", "reason": "Public deployment command, smoke, live readiness and rollback gates."}
|
||||
{"file": ".trellis/spec/hyb-oa/frontend-react-vite-design.md", "reason": "Frontend deploy/test expectations and mature enterprise page baseline for visual checks."}
|
||||
@@ -0,0 +1,18 @@
|
||||
# 实施方案
|
||||
|
||||
## 步骤
|
||||
|
||||
1. 预检本地状态:确认工作区干净、当前 commit、基础前端构建和部署环境契约。
|
||||
2. 安全部署:运行 `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`,不覆盖远端 `.env`。
|
||||
3. 公网复检:检查 `/ready`、public bundle copy、live smoke readiness;如果凭据不足,保留脚本 SKIP 记录。
|
||||
4. 页面抽查:用公网地址检查工作台、待我处理、采购、合同、项目、资产、应付账款等本轮重点页面是否反映统一设计规则。
|
||||
5. 若发现阻断:只做最小修复并重新部署验证;涉及远端配置、真实数据或写企业微信时停止并单独确认。
|
||||
6. 收尾:记录结果,提交任务文档或必要修复,明确是否可线上测试。
|
||||
|
||||
## 风险控制
|
||||
|
||||
- 不使用 `SYNC_ENV=1`。
|
||||
- 不执行数据库 reset。
|
||||
- 不执行真实付款、企业微信写入或外部系统写入。
|
||||
- 不把 secret、账号、完整 payload 写入文档或回复。
|
||||
- 发现历史 artifact / rsync 清理问题时,先保守排查并只清理部署排除目录或历史产物,不动业务数据目录。
|
||||
@@ -0,0 +1,97 @@
|
||||
# 部署最新页面改造并公网复检
|
||||
|
||||
## Goal
|
||||
|
||||
把已提交的统一页面设计改造部署到公网试运行环境,并完成公网可用性、关键页面冒烟和基础视觉/代码审计,明确当前线上是否可以让用户测试。
|
||||
|
||||
## What I already know
|
||||
|
||||
- 最新页面改造已提交:
|
||||
- `d5ef818` 统一业务台账首屏设计(05-30-unified-page-design-review)
|
||||
- `74db53b` 归档统一页面设计规范重审(05-30-unified-page-design-review)
|
||||
- 上一轮本地验证通过:`git diff --check`、Trellis task validate、Trellis governance check。
|
||||
- 用户要求继续推进;结合上一轮结论,本任务范围是部署和公网复检。
|
||||
- 公网试运行环境是准生产环境,部署和公网检查必须记录命令、commit、`/ready`、smoke 结果和是否可测试。
|
||||
- 默认部署命令是 `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`,不得覆盖远端 `.env`。
|
||||
- 部署脚本已包含环境契约、远端 `/ready`、external deps smoke、runtime smoke 和公网 bundle 文案扫描。
|
||||
|
||||
## Assumptions
|
||||
|
||||
- 本轮只部署已提交代码,不修改生产配置、不重置数据库、不写企业微信、不执行真实付款。
|
||||
- 如果缺少 live smoke 凭据或真实 OCR 样本,按脚本规则记录 SKIP,不阻塞基础部署结论。
|
||||
- 如果部署或公网检查失败,先定位并修复最小问题,再重新验证;涉及真实配置变更时再单独请求确认。
|
||||
|
||||
## Scope
|
||||
|
||||
- 部署当前 `main` HEAD 到 `https://oa.hyinda.com` 对应试运行环境。
|
||||
- 执行部署前本地质量门禁。
|
||||
- 执行部署脚本的安全模式发布。
|
||||
- 执行公网 `/ready`、公网 bundle copy、live smoke readiness。
|
||||
- 对这次修改涉及的主要页面做公网视觉/交互抽查:
|
||||
- 工作台
|
||||
- 待我处理
|
||||
- 采购
|
||||
- 合同
|
||||
- 项目
|
||||
- 资产
|
||||
- 应付账款
|
||||
- 输出是否可以线上测试的结论。
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- 不改业务功能范围。
|
||||
- 不新增 UI 设计规则。
|
||||
- 不做数据库重置、远端 `.env` 覆盖、企业微信写入或真实付款。
|
||||
- 不导入真实业务数据。
|
||||
|
||||
## Requirements
|
||||
|
||||
- 部署前必须确认工作区状态,避免带未提交改动发布。
|
||||
- 使用 `SYNC_ENV=0`,不覆盖远端 `.env`。
|
||||
- 发布后必须检查公网 `/ready`。
|
||||
- 面向用户可测试的声明必须以部署和公网检查通过为前提。
|
||||
- 如果发现视觉或交互问题,优先判断是否属于本轮部署阻断;非阻断问题另建后续改造任务。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [x] 当前 commit 已部署到公网试运行环境。
|
||||
- [x] `https://oa.hyinda.com/ready` 通过。
|
||||
- [x] 部署脚本内置 smoke 通过或失败原因已记录。
|
||||
- [x] `scripts/qa/run-live-smoke-readiness.sh` 通过。
|
||||
- [x] 至少完成工作台、待我处理和本轮主要业务台账页面的公网抽查。
|
||||
- [x] 最终结论明确:可以线上测试 / 已部署但仍有阻断 / 未部署成功。
|
||||
|
||||
## Test Plan
|
||||
|
||||
- `git status --short`
|
||||
- `cd client && npm run typecheck`
|
||||
- `cd client && npm run build`
|
||||
- `node deploy/trialrun/validate-env-contract.js`
|
||||
- `SYNC_ENV=0 bash deploy/trialrun/deploy.sh`
|
||||
- `LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh`
|
||||
- 必要时运行聚焦公网 Playwright 或截图检查。
|
||||
- `git diff --check`
|
||||
- `python3 ./.trellis/scripts/task.py validate 05-30-deploy-unified-page-design-public-check`
|
||||
- `node scripts/qa/check-trellis-governance.mjs`
|
||||
|
||||
## Technical Notes
|
||||
|
||||
- 相关规范:
|
||||
- `.trellis/spec/hyb-oa/project-governance.md`
|
||||
- `.trellis/spec/hyb-oa/deployment-trialrun.md`
|
||||
- `.trellis/spec/hyb-oa/frontend-react-vite-design.md`
|
||||
- 相关脚本:
|
||||
- `deploy/trialrun/deploy.sh`
|
||||
- `scripts/qa/run-live-smoke-readiness.sh`
|
||||
- `scripts/qa/check-public-bundle-copy.mjs`
|
||||
- 本轮部署 commit:`74db53b`。
|
||||
- 公网 readiness 报告:`artifacts/qa-flow/live-smoke-readiness-20260530-200338`。
|
||||
- 公网页面抽查截图:`output/playwright/05-30-public-ledger-check/`。
|
||||
- `r104-public-visual-acceptance` 原先在普通浏览器要求企业微信 OAuth 按钮;当前产品规则是普通浏览器展示企业微信工作台边界说明,企业微信 WebView UA 才展示一键进入按钮。本任务已同步修正断言。
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- 公网部署与复检完成。
|
||||
- 阻断问题已修复或明确记录。
|
||||
- 任务文档、验证命令和提交记录完整。
|
||||
- 最终回复包含 commit、部署状态、公网检查结果和可测试结论。
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "deploy-unified-page-design-public-check",
|
||||
"name": "deploy-unified-page-design-public-check",
|
||||
"title": "部署最新页面改造并公网复检",
|
||||
"description": "部署最新统一页面设计改造到公网试运行环境,并完成公网 readiness、live smoke、视觉抽查和收尾审计。",
|
||||
"status": "in_progress",
|
||||
"dev_type": null,
|
||||
"scope": "deploy,qa,frontend-test",
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "shunli",
|
||||
"assignee": "shunli",
|
||||
"createdAt": "2026-05-30",
|
||||
"completedAt": null,
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import { getExpectedWecomStatus, loginLiveUser, logoutLiveUser } from "./helpers
|
||||
import { fetchWecomConfig } from "./helpers/smokeAssertions";
|
||||
|
||||
const screenshotDir = path.resolve(process.cwd(), "../output/playwright/r104");
|
||||
const WECOM_UA =
|
||||
"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 Language/zh_CN";
|
||||
|
||||
function screenshotPath(fileName: string) {
|
||||
mkdirSync(screenshotDir, { recursive: true });
|
||||
@@ -30,7 +32,8 @@ test.describe("R104 public visual acceptance", () => {
|
||||
await expect(page.getByTestId("login-identifier")).toBeVisible();
|
||||
await expect(page.getByTestId("login-password")).toBeVisible();
|
||||
await expect(page.getByTestId("login-submit")).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: /企业微信/ })).toBeVisible();
|
||||
await expect(page.getByTestId("wecom-entry-browser-notice")).toBeVisible();
|
||||
await expect(page.getByTestId("wecom-entry-browser-notice")).toContainText("企业微信一键进入");
|
||||
|
||||
const wecomConfig = await fetchWecomConfig(page);
|
||||
expect(wecomConfig.status).toBe(200);
|
||||
@@ -42,15 +45,24 @@ test.describe("R104 public visual acceptance", () => {
|
||||
await page.screenshot({ path: screenshotPath("desktop-login.png"), fullPage: true });
|
||||
});
|
||||
|
||||
test("公网企业微信入口跳转到 OAuth 边界", async ({ page }) => {
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
test("公网企业微信 WebView 入口跳转到 OAuth 边界", async ({ browser }) => {
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: 390, height: 844 },
|
||||
isMobile: true,
|
||||
hasTouch: true,
|
||||
userAgent: WECOM_UA,
|
||||
locale: "zh-CN",
|
||||
serviceWorkers: "block",
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto("/login");
|
||||
|
||||
await expect(page.getByRole("button", { name: /企业微信/ })).toBeVisible();
|
||||
await expect(page.getByTestId("wecom-entry-primary")).toBeVisible();
|
||||
await page.screenshot({ path: screenshotPath("mobile-login-before-wecom.png"), fullPage: true });
|
||||
|
||||
await page.getByRole("button", { name: /企业微信/ }).click();
|
||||
await page.getByTestId("wecom-entry-primary").click();
|
||||
await expect(page).toHaveURL(/open\.weixin\.qq\.com|work\.weixin\.qq\.com|wechat_redirect/);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
test("公网员工移动工作台与高频页面保持移动体验", async ({ page }) => {
|
||||
|
||||
在新工单中引用
屏蔽一个用户