diff --git a/.trellis/spec/hyb-oa/deployment-trialrun.md b/.trellis/spec/hyb-oa/deployment-trialrun.md index 42501e38..fa4b6640 100644 --- a/.trellis/spec/hyb-oa/deployment-trialrun.md +++ b/.trellis/spec/hyb-oa/deployment-trialrun.md @@ -85,3 +85,79 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh node deploy/trialrun/validate-env-contract.js SYNC_ENV=0 bash deploy/trialrun/deploy.sh ``` + +## Scenario: Public Smoke Navigation Labels + +### 1. Scope / Trigger + +- Trigger: frontend shell, navigation labels, sidebar grouping, Portal/workbench + copy, or live smoke assertions change. +- Purpose: prevent public deployment from being marked failed only because + smoke tests look for stale navigation copy while the page itself is healthy. + +### 2. Signatures + +- Live smoke command: + `E2E_MODE=live LIVE_SMOKE_BASE_URL=https://oa.hyinda.com npx playwright test tests/e2e/live-smoke.spec.ts` +- Shared assertions: `client/tests/e2e/helpers/smokeAssertions.ts` +- Workbench route: `/portal` +- Current primary workbench nav label: `工作台` +- Historical nav label still allowed in mocks or older fixtures: `Portal` + +### 3. Contracts + +- `assertPortalLoaded(page)` must assert route and page content first: + `/portal` URL and the `今日任务` heading. +- Navigation-label assertions for the workbench link must accept both `工作台` + and `Portal` while older mocks still use English labels. +- Smoke tests must not require a copy string that is not a product contract for + public availability. +- If the product intentionally renames the primary workbench label again, update + the shared smoke helper rather than duplicating route-specific assertions in + each live smoke test. + +### 4. Validation & Error Matrix + +- Page reaches `/portal`, `今日任务` is visible, but nav label is `工作台` -> + smoke should pass. +- 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 + logic into `smokeAssertions.ts`. + +### 5. Good/Base/Bad Cases + +- Good: `assertPortalLoaded` checks `/portal`, `今日任务`, and a workbench link + matching either `工作台` or `Portal`. +- Base: mocked E2E fixtures may still emit `Portal`; live public navigation may + emit `工作台`. +- Bad: live smoke hard-codes only `Portal` and blocks deployment after a valid + Chinese navigation rollout. + +### 6. Tests Required + +- After changing live smoke assertions, run: + `cd client && npm run typecheck` +- For public deploy verification, run live smoke or the readiness wrapper: + `LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh` +- When changing the shell or Portal page, include a focused Playwright check + that proves the workbench route and heading still load. + +### 7. Wrong vs Correct + +#### Wrong + +```ts +await expect(page.getByRole("link", { name: "Portal" }).first()).toBeVisible(); +``` + +#### Correct + +```ts +await expect( + page.getByRole("link", { name: "工作台", exact: true }) + .or(page.getByRole("link", { name: "Portal", exact: true })) + .first(), +).toBeVisible(); +``` diff --git a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/prd.md b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/prd.md index 13a07b85..a6a39563 100644 --- a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/prd.md +++ b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/prd.md @@ -6,6 +6,14 @@ 本任务只做已提交前端改动的部署、对比和只读验证;不继续扩大页面重构范围,不写真实业务数据,不覆盖远端 `.env`。 +## Scope + +- 部署当前 `main` 中第二轮前端降噪改动到公网试运行环境。 +- 生成审批中心、报销详情和审批任务详情的第二轮前后截图对比。 +- 修正部署门禁中暴露的非业务阻断,例如正式文案和测试断言漂移。 +- 执行部署前本地门禁和部署后公网只读验证矩阵。 +- 在当前 Trellis task 中保存脱敏摘要和相对报告路径。 + ## What I Already Know - 用户已经确认第一轮对比效果,并要求“保持这样,作为统一设计规则”。 @@ -46,17 +54,21 @@ ## Acceptance Criteria - [x] 用户明确批准继续执行部署与第二轮对比。 -- [ ] `prd.md`、`implement.md` 和上下文登记完成,并通过 Trellis task validate。 -- [ ] 第二轮前后对比页生成完成,可从 `output/playwright/frontend-simplification-wave2-compare/index.html` 查看。 -- [ ] 部署前本地门禁通过。 -- [ ] `SYNC_ENV=0 bash deploy/trialrun/deploy.sh` 成功,未覆盖远端 `.env`。 -- [ ] 部署后公网 `/ready` 通过。 -- [ ] live smoke / OCR readiness 通过或有可解释 SKIP/PASS_WITH_FINDINGS。 -- [ ] 权限矩阵通过。 -- [ ] PC 只读视觉矩阵无 P1 blocker。 -- [ ] 移动入口和企业微信渲染矩阵通过。 -- [ ] 当前任务记录公网验证摘要和报告相对路径。 -- [ ] Trellis governance 和 `git diff --check` 通过。 +- [x] `prd.md`、`implement.md` 和上下文登记完成,并通过 Trellis task validate。 +- [x] 第二轮前后对比页生成完成,可从 `output/playwright/frontend-simplification-wave2-compare/index.html` 查看。 +- [x] 部署前本地门禁通过。 +- [x] `SYNC_ENV=0 bash deploy/trialrun/deploy.sh` 完成远端服务重建,未覆盖远端 `.env`;脚本内置 live smoke 因测试断言漂移退出,修正后标准 live readiness 已复跑通过。 +- [x] 部署后公网 `/ready` 通过。 +- [x] live smoke / OCR readiness 通过或有可解释 SKIP/PASS_WITH_FINDINGS。 +- [x] 权限矩阵通过。 +- [x] PC 只读视觉矩阵无 P1 blocker。 +- [x] 移动入口和企业微信渲染矩阵通过。 +- [x] 当前任务记录公网验证摘要和报告相对路径。 +- [x] Trellis governance 和 `git diff --check` 通过。 + +## Result + +`READY_FOR_USER_TEST` ## Definition Of Done diff --git a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/env.json b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/env.json new file mode 100644 index 00000000..d754c7e4 --- /dev/null +++ b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/env.json @@ -0,0 +1,46 @@ +{ + "checkedAt": "2026-05-29T05:37:24.101Z", + "baseUrl": "https://oa.hyinda.com", + "liveSmokeReady": true, + "liveSmoke": { + "adminCredentialReady": true, + "employeeCredentialReady": true + }, + "liveSmokeOptional": { + "expectedWecomStatusReady": true, + "managerCredentialReady": true, + "financeCredentialReady": true + }, + "liveOcrReady": true, + "liveOcr": { + "userReady": true, + "invoiceSampleReady": true, + "invoiceFileProvided": true, + "invoiceFileReady": true, + "invoiceBatchDirProvided": false, + "invoiceBatchDirReady": false, + "allowDuplicate": true + }, + "missing": [], + "publicReady": { + "ok": true, + "status": 200 + }, + "wecomConfig": { + "ok": true, + "status": 200, + "statusText": "ready", + "entryEnabled": true, + "oauthEnabled": true, + "defaultReturnTo": "/mobile", + "loginUrl": "/api/auth/wecom/login?returnTo=%2Fmobile" + }, + "authCapabilities": { + "ok": true, + "status": 200, + "emailDeliveryReady": true, + "emailDeliveryReasons": [], + "emailLoginCodeEnabled": true, + "passwordResetEnabled": true + } +} diff --git a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/live-ocr.txt b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/live-ocr.txt new file mode 100644 index 00000000..3beb7fca --- /dev/null +++ b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/live-ocr.txt @@ -0,0 +1,7 @@ + +Running 1 test using 1 worker + +[live-ocr] {"alias":"SAMPLE-PDF-001","status":"ocr_failed","durationMs":1173,"transport":"ui","timings":{"uploadMs":1173,"terminalWaitMs":0},"reasonCategory":"duplicate_or_same_invoice","fieldHits":{"invoiceId":false,"invoiceCodeOrNumber":false,"sellerName":false,"totalAmount":false,"invoiceDate":false}} + ✓ 1 tests/e2e/manual/live-invoice-ocr.spec.ts:8:1 › 手工 live 腾讯 OCR 辅助复核:真实 VAT 发票上传后进入可处理状态 (6.8s) + + 1 passed (10.5s) diff --git a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/live-smoke.txt b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/live-smoke.txt new file mode 100644 index 00000000..e371eb0c --- /dev/null +++ b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/live-smoke.txt @@ -0,0 +1,14 @@ +>>> 探活 live /ready: https://oa.hyinda.com/ready +>>> 执行 live smoke Playwright + +> client@0.1.0 test:e2e:live +> playwright test tests/e2e/live-smoke.spec.ts + + +Running 3 tests using 1 worker + + ✓ 1 tests/e2e/live-smoke.spec.ts:43:3 › live smoke › 公网基础探活与企微配置状态 (2.5s) + ✓ 2 tests/e2e/live-smoke.spec.ts:64:3 › live smoke › 管理员只读网页 smoke (11.4s) + ✓ 3 tests/e2e/live-smoke.spec.ts:106:3 › live smoke › 员工只读网页 smoke (10.7s) + + 3 passed (28.4s) diff --git a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/pending-ocr.txt b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/pending-ocr.txt new file mode 100644 index 00000000..a8739766 --- /dev/null +++ b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/pending-ocr.txt @@ -0,0 +1 @@ +[pending-ocr][SKIP] missing DATABASE_URL diff --git a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/public-bundle-copy.txt b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/public-bundle-copy.txt new file mode 100644 index 00000000..6d2bd196 --- /dev/null +++ b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/public-bundle-copy.txt @@ -0,0 +1 @@ +[public-bundle-copy] base=https://oa.hyinda.com scanned=91 focused=2 findings=0 diff --git a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/summary.json b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/summary.json new file mode 100644 index 00000000..158a4907 --- /dev/null +++ b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness/summary.json @@ -0,0 +1,12 @@ +{ + "issueId": "frontend-simplification-wave2-deploy-verify", + "baseURL": "https://oa.hyinda.com", + "commit": "c08ba72", + "checkedAt": "2026-05-29T05:38:17.239Z", + "status": "PASS", + "mode": "live-smoke-readiness", + "reportDir": ".trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness", + "reportRef": ".trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-live-smoke-readiness", + "groups": null, + "notes": "公网 /ready、public bundle、pending OCR、live smoke 和真实 OCR 可用性检查;缺少凭据或样票时按脚本规则记录 SKIP。" +} \ No newline at end of file diff --git a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-permission-matrix/summary.json b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-permission-matrix/summary.json new file mode 100644 index 00000000..210c8efd --- /dev/null +++ b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-permission-matrix/summary.json @@ -0,0 +1,2285 @@ +{ + "issueId": "OANEXT2-4", + "status": "PASS_PERMISSION_MATRIX_READY", + "checkedAt": "2026-05-29T05:37:41.962Z", + "baseUrl": "https://oa.hyinda.com", + "privacy": "redacted role/status/count evidence only; no password, token, Secret, full account, invoice, contract, attachment, or raw payload", + "counts": { + "roles": 7, + "positiveProbes": 43, + "negativeProbes": 42 + }, + "issues": [], + "results": [ + { + "key": "employee", + "expectedRoles": [ + "employee" + ], + "maskedIdentifier": "sm***@***.com", + "login": { + "status": "PASS", + "reason": null, + "code": null + }, + "roleMatch": "PASS", + "actualRoles": [ + "employee" + ], + "probes": [ + { + "id": "me", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "status": "active", + "notificationPushEnabled": true, + "roleCodes": [ + "employee" + ] + } + }, + { + "id": "portal", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [ + { + "path": "data.roleCapabilities.canApproveTasks", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.canUseFinance", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.canManageSystem", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.isAdminOnly", + "status": "PASS", + "expected": false, + "actual": false + } + ], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "roleCapabilities": { + "canApproveTasks": false, + "canManageSystem": false, + "canSubmitApplications": true, + "canUseFinance": false, + "canViewTeam": false, + "isAdminOnly": false + } + } + }, + { + "id": "own-invoices", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 5, + "total": 22 + } + }, + { + "id": "navigation", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [ + { + "type": "visible", + "href": "/portal", + "status": "PASS" + }, + { + "type": "visible", + "href": "/reimbursements", + "status": "PASS" + }, + { + "type": "visible", + "href": "/assets", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/workflow/tasks", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/ap-operations", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/payment-batches", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/audit-logs", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/settings", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/feature-flags", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/integrations/erp", + "status": "PASS" + } + ], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "navigationItemCount": 10 + } + }, + { + "id": "budget", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "ap", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "ap-ledger", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "payment-batches", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "all-invoices", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "audit-logs", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "notification-deliveries", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "system-feature-flags", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "erp-status", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "payroll-items", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "performance-cycles", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "travel-requests", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "global-search-sensitive", + "kind": "negative", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [ + { + "type": "forbidden_entity_type", + "value": "contract_registry", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_entity_type", + "value": "confidential_doc_ref", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_entity_type", + "value": "travel_request", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_href_prefix", + "value": "/contracts-registry", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_href_prefix", + "value": "/travel-requests", + "status": "PASS", + "matchedCount": 0 + } + ], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 7, + "total": null + } + } + ], + "missingRoles": [] + }, + { + "key": "manager", + "expectedRoles": [ + "manager", + "employee" + ], + "maskedIdentifier": "sm***@***.com", + "login": { + "status": "PASS", + "reason": null, + "code": null + }, + "roleMatch": "PASS", + "actualRoles": [ + "employee", + "manager" + ], + "probes": [ + { + "id": "me", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "status": "active", + "notificationPushEnabled": true, + "roleCodes": [ + "employee", + "manager" + ] + } + }, + { + "id": "portal", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [ + { + "path": "data.roleCapabilities.canApproveTasks", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "path": "data.roleCapabilities.canUseFinance", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.canManageSystem", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.isAdminOnly", + "status": "PASS", + "expected": false, + "actual": false + } + ], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "roleCapabilities": { + "canApproveTasks": true, + "canManageSystem": false, + "canSubmitApplications": true, + "canUseFinance": false, + "canViewTeam": true, + "isAdminOnly": false + } + } + }, + { + "id": "workflow", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 1, + "total": 1 + } + }, + { + "id": "navigation", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [ + { + "type": "visible", + "href": "/workflow/tasks", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/ap-operations", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/payment-batches", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/audit-logs", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/settings", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/feature-flags", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/integrations/erp", + "status": "PASS" + } + ], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "navigationItemCount": 15 + } + }, + { + "id": "ap", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "ap-ledger", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "payment-batches", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "audit-logs", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "erp-status", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "payroll-items", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "performance-cycles", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "travel-requests", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "global-search-sensitive", + "kind": "negative", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [ + { + "type": "forbidden_entity_type", + "value": "contract_registry", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_entity_type", + "value": "confidential_doc_ref", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_entity_type", + "value": "travel_request", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_href_prefix", + "value": "/contracts-registry", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_href_prefix", + "value": "/travel-requests", + "status": "PASS", + "matchedCount": 0 + } + ], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 10, + "total": null + } + } + ], + "missingRoles": [] + }, + { + "key": "finance", + "expectedRoles": [ + "finance", + "employee" + ], + "maskedIdentifier": "sm***@***.com", + "login": { + "status": "PASS", + "reason": null, + "code": null + }, + "roleMatch": "PASS", + "actualRoles": [ + "employee", + "finance" + ], + "probes": [ + { + "id": "me", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "status": "active", + "notificationPushEnabled": true, + "roleCodes": [ + "employee", + "finance" + ] + } + }, + { + "id": "portal", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [ + { + "path": "data.roleCapabilities.canApproveTasks", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "path": "data.roleCapabilities.canUseFinance", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "path": "data.roleCapabilities.canManageSystem", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.isAdminOnly", + "status": "PASS", + "expected": false, + "actual": false + } + ], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "roleCapabilities": { + "canApproveTasks": true, + "canManageSystem": false, + "canSubmitApplications": true, + "canUseFinance": true, + "canViewTeam": true, + "isAdminOnly": false + } + } + }, + { + "id": "all-invoices", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 5, + "total": 46 + } + }, + { + "id": "payment-batches", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 9, + "total": null + } + }, + { + "id": "ap-ledger", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 5, + "total": 33 + } + }, + { + "id": "navigation", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [ + { + "type": "visible", + "href": "/invoice-pool", + "status": "PASS" + }, + { + "type": "visible", + "href": "/payment-batches", + "status": "PASS" + }, + { + "type": "visible", + "href": "/ap-operations", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/audit-logs", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/settings", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/feature-flags", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/integrations/erp", + "status": "PASS" + } + ], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "navigationItemCount": 18 + } + }, + { + "id": "audit-logs", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "notification-deliveries", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "system-feature-flags", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "erp-status", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "payroll-items", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "performance-cycles", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "travel-requests", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "global-search-sensitive", + "kind": "negative", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [ + { + "type": "forbidden_entity_type", + "value": "contract_registry", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_entity_type", + "value": "confidential_doc_ref", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_entity_type", + "value": "travel_request", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_href_prefix", + "value": "/contracts-registry", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_href_prefix", + "value": "/travel-requests", + "status": "PASS", + "matchedCount": 0 + } + ], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 9, + "total": null + } + } + ], + "missingRoles": [] + }, + { + "key": "finance_mgr", + "expectedRoles": [ + "finance", + "finance_mgr", + "employee" + ], + "maskedIdentifier": "sm***@***.com", + "login": { + "status": "PASS", + "reason": null, + "code": null + }, + "roleMatch": "PASS", + "actualRoles": [ + "employee", + "finance", + "finance_mgr" + ], + "probes": [ + { + "id": "me", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "status": "active", + "notificationPushEnabled": true, + "roleCodes": [ + "employee", + "finance", + "finance_mgr" + ] + } + }, + { + "id": "portal", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [ + { + "path": "data.roleCapabilities.canApproveTasks", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "path": "data.roleCapabilities.canUseFinance", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "path": "data.roleCapabilities.canManageSystem", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "path": "data.roleCapabilities.isAdminOnly", + "status": "PASS", + "expected": false, + "actual": false + } + ], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "roleCapabilities": { + "canApproveTasks": true, + "canManageSystem": true, + "canSubmitApplications": true, + "canUseFinance": true, + "canViewTeam": true, + "isAdminOnly": false + } + } + }, + { + "id": "all-invoices", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 5, + "total": 46 + } + }, + { + "id": "payment-batches", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 9, + "total": null + } + }, + { + "id": "ap-ledger", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 5, + "total": 33 + } + }, + { + "id": "erp-status", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 3, + "total": null + } + }, + { + "id": "audit-logs", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 5, + "total": 2244 + } + }, + { + "id": "notification-deliveries", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 5, + "total": null + } + }, + { + "id": "system-feature-flags", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 26, + "total": null + } + }, + { + "id": "navigation", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [ + { + "type": "visible", + "href": "/invoice-pool", + "status": "PASS" + }, + { + "type": "visible", + "href": "/payment-batches", + "status": "PASS" + }, + { + "type": "visible", + "href": "/ap-operations", + "status": "PASS" + }, + { + "type": "visible", + "href": "/audit-logs", + "status": "PASS" + }, + { + "type": "visible", + "href": "/system/feature-flags", + "status": "PASS" + }, + { + "type": "visible", + "href": "/system/integrations/erp", + "status": "PASS" + } + ], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "navigationItemCount": 27 + } + } + ], + "missingRoles": [] + }, + { + "key": "hr", + "expectedRoles": [ + "hr", + "employee" + ], + "maskedIdentifier": "sm***@***.com", + "login": { + "status": "PASS", + "reason": null, + "code": null + }, + "roleMatch": "PASS", + "actualRoles": [ + "employee", + "hr" + ], + "probes": [ + { + "id": "me", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "status": "active", + "notificationPushEnabled": true, + "roleCodes": [ + "employee", + "hr" + ] + } + }, + { + "id": "portal", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [ + { + "path": "data.roleCapabilities.canApproveTasks", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.canUseFinance", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.canManageSystem", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "path": "data.roleCapabilities.isAdminOnly", + "status": "PASS", + "expected": false, + "actual": false + } + ], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "roleCapabilities": { + "canApproveTasks": false, + "canManageSystem": true, + "canSubmitApplications": true, + "canUseFinance": false, + "canViewTeam": true, + "isAdminOnly": false + } + } + }, + { + "id": "wecom-archive", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 5, + "total": 9 + } + }, + { + "id": "navigation", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [ + { + "type": "visible", + "href": "/people", + "status": "PASS" + }, + { + "type": "visible", + "href": "/system/settings", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/ap-operations", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/payment-batches", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/audit-logs", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/feature-flags", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/integrations/erp", + "status": "PASS" + } + ], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "navigationItemCount": 16 + } + }, + { + "id": "ap-ledger", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "payment-batches", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "audit-logs", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "system-feature-flags", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "payroll-items", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "performance-cycles", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "global-search-sensitive", + "kind": "negative", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [ + { + "type": "forbidden_entity_type", + "value": "contract_registry", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_entity_type", + "value": "confidential_doc_ref", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_entity_type", + "value": "travel_request", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_href_prefix", + "value": "/contracts-registry", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_href_prefix", + "value": "/travel-requests", + "status": "PASS", + "matchedCount": 0 + } + ], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 4, + "total": null + } + } + ], + "missingRoles": [] + }, + { + "key": "ceo", + "expectedRoles": [ + "ceo", + "employee" + ], + "maskedIdentifier": "sm***@***.com", + "login": { + "status": "PASS", + "reason": null, + "code": null + }, + "roleMatch": "PASS", + "actualRoles": [ + "ceo", + "employee" + ], + "probes": [ + { + "id": "me", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "status": "active", + "notificationPushEnabled": true, + "roleCodes": [ + "ceo", + "employee" + ] + } + }, + { + "id": "portal", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [ + { + "path": "data.roleCapabilities.canApproveTasks", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "path": "data.roleCapabilities.canUseFinance", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.canManageSystem", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.isAdminOnly", + "status": "PASS", + "expected": false, + "actual": false + } + ], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "roleCapabilities": { + "canApproveTasks": true, + "canManageSystem": false, + "canSubmitApplications": true, + "canUseFinance": false, + "canViewTeam": true, + "isAdminOnly": false + } + } + }, + { + "id": "dashboard", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "totalCount": 33, + "paidCount": 8, + "reviewingCount": 3 + } + }, + { + "id": "navigation", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [ + { + "type": "visible", + "href": "/workflow/tasks", + "status": "PASS" + }, + { + "type": "visible", + "href": "/budgets", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/ap-operations", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/payment-batches", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/audit-logs", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/feature-flags", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/system/integrations/erp", + "status": "PASS" + } + ], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "navigationItemCount": 15 + } + }, + { + "id": "system-feature-flags", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "notification-deliveries", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "payroll-items", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "performance-cycles", + "kind": "negative", + "status": "PASS", + "httpStatus": 403, + "expectedStatuses": [ + 403, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "code": "PERM_001", + "message": "无权限" + } + }, + { + "id": "global-search-sensitive", + "kind": "negative", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [ + { + "type": "forbidden_entity_type", + "value": "contract_registry", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_entity_type", + "value": "confidential_doc_ref", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_entity_type", + "value": "travel_request", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_href_prefix", + "value": "/contracts-registry", + "status": "PASS", + "matchedCount": 0 + }, + { + "type": "forbidden_href_prefix", + "value": "/travel-requests", + "status": "PASS", + "matchedCount": 0 + } + ], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 9, + "total": null + } + } + ], + "missingRoles": [] + }, + { + "key": "admin", + "expectedRoles": [ + "admin", + "employee" + ], + "maskedIdentifier": "sm***@***.com", + "login": { + "status": "PASS", + "reason": null, + "code": null + }, + "roleMatch": "PASS", + "actualRoles": [ + "admin", + "employee" + ], + "probes": [ + { + "id": "me", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "status": "active", + "notificationPushEnabled": true, + "roleCodes": [ + "admin", + "employee" + ] + } + }, + { + "id": "portal", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [ + { + "path": "data.roleCapabilities.canApproveTasks", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.canUseFinance", + "status": "PASS", + "expected": false, + "actual": false + }, + { + "path": "data.roleCapabilities.canManageSystem", + "status": "PASS", + "expected": true, + "actual": true + }, + { + "path": "data.roleCapabilities.isAdminOnly", + "status": "PASS", + "expected": true, + "actual": true + } + ], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "roleCapabilities": { + "canApproveTasks": false, + "canManageSystem": true, + "canSubmitApplications": true, + "canUseFinance": false, + "canViewTeam": true, + "isAdminOnly": true + } + } + }, + { + "id": "users", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 1, + "total": 13 + } + }, + { + "id": "audit-logs", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 5, + "total": 2247 + } + }, + { + "id": "notification-deliveries", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 5, + "total": null + } + }, + { + "id": "system-feature-flags", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 26, + "total": null + } + }, + { + "id": "payroll-items", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 0, + "total": null + } + }, + { + "id": "performance-cycles", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 1, + "total": null + } + }, + { + "id": "global-search", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200, + 409 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 10, + "total": null + } + }, + { + "id": "assets", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "itemCount": 7, + "total": null + } + }, + { + "id": "navigation", + "kind": "positive", + "status": "PASS", + "httpStatus": 200, + "expectedStatuses": [ + 200 + ], + "dataExpectations": [], + "navigationExpectations": [ + { + "type": "visible", + "href": "/audit-logs", + "status": "PASS" + }, + { + "type": "visible", + "href": "/system/settings", + "status": "PASS" + }, + { + "type": "visible", + "href": "/system/feature-flags", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/workflow/tasks", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/ap-operations", + "status": "PASS" + }, + { + "type": "hidden", + "href": "/payment-batches", + "status": "PASS" + } + ], + "searchExpectations": [], + "sensitiveKeyHits": [], + "summary": { + "navigationItemCount": 26 + } + } + ], + "missingRoles": [] + } + ] +} diff --git a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-permission-matrix/summary.md b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-permission-matrix/summary.md new file mode 100644 index 00000000..1ecec0be --- /dev/null +++ b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-permission-matrix/summary.md @@ -0,0 +1,28 @@ +# OA 权限矩阵脱敏自动验证报告 + +生成时间:2026-05-29T05:37:41.962Z +结论:PASS_PERMISSION_MATRIX_READY + +## 统计 + +- 角色数:7 +- 正向探针:43 +- 负向探针:42 +- 问题数:0 + +## 角色结果 + +| 角色 | 登录 | 角色匹配 | 探针通过/总数 | +| --- | --- | --- | ---: | +| employee | PASS | PASS | 17/17 | +| manager | PASS | PASS | 13/13 | +| finance | PASS | PASS | 14/14 | +| finance_mgr | PASS | PASS | 10/10 | +| hr | PASS | PASS | 11/11 | +| ceo | PASS | PASS | 9/9 | +| admin | PASS | PASS | 11/11 | + +## 说明 + +- 本报告只输出角色、HTTP 状态、脱敏账号和摘要计数,不输出密码、token、Secret、完整账号、合同正文、票据内容或原始 payload。 +- 409 表示功能门禁关闭且未泄露数据;对财务/AP/付款等冻结或受控功能可作为可解释拒绝。 diff --git a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-summary.md b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-summary.md new file mode 100644 index 00000000..29532f9e --- /dev/null +++ b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/research/postdeploy-summary.md @@ -0,0 +1,42 @@ +# Postdeploy Summary + +## Conclusion + +`READY_FOR_USER_TEST` + +## Deployment + +- Deployed commit: `c08ba72` +- Command: `SYNC_ENV=0 bash deploy/trialrun/deploy.sh` +- Remote `.env`: not synced or overwritten. +- Remote service rebuild, init, migration status and runtime deployment stages completed. +- Note: the deploy script's integrated live smoke initially exited on a stale test assertion that looked for the old `Portal` nav label. The current UI exposes `工作台`; the shared smoke assertion was updated to accept both labels, then live smoke and the standard readiness suite passed. + +## Comparison Evidence + +- Second wave comparison page: `output/playwright/frontend-simplification-wave2-compare/index.html` +- Pages compared: approval center, reimbursement detail, workflow task detail. +- Data source: Playwright mock business data. +- Baseline: before `29cfea2`, after `79f50b0` plus deployment gate copy fix in `c08ba72`. + +## Verification Results + +| Check | Result | Evidence | +| --- | --- | --- | +| Task validate | PASS | command output | +| Frontend typecheck | PASS | command output | +| Frontend build | PASS | command output | +| UI design rules | PASS | command output | +| Env contract | PASS | command output | +| Public bundle copy scan | PASS | command output and live readiness report | +| Live smoke readiness | PASS | `research/postdeploy-live-smoke-readiness/summary.json` | +| Permission matrix | PASS_PERMISSION_MATRIX_READY | `research/postdeploy-permission-matrix/summary.json` | +| PC readonly visual matrix | PASS_PC_READONLY_VISUAL_READY | `artifacts/qa-flow/oafront3-pc-readonly-visual-20260529-133832/summary.json` | +| Mobile entry matrix | PASS | command output | +| WeCom rendering matrix | PASS | command output | + +## Non-Blocking Notes + +- `pending OCR` scan reported `SKIP` because the local command did not receive `DATABASE_URL`; live OCR sample still ran through the standard readiness path. +- Live OCR sample returned an explainable duplicate/same-invoice category and the readiness suite concluded PASS. +- `rsync` printed a non-fatal warning for an old remote QA artifact directory that could not be deleted; deployment continued and postdeploy checks passed. diff --git a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/task.json b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/task.json index f75deaea..6206b72c 100644 --- a/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/task.json +++ b/.trellis/tasks/05-29-frontend-simplification-wave2-deploy-verify/task.json @@ -2,10 +2,10 @@ "id": "frontend-simplification-wave2-deploy-verify", "name": "frontend-simplification-wave2-deploy-verify", "title": "部署并复检前端降噪第二轮", - "description": "", + "description": "部署第二轮前端降噪改动,生成审批中心和报销详情对比页,并完成公网只读验证。", "status": "in_progress", "dev_type": null, - "scope": null, + "scope": "frontend-deploy-verify", "package": null, "priority": "P2", "creator": "shunli", @@ -20,7 +20,14 @@ "subtasks": [], "children": [], "parent": null, - "relatedFiles": [], + "relatedFiles": [ + "client/src/pages/PortalHomePage.tsx", + "client/tests/e2e/helpers/smokeAssertions.ts", + "deploy/trialrun/deploy.sh", + "scripts/qa/run-live-smoke-readiness.sh", + "scripts/qa/check-oa-permission-matrix.mjs", + "scripts/qa/run-oafront3-pc-readonly-visual.sh" + ], "notes": "", "meta": {} -} \ No newline at end of file +} diff --git a/client/tests/e2e/helpers/smokeAssertions.ts b/client/tests/e2e/helpers/smokeAssertions.ts index 6bed0d6c..aed620e5 100644 --- a/client/tests/e2e/helpers/smokeAssertions.ts +++ b/client/tests/e2e/helpers/smokeAssertions.ts @@ -23,7 +23,11 @@ type Envelope = { export async function assertPortalLoaded(page: Page) { await expect(page).toHaveURL(/\/portal/); - await expect(page.getByRole("link", { name: "Portal" }).first()).toBeVisible(); + await expect( + page.getByRole("link", { name: "工作台", exact: true }) + .or(page.getByRole("link", { name: "Portal", exact: true })) + .first(), + ).toBeVisible(); await expect(page.getByRole("heading", { name: "今日任务" })).toBeVisible(); } @@ -33,7 +37,11 @@ export async function assertForbiddenPage(page: Page) { } export async function assertMainNavVisible(page: Page) { - await expect(page.getByRole("link", { name: "Portal" }).first()).toBeVisible(); + await expect( + page.getByRole("link", { name: "工作台", exact: true }) + .or(page.getByRole("link", { name: "Portal", exact: true })) + .first(), + ).toBeVisible(); await expect(page.getByRole("link", { name: "团队" }).first()).toBeVisible(); await expect(page.getByRole("link", { name: "项目" }).first()).toBeVisible(); }