R6-ISSUE-002 对齐报销 E2E 导航口径
这个提交包含在:
@@ -33,6 +33,26 @@ export class ReimbursementDetailPageObject {
|
|||||||
return this.page.getByTestId(TEST_IDS.reimbursement.workflowProgress);
|
return this.page.getByTestId(TEST_IDS.reimbursement.workflowProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get invoices() {
|
||||||
|
return this.page.getByTestId(TEST_IDS.reimbursement.invoices);
|
||||||
|
}
|
||||||
|
|
||||||
|
get invoiceAddBtn() {
|
||||||
|
return this.page.getByTestId(TEST_IDS.reimbursement.invoiceAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
get invoiceDialog() {
|
||||||
|
return this.page.getByTestId(TEST_IDS.reimbursement.invoiceDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
get invoiceUploadFile() {
|
||||||
|
return this.page.getByTestId(TEST_IDS.reimbursement.invoiceUploadFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
get invoiceUploadSubmit() {
|
||||||
|
return this.page.getByTestId(TEST_IDS.reimbursement.invoiceUploadSubmit);
|
||||||
|
}
|
||||||
|
|
||||||
get erpExport() {
|
get erpExport() {
|
||||||
return this.page.getByTestId(TEST_IDS.reimbursement.erpExport);
|
return this.page.getByTestId(TEST_IDS.reimbursement.erpExport);
|
||||||
}
|
}
|
||||||
@@ -58,6 +78,36 @@ export class ReimbursementDetailPageObject {
|
|||||||
await expect(this.summary).toContainText(statusText);
|
await expect(this.summary).toContainText(statusText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async uploadInvoicePdf(fileName: string, buffer: Buffer) {
|
||||||
|
await this.invoiceAddBtn.click();
|
||||||
|
await expect(this.invoiceDialog).toBeVisible();
|
||||||
|
|
||||||
|
const uploadResponsePromise = this.page.waitForResponse((response) => {
|
||||||
|
const url = new URL(response.url());
|
||||||
|
return response.request().method() === "POST" && url.pathname === "/api/invoice-pool/upload" && response.ok();
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.invoiceUploadFile.setInputFiles({
|
||||||
|
name: fileName,
|
||||||
|
mimeType: "application/pdf",
|
||||||
|
buffer,
|
||||||
|
});
|
||||||
|
await this.invoiceUploadSubmit.scrollIntoViewIfNeeded();
|
||||||
|
await this.invoiceUploadSubmit.click();
|
||||||
|
|
||||||
|
const uploadPayload = (await (await uploadResponsePromise).json()) as { data?: { invoicePoolId?: string | null } };
|
||||||
|
const invoiceId = uploadPayload.data?.invoicePoolId;
|
||||||
|
if (!invoiceId) {
|
||||||
|
throw new Error(`上传发票后未返回票据 ID: ${JSON.stringify(uploadPayload)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(this.invoices).toContainText(fileName, { timeout: 90_000 });
|
||||||
|
await this.page.keyboard.press("Escape");
|
||||||
|
await expect(this.invoiceDialog).toHaveCount(0);
|
||||||
|
|
||||||
|
return invoiceId;
|
||||||
|
}
|
||||||
|
|
||||||
// 当前 seed 保证 precheck 不触发 warn/manual_review;若 seed 变更,此处需相应调整。
|
// 当前 seed 保证 precheck 不触发 warn/manual_review;若 seed 变更,此处需相应调整。
|
||||||
async submitReimbursement(acceptConfirm = true) {
|
async submitReimbursement(acceptConfirm = true) {
|
||||||
const dialogPromise = acceptConfirm
|
const dialogPromise = acceptConfirm
|
||||||
|
|||||||
@@ -81,60 +81,49 @@ test("E2E 报销支付批次主链路:提交 -> 审批 -> 创建批次 -> 复
|
|||||||
await page.goto(`/reimbursements/${reimbursementId}`);
|
await page.goto(`/reimbursements/${reimbursementId}`);
|
||||||
await reimbursementDetail.assertLoaded();
|
await reimbursementDetail.assertLoaded();
|
||||||
|
|
||||||
await (await openNavLink(page, "票据中心")).click();
|
|
||||||
const marker = `R78_BATCH_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
const marker = `R78_BATCH_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
||||||
const uploadResponsePromise = page.waitForResponse((response) => {
|
const invoiceId = await reimbursementDetail.uploadInvoicePdf(
|
||||||
const url = new URL(response.url());
|
`r78-${Date.now()}.pdf`,
|
||||||
return response.request().method() === "POST" && url.pathname === "/api/invoice-pool/upload" && response.ok();
|
Buffer.from(`%PDF-1.4\n${marker}\n%%EOF\n`, "utf8"),
|
||||||
});
|
);
|
||||||
await page.getByTestId("invoice-upload-file").setInputFiles({
|
|
||||||
name: `r78-${Date.now()}.pdf`,
|
|
||||||
mimeType: "application/pdf",
|
|
||||||
buffer: Buffer.from(`%PDF-1.4\n${marker}\n%%EOF\n`, "utf8"),
|
|
||||||
});
|
|
||||||
await page.getByTestId("invoice-upload-submit").click();
|
|
||||||
const uploadPayload = (await (await uploadResponsePromise).json()) as { data?: { invoicePoolId?: string | null } };
|
|
||||||
const invoiceId = uploadPayload.data?.invoicePoolId;
|
|
||||||
if (!invoiceId) {
|
|
||||||
throw new Error(`上传发票后未返回票据 ID: ${JSON.stringify(uploadPayload)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await expect(page.getByTestId("invoice-detail-panel")).toBeVisible();
|
|
||||||
await expect(page.getByTestId("invoice-detail-panel")).toContainText("状态:可用", { timeout: 90_000 });
|
|
||||||
|
|
||||||
await page.getByTestId("invoice-bind-invoice").selectOption(invoiceId);
|
|
||||||
await page.getByTestId("invoice-bind-reimbursement").selectOption(reimbursementId);
|
|
||||||
await page.getByTestId("invoice-bind-submit").click();
|
|
||||||
await expect(page.getByText("绑定成功")).toBeVisible({ timeout: 30_000 });
|
|
||||||
|
|
||||||
await page.goto(`/reimbursements/${reimbursementId}`);
|
await page.goto(`/reimbursements/${reimbursementId}`);
|
||||||
await reimbursementDetail.assertLoaded();
|
await reimbursementDetail.assertLoaded();
|
||||||
await reimbursementDetail.submitReimbursement();
|
await reimbursementDetail.submitReimbursement();
|
||||||
await expect(page.getByText(/状态:(reviewing|审批中)/)).toBeVisible();
|
await expect(page.getByText(/状态:(reviewing|审批中)/)).toBeVisible();
|
||||||
|
const reimbursementAfterSubmit = await apiGetData<{ formNumber: string | null; totalAmount: string | null }>(
|
||||||
|
employeeApi.api,
|
||||||
|
`/api/reimbursements/${reimbursementId}`,
|
||||||
|
);
|
||||||
|
const reimbursementDisplayCode = reimbursementAfterSubmit.formNumber ?? reimbursementId;
|
||||||
|
const secondApprovalUser = Number(reimbursementAfterSubmit.totalAmount ?? 0) > 500 ? FINANCE_MGR : FINANCE;
|
||||||
|
|
||||||
await logout(page, EMPLOYEE.name);
|
await logout(page, EMPLOYEE.name);
|
||||||
|
|
||||||
await login(page, MANAGER);
|
await login(page, MANAGER);
|
||||||
await (await openNavLink(page, "待我处理")).click();
|
await (await openNavLink(page, "待我处理")).click();
|
||||||
await expect(page.getByTestId(TEST_IDS.workflow.tasksTable)).toBeVisible();
|
await expect(page.getByTestId(TEST_IDS.workflow.tasksTable)).toBeVisible();
|
||||||
const managerTaskRow = page.getByTestId(TEST_IDS.workflow.taskRow).filter({ hasText: reimbursementId }).first();
|
const managerTaskRow = page.getByTestId(TEST_IDS.workflow.taskRow).filter({ hasText: reimbursementDisplayCode }).first();
|
||||||
await expect(managerTaskRow).toBeVisible();
|
await expect(managerTaskRow).toBeVisible();
|
||||||
await managerTaskRow.getByTestId(TEST_IDS.workflow.detailLink).click();
|
await managerTaskRow.getByTestId(TEST_IDS.workflow.detailLink).click();
|
||||||
await workflowDetail.assertLoaded();
|
await workflowDetail.assertLoaded();
|
||||||
await workflowDetail.approve();
|
await workflowDetail.approve();
|
||||||
await expect(workflowDetail.summary).toContainText(/状态:(completed|已完成)/);
|
await expect(workflowDetail.summary).toContainText(/状态:(已处理|completed|已完成)/);
|
||||||
|
|
||||||
await logout(page, MANAGER.name);
|
await logout(page, MANAGER.name);
|
||||||
|
|
||||||
await login(page, FINANCE);
|
await login(page, secondApprovalUser);
|
||||||
await (await openNavLink(page, "待我处理")).click();
|
await (await openNavLink(page, "待我处理")).click();
|
||||||
await expect(page.getByTestId(TEST_IDS.workflow.tasksTable)).toBeVisible();
|
await expect(page.getByTestId(TEST_IDS.workflow.tasksTable)).toBeVisible();
|
||||||
const financeTaskRow = page.getByTestId(TEST_IDS.workflow.taskRow).filter({ hasText: reimbursementId }).first();
|
const financeTaskRow = page.getByTestId(TEST_IDS.workflow.taskRow).filter({ hasText: reimbursementDisplayCode }).first();
|
||||||
await expect(financeTaskRow).toBeVisible();
|
await expect(financeTaskRow).toBeVisible();
|
||||||
await financeTaskRow.getByTestId(TEST_IDS.workflow.detailLink).click();
|
await financeTaskRow.getByTestId(TEST_IDS.workflow.detailLink).click();
|
||||||
await workflowDetail.assertLoaded();
|
await workflowDetail.assertLoaded();
|
||||||
await workflowDetail.approve();
|
await workflowDetail.approve();
|
||||||
await expect(workflowDetail.summary).toContainText(/状态:(completed|已完成)/);
|
await expect(workflowDetail.summary).toContainText(/状态:(已处理|completed|已完成)/);
|
||||||
|
|
||||||
|
await logout(page, secondApprovalUser.name);
|
||||||
|
await login(page, FINANCE);
|
||||||
|
|
||||||
const batchName = `R78-报销付款批次-${Date.now()}`;
|
const batchName = `R78-报销付款批次-${Date.now()}`;
|
||||||
await page.goto("/payment-batches");
|
await page.goto("/payment-batches");
|
||||||
|
|||||||
@@ -24,30 +24,11 @@ test("E2E 报销审批进度:提交后详情页展示当前步骤进度", asyn
|
|||||||
await page.goto(`/reimbursements/${reimbursementId}`);
|
await page.goto(`/reimbursements/${reimbursementId}`);
|
||||||
await reimbursementDetail.assertLoaded();
|
await reimbursementDetail.assertLoaded();
|
||||||
|
|
||||||
await (await openNavLink(page, "票据中心")).click();
|
|
||||||
const marker = `MOCK_E2E_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
const marker = `MOCK_E2E_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
||||||
const uploadResponsePromise = page.waitForResponse((response) => {
|
await reimbursementDetail.uploadInvoicePdf(
|
||||||
const url = new URL(response.url());
|
`progress-${Date.now()}.pdf`,
|
||||||
return response.request().method() === "POST" && url.pathname === "/api/invoice-pool/upload" && response.ok();
|
Buffer.from(`%PDF-1.4\n${marker}\n%%EOF\n`, "utf8"),
|
||||||
});
|
);
|
||||||
await page.getByTestId("invoice-upload-file").setInputFiles({
|
|
||||||
name: `progress-${Date.now()}.pdf`,
|
|
||||||
mimeType: "application/pdf",
|
|
||||||
buffer: Buffer.from(`%PDF-1.4\n${marker}\n%%EOF\n`, "utf8"),
|
|
||||||
});
|
|
||||||
await page.getByTestId("invoice-upload-submit").click();
|
|
||||||
const uploadPayload = (await (await uploadResponsePromise).json()) as { data?: { invoicePoolId?: string | null } };
|
|
||||||
const invoiceId = uploadPayload.data?.invoicePoolId;
|
|
||||||
if (!invoiceId) {
|
|
||||||
throw new Error(`上传发票后未返回票据 ID: ${JSON.stringify(uploadPayload)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await expect(page.getByTestId("invoice-detail-panel")).toBeVisible();
|
|
||||||
await expect(page.getByTestId("invoice-detail-panel")).toContainText("状态:可用", { timeout: 90_000 });
|
|
||||||
|
|
||||||
await page.getByTestId("invoice-bind-invoice").selectOption(invoiceId);
|
|
||||||
await page.getByTestId("invoice-bind-reimbursement").selectOption(reimbursementId);
|
|
||||||
await page.getByTestId("invoice-bind-submit").click();
|
|
||||||
|
|
||||||
await page.goto(`/reimbursements/${reimbursementId}`);
|
await page.goto(`/reimbursements/${reimbursementId}`);
|
||||||
await reimbursementDetail.assertLoaded();
|
await reimbursementDetail.assertLoaded();
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
| 基线 | 通过 | 本地基线 `cee04d5`;R6 未启动;`RS6-7` 与 `UI6-6` 继续保持 `DONE/DONE/TODO`;`R6-ISSUE-001` 继续 `open/PAUSED` |
|
| 基线 | 通过 | 本地基线 `cee04d5`;R6 未启动;`RS6-7` 与 `UI6-6` 继续保持 `DONE/DONE/TODO`;`R6-ISSUE-001` 继续 `open/PAUSED` |
|
||||||
| 公网健康与 live smoke | 通过 | `check-live-smoke-env --strict` 通过;`run-live-smoke-readiness.sh` 通过;报告 `artifacts/qa-flow/live-smoke-readiness-20260511-135958`;公网 bundle `findings=0`;live OCR 返回重复票据的可解释终态 |
|
| 公网健康与 live smoke | 通过 | `check-live-smoke-env --strict` 通过;`run-live-smoke-readiness.sh` 通过;报告 `artifacts/qa-flow/live-smoke-readiness-20260511-135958`;公网 bundle `findings=0`;live OCR 返回重复票据的可解释终态 |
|
||||||
| 企业微信个人应用消息 | 通过 | 只读查询确认 `R5-WECOM-APP-PERSONAL` 为 `enabled=true`、`mode=wecom_app_message`;旧 `R50-MOCK-007334` 为 `enabled=false`;mock 不作为 R6 通知验收依据 |
|
| 企业微信个人应用消息 | 通过 | 只读查询确认 `R5-WECOM-APP-PERSONAL` 为 `enabled=true`、`mode=wecom_app_message`;旧 `R50-MOCK-007334` 为 `enabled=false`;mock 不作为 R6 通知验收依据 |
|
||||||
| 业务自动化 | 部分通过 | `approval/leave/procurement` 本地隔离业务流通过,报告 `artifacts/qa-flow/business-flow-20260511-140942`;公网 live smoke 通过;`reimbursement` 组有 2 条旧导航入口失败,登记为 `R6-ISSUE-002` |
|
| 业务自动化 | 通过 | `approval/leave/procurement` 本地隔离业务流通过,报告 `artifacts/qa-flow/business-flow-20260511-140942`;公网 live smoke 通过;`reimbursement` 组已在 `R6-ISSUE-002` 对齐测试口径后通过,报告 `artifacts/qa-flow/business-flow-20260511-161344` |
|
||||||
| 财务链路 | 通过 / 候选待确认 | `server/scripts/regression-payment-batches.sh` 通过,`paymentBatches` 单测 3 passed;只读查询确认 `SMOKE301/SMOKE302` 为 active 测试财务账号;未命中 `wangzhiling` OA 候选账号,R6-3 前需补确认 |
|
| 财务链路 | 通过 / 候选待确认 | `server/scripts/regression-payment-batches.sh` 通过,`paymentBatches` 单测 3 passed;只读查询确认 `SMOKE301/SMOKE302` 为 active 测试财务账号;未命中 `wangzhiling` OA 候选账号,R6-3 前需补确认 |
|
||||||
| PC 与移动模拟视觉 | 通过 | R105 公网视觉 4 passed;移动响应式矩阵 9 passed;截图汇总到 `output/playwright/r6-non-device-business-verification/` |
|
| PC 与移动模拟视觉 | 通过 | R105 公网视觉 4 passed;移动响应式矩阵 9 passed;截图汇总到 `output/playwright/r6-non-device-business-verification/` |
|
||||||
| 真机门禁 | 暂停 | iPhone 11 / Redmi K60 企业微信真实触控、入口和布局体验仍需人工确认;自动视觉不替代真机验收 |
|
| 真机门禁 | 暂停 | iPhone 11 / Redmi K60 企业微信真实触控、入口和布局体验仍需人工确认;自动视觉不替代真机验收 |
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
| ID | 级别 | 角色 | 入口/设备 | 流程 | 复现步骤 | 当前处理 | 负责人 | 状态 | 关联 Issue |
|
| ID | 级别 | 角色 | 入口/设备 | 流程 | 复现步骤 | 当前处理 | 负责人 | 状态 | 关联 Issue |
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
| R6-ISSUE-001 | 阻断 | 审批人 / 员工 | iPhone 11 / 企业微信 WebView | 移动底部导航、审批入口、首页快捷卡 | iPhone 11 使用企业微信打开 OA,底部菜单第二项“审批”点不了,首页快捷卡图标和文字挤在一起;用户反馈单点适配不足,需要像主流移动网页/App 一样自动适配不同分辨率 | `PAUSED`:已从单机型热修升级为移动响应式体系修复并部署;首页快捷入口改为图标宫格,公网 320/360/375/390/393/414/430 视觉矩阵通过;用户暂时暂停真机验证,待 iPhone 11 / Redmi K60 真机确认后关闭或降级 | Codex / 待指派 | open | `issues/2026-05-11_12-17-07-r6-mobile-responsive-system-hotfix.csv` |
|
| R6-ISSUE-001 | 阻断 | 审批人 / 员工 | iPhone 11 / 企业微信 WebView | 移动底部导航、审批入口、首页快捷卡 | iPhone 11 使用企业微信打开 OA,底部菜单第二项“审批”点不了,首页快捷卡图标和文字挤在一起;用户反馈单点适配不足,需要像主流移动网页/App 一样自动适配不同分辨率 | `PAUSED`:已从单机型热修升级为移动响应式体系修复并部署;首页快捷入口改为图标宫格,公网 320/360/375/390/393/414/430 视觉矩阵通过;用户暂时暂停真机验证,待 iPhone 11 / Redmi K60 真机确认后关闭或降级 | Codex / 待指派 | open | `issues/2026-05-11_12-17-07-r6-mobile-responsive-system-hotfix.csv` |
|
||||||
| R6-ISSUE-002 | 一般 | 自动化 / 员工与财务测试账号 | 本地隔离 E2E / PC | 报销审批进度、报销支付批次 | `BUSINESS_FLOW_GROUPS=reimbursement bash scripts/qa/run-business-flow-gates.sh local` 中 2 条用例失败,失败点为 Playwright 查找旧导航入口 `票据中心`;当前页面侧边栏显示 `我的报销`,未找到 `票据中心` | 先登记为测试/导航口径不一致问题;不阻断本轮非真机验证结论,后续单独确认是恢复入口还是更新 E2E 入口选择器 | Codex / 待指派 | open | `issues/2026-05-11_13-58-30-r6-non-device-business-verification.csv` |
|
| R6-ISSUE-002 | 一般 | 自动化 / 员工与财务测试账号 | 本地隔离 E2E / PC | 报销审批进度、报销支付批次 | `BUSINESS_FLOW_GROUPS=reimbursement bash scripts/qa/run-business-flow-gates.sh local` 中 2 条用例曾失败,失败点为 Playwright 查找旧导航入口 `票据中心`;当前正式员工路径应从报销详情添加 PDF 发票 | 已关闭:测试已改为报销详情页“添加发票”路径,不恢复普通员工“票据中心”入口;支付批次用例同步按业务编号定位待办,并按金额选择财务或财务主管二级审批;报销业务流 gate 10 passed、1 skipped | Codex | closed | `issues/2026-05-11_15-50-00-r6-reimbursement-e2e-nav-alignment.csv` |
|
||||||
|
|
||||||
级别定义:
|
级别定义:
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
ID,Title,Description,Acceptance,Test_Method,Tools,Dev_Status,Review1_Status,Regression_Status,Files,Dependencies,Notes
|
ID,Title,Description,Acceptance,Test_Method,Tools,Dev_Status,Review1_Status,Regression_Status,Files,Dependencies,Notes
|
||||||
BV6-1,固定非真机自动验证基线,记录当前基线 cee04d5、工作区干净、R6 未启动、RS6/UI6 真机门禁仍 PAUSED;本轮不改业务代码、不部署、不改远端数据。,"1) 本 CSV 存在且校验通过;2) git status 干净;3) RS6-7 和 UI6-6 保持 DONE/DONE/TODO;4) R6-ISSUE-001 保持 open/PAUSED;5) 不把 R6 标为 DOING。","流程级验证:git status --short;git rev-parse --short HEAD;人工核对 issues/2026-05-11_12-17-07-r6-mobile-responsive-system-hotfix.csv、issues/2026-05-11_10-46-10-r6-iphone11-wecom-mobile-layout-hotfix.csv 和 docs/trialrun/manual-trialrun-report.md。",git|manual,DONE,DONE,DONE,issues/2026-05-11_13-58-30-r6-non-device-business-verification.csv,[],已确认基线 cee04d5;本轮仅新增 BV6 合同和报告;RS6-7/UI6-6 仍为 DONE/DONE/TODO;R6-ISSUE-001 仍为 open/PAUSED;R6 未置 DOING。
|
BV6-1,固定非真机自动验证基线,记录当前基线 cee04d5、工作区干净、R6 未启动、RS6/UI6 真机门禁仍 PAUSED;本轮不改业务代码、不部署、不改远端数据。,"1) 本 CSV 存在且校验通过;2) git status 干净;3) RS6-7 和 UI6-6 保持 DONE/DONE/TODO;4) R6-ISSUE-001 保持 open/PAUSED;5) 不把 R6 标为 DOING。","流程级验证:git status --short;git rev-parse --short HEAD;人工核对 issues/2026-05-11_12-17-07-r6-mobile-responsive-system-hotfix.csv、issues/2026-05-11_10-46-10-r6-iphone11-wecom-mobile-layout-hotfix.csv 和 docs/trialrun/manual-trialrun-report.md。",git|manual,DONE,DONE,DONE,issues/2026-05-11_13-58-30-r6-non-device-business-verification.csv,[],已确认基线 cee04d5;本轮仅新增 BV6 合同和报告;RS6-7/UI6-6 仍为 DONE/DONE/TODO;R6-ISSUE-001 仍为 open/PAUSED;R6 未置 DOING。
|
||||||
BV6-2,公网健康与 live smoke 环境检查,只读检查公网 ready、live smoke 环境、通知渠道和 R5 关键公网门禁;不修改远端配置。,"1) /ready 正常;2) check-live-smoke-env --strict 通过或缺项明确记录;3) run-live-smoke-readiness.sh 通过或失败项明确归类;4) 企业微信个人应用消息仍是正式通知证据,mock 不作为 R6 通知验收依据。","流程级验证:node scripts/qa/check-live-smoke-env.mjs --strict;LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh;检查最新 artifacts/qa-flow/live-smoke-readiness-*。",node|bash|manual,DONE,DONE,DONE,scripts/qa/*|artifacts/qa-flow,"[""BV6-1""]",check-live-smoke-env --strict 通过;live smoke readiness PASS 报告 artifacts/qa-flow/live-smoke-readiness-20260511-135958;公网 bundle findings=0;个人应用消息渠道 enabled 且旧 mock disabled;未打印 secret。
|
BV6-2,公网健康与 live smoke 环境检查,只读检查公网 ready、live smoke 环境、通知渠道和 R5 关键公网门禁;不修改远端配置。,"1) /ready 正常;2) check-live-smoke-env --strict 通过或缺项明确记录;3) run-live-smoke-readiness.sh 通过或失败项明确归类;4) 企业微信个人应用消息仍是正式通知证据,mock 不作为 R6 通知验收依据。","流程级验证:node scripts/qa/check-live-smoke-env.mjs --strict;LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh;检查最新 artifacts/qa-flow/live-smoke-readiness-*。",node|bash|manual,DONE,DONE,DONE,scripts/qa/*|artifacts/qa-flow,"[""BV6-1""]",check-live-smoke-env --strict 通过;live smoke readiness PASS 报告 artifacts/qa-flow/live-smoke-readiness-20260511-135958;公网 bundle findings=0;个人应用消息渠道 enabled 且旧 mock disabled;未打印 secret。
|
||||||
BV6-3,PC 与自动化业务链路验证,用测试账号和既有自动化覆盖报销、请假、采购、审批链、自审阻断和 admin-only 禁止审批。,"1) 普通员工提交低/中额单据进入 SMOKE101;2) 高额/例外链路覆盖负责人或财务节点;3) SMOKE201 本人自审被阻断或无兜底时可解释阻断;4) SMOKE001 仅 admin 不能审批业务待办;5) 不使用真实员工日常账号。","流程级验证:优先复用 LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh 证据;必要时补 PC Playwright 或现有 procurement/leave/reimbursement E2E 只读页面巡检,不新增功能。",bash|playwright|manual,DONE,DONE,TODO,scripts/qa/*|client/tests/e2e/*,"[""BV6-2""]",已执行公网 live smoke PASS;本地隔离 approval/leave/procurement PASS;reimbursement 组 8 passed、1 skipped、2 failed,失败点为旧导航入口 票据中心;已登记 R6-ISSUE-002,后续确认恢复入口或更新 E2E 选择器。
|
BV6-3,PC 与自动化业务链路验证,用测试账号和既有自动化覆盖报销、请假、采购、审批链、自审阻断和 admin-only 禁止审批。,"1) 普通员工提交低/中额单据进入 SMOKE101;2) 高额/例外链路覆盖负责人或财务节点;3) SMOKE201 本人自审被阻断或无兜底时可解释阻断;4) SMOKE001 仅 admin 不能审批业务待办;5) 不使用真实员工日常账号。","流程级验证:优先复用 LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh 证据;必要时补 PC Playwright 或现有 procurement/leave/reimbursement E2E 只读页面巡检,不新增功能。",bash|playwright|manual,DONE,DONE,DONE,scripts/qa/*|client/tests/e2e/*,"[""BV6-2""]",已执行公网 live smoke PASS;本地隔离 approval/leave/procurement PASS;reimbursement 组已在 R6-ISSUE-002 对齐测试口径后 PASS,10 passed、1 skipped,报告 artifacts/qa-flow/business-flow-20260511-161344/summary.txt。
|
||||||
BV6-4,财务链路与 wangzhiling 只读候选确认,财务自动化优先使用 SMOKE301/SMOKE302 等测试财务账号;wangzhiling 只做候选账号只读确认,不猜密码、不自动登录。,"1) 测试财务账号可覆盖财务复核、付款标记或导出证据;2) 若测试财务凭据缺失则记录 BLOCKED;3) wangzhiling 只确认 active、角色和权限摘要;4) 不使用真实财务密码。","流程级验证:检查 live smoke 或既有财务自动化报告;必要时只读查询 wangzhiling 账号 active、角色和部门摘要;不打印 secret。",ssh|manual|playwright,DONE,DONE,DONE,external|artifacts/qa-flow|docs/trialrun/manual-trialrun-report.md,"[""BV6-2""]",server/scripts/regression-payment-batches.sh 通过,paymentBatches 单测 3 passed;远端只读查询确认 SMOKE301/SMOKE302 active 且具备测试财务角色;未命中 wangzhiling OA 候选账号,本轮不猜密码、不自动登录真实财务。
|
BV6-4,财务链路与 wangzhiling 只读候选确认,财务自动化优先使用 SMOKE301/SMOKE302 等测试财务账号;wangzhiling 只做候选账号只读确认,不猜密码、不自动登录。,"1) 测试财务账号可覆盖财务复核、付款标记或导出证据;2) 若测试财务凭据缺失则记录 BLOCKED;3) wangzhiling 只确认 active、角色和权限摘要;4) 不使用真实财务密码。","流程级验证:检查 live smoke 或既有财务自动化报告;必要时只读查询 wangzhiling 账号 active、角色和部门摘要;不打印 secret。",ssh|manual|playwright,DONE,DONE,DONE,external|artifacts/qa-flow|docs/trialrun/manual-trialrun-report.md,"[""BV6-2""]",server/scripts/regression-payment-batches.sh 通过,paymentBatches 单测 3 passed;远端只读查询确认 SMOKE301/SMOKE302 active 且具备测试财务角色;未命中 wangzhiling OA 候选账号,本轮不猜密码、不自动登录真实财务。
|
||||||
BV6-5,PC 移动与企业微信 UA 视觉巡检,在不依赖真实设备的情况下跑 PC 1440x960、移动模拟视口和企业微信 UA 视觉巡检,输出截图和 DOM 指标。,"1) 截图输出到 output/playwright/r6-non-device-business-verification;2) PC 页面无明显错版;3) 移动模拟视口无横向滚动、无 PC 壳、底部导航命中;4) 首页图标宫格不重叠;5) 报销详情吸底操作不遮挡。","流程级验证:运行现有移动视觉专项和必要 PC/页面巡检;覆盖 /mobile、/mobile/approvals、/mobile/reimbursements、/mobile/notifications、/workflow/tasks、报销/请假/采购/财务相关入口。",playwright|manual,DONE,DONE,DONE,client/tests/e2e/*|output/playwright/r6-non-device-business-verification,"[""BV6-2""]",R105 公网视觉 4 passed;移动响应式矩阵在公网 baseURL 与 mock API 口径下 9 passed;首次本地无服务调用失败为环境调用问题;截图已汇总到 output/playwright/r6-non-device-business-verification;真机仍不被替代。
|
BV6-5,PC 移动与企业微信 UA 视觉巡检,在不依赖真实设备的情况下跑 PC 1440x960、移动模拟视口和企业微信 UA 视觉巡检,输出截图和 DOM 指标。,"1) 截图输出到 output/playwright/r6-non-device-business-verification;2) PC 页面无明显错版;3) 移动模拟视口无横向滚动、无 PC 壳、底部导航命中;4) 首页图标宫格不重叠;5) 报销详情吸底操作不遮挡。","流程级验证:运行现有移动视觉专项和必要 PC/页面巡检;覆盖 /mobile、/mobile/approvals、/mobile/reimbursements、/mobile/notifications、/workflow/tasks、报销/请假/采购/财务相关入口。",playwright|manual,DONE,DONE,DONE,client/tests/e2e/*|output/playwright/r6-non-device-business-verification,"[""BV6-2""]",R105 公网视觉 4 passed;移动响应式矩阵在公网 baseURL 与 mock API 口径下 9 passed;首次本地无服务调用失败为环境调用问题;截图已汇总到 output/playwright/r6-non-device-business-verification;真机仍不被替代。
|
||||||
BV6-6,汇总结论与状态回填,汇总非真机自动验证结论,将问题登记到 R6 报告或新 Issue,保持真机门禁 PAUSED。,"1) BV6-1 到 BV6-5 状态按实际结果回填;2) docs/trialrun/manual-trialrun-report.md 记录非真机自动验证结论;3) 阻断、严重、一般、体验建议分类清楚;4) R6、RS6-7、UI6-6 和 R6-ISSUE-001 不因自动验证被误关闭;5) CSV、diff 校验通过并提交。","流程级验证:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-11_13-58-30-r6-non-device-business-verification.csv;git diff --check;人工核对 R6 报告和相关 Issue 状态。",python3|git|manual,DONE,DONE,DONE,issues/2026-05-11_13-58-30-r6-non-device-business-verification.csv|docs/trialrun/manual-trialrun-report.md,"[""BV6-1"",""BV6-2"",""BV6-3"",""BV6-4"",""BV6-5""]",已更新 R6 报告并登记 R6-ISSUE-002;BV6-3 因报销旧导航 E2E 仍保留 Regression TODO;RS6-7/UI6-6/R6-ISSUE-001 保持 PAUSED/open;本轮不表示 R6 已启动。
|
BV6-6,汇总结论与状态回填,汇总非真机自动验证结论,将问题登记到 R6 报告或新 Issue,保持真机门禁 PAUSED。,"1) BV6-1 到 BV6-5 状态按实际结果回填;2) docs/trialrun/manual-trialrun-report.md 记录非真机自动验证结论;3) 阻断、严重、一般、体验建议分类清楚;4) R6、RS6-7、UI6-6 和 R6-ISSUE-001 不因自动验证被误关闭;5) CSV、diff 校验通过并提交。","流程级验证:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-11_13-58-30-r6-non-device-business-verification.csv;git diff --check;人工核对 R6 报告和相关 Issue 状态。",python3|git|manual,DONE,DONE,DONE,issues/2026-05-11_13-58-30-r6-non-device-business-verification.csv|docs/trialrun/manual-trialrun-report.md,"[""BV6-1"",""BV6-2"",""BV6-3"",""BV6-4"",""BV6-5""]",R6-ISSUE-002 已通过 issues/2026-05-11_15-50-00-r6-reimbursement-e2e-nav-alignment.csv 关闭;RS6-7/UI6-6/R6-ISSUE-001 保持 PAUSED/open;本轮不表示 R6 已启动。
|
||||||
|
|||||||
|
@@ -0,0 +1,6 @@
|
|||||||
|
ID,Title,Description,Acceptance,Test_Method,Tools,Dev_Status,Review1_Status,Regression_Status,Files,Dependencies,Notes
|
||||||
|
RN6-1,建档 R6-ISSUE-002 报销 E2E 导航口径修复,固定本轮只处理报销 E2E 旧导航入口票据中心与当前产品口径不一致问题;不恢复普通员工票据中心入口,不启动 R6,不关闭真机门禁。,"1) 本 CSV 存在且校验通过;2) 工作区基线为 c61151d 或其后续提交;3) R6-ISSUE-001 仍保持 open/PAUSED;4) R6 不置为 DOING;5) 本轮不部署、不改远端数据。","流程级验证:git status --short;git rev-parse --short HEAD;python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-11_15-50-00-r6-reimbursement-e2e-nav-alignment.csv;人工核对 docs/trialrun/manual-trialrun-report.md。",git|python3|manual,DONE,DONE,DONE,issues/2026-05-11_15-50-00-r6-reimbursement-e2e-nav-alignment.csv,[],已建档并校验通过;基线 c61151d;本轮不部署、不改远端数据;R6-ISSUE-001 保持 open/PAUSED。
|
||||||
|
RN6-2,确认报销详情添加发票测试入口,只读确认报销详情页现有添加发票、PDF 上传、绑定或自动添加发票的稳定控件;若缺少稳定定位,只补最小 data-testid。,"1) 员工侧测试路径不再依赖侧边栏票据中心;2) 报销详情页存在可测试的添加 PDF 发票路径;3) 不改变用户可见 UI;4) 不修改权限模型。","流程级验证:rg 搜索报销详情和发票上传控件;必要时运行相关单测或 Playwright 单用例确认定位可用。",rg|playwright,DONE,DONE,DONE,client/src/pages/ReimbursementDetailPage.tsx|client/tests/e2e/*,"[""RN6-1""]",确认已有 reimbursement-invoice-add、reimbursement-invoice-upload-file、reimbursement-invoice-upload-submit;仅新增测试 Page Object 封装,无用户可见 UI 改动。
|
||||||
|
RN6-3,重构报销 E2E 使用正式员工发票路径,更新 reimbursement-workflow-progress 和 reimbursement-payment-batch-flow 两个 E2E,从报销详情页添加 PDF 发票,不再点击普通员工不可见的票据中心。,"1) 两个原失败用例不再调用 openNavLink(page, ""票据中心"");2) 员工创建报销单后在详情页添加 PDF 发票;3) 财务和财务主管后续支付批次链路保持不变;4) 保留财务/管理角色访问 invoice-pool 的其他测试。","流程级验证:cd client && npm run test:e2e -- reimbursement-workflow-progress.spec.ts reimbursement-payment-batch-flow.spec.ts --workers=1 --reporter=list;失败时记录具体页面或接口原因。",playwright|npm,DONE,DONE,DONE,client/tests/e2e/reimbursement-workflow-progress.spec.ts|client/tests/e2e/reimbursement-payment-batch-flow.spec.ts,"[""RN6-2""]",两个目标 E2E 已改为报销详情页上传 PDF 发票;支付批次用例同步按业务编号定位待办,并按 mock OCR 金额选择 finance 或 finance_mgr 二级审批人。
|
||||||
|
RN6-4,本地报销业务回归与前端校验,跑报销业务流 gate 和前端类型构建,确认 R6-ISSUE-002 已关闭且没有前端类型或构建回归。,"1) BUSINESS_FLOW_GROUPS=reimbursement bash scripts/qa/run-business-flow-gates.sh local 通过;2) cd client && npm run typecheck 通过;3) cd client && npm run build 通过;4) git diff --check 通过。","流程级验证:BUSINESS_FLOW_GROUPS=reimbursement bash scripts/qa/run-business-flow-gates.sh local;cd client && npm run typecheck;cd client && npm run build;git diff --check。",bash|npm|git,DONE,DONE,DONE,scripts/qa/*|client/tests/e2e/*,"[""RN6-3""]",报销业务流 gate PASS,10 passed、1 skipped,报告 artifacts/qa-flow/business-flow-20260511-161344/summary.txt;npm run typecheck 和 npm run build 通过;build 仅保留既有 chunk 警告。
|
||||||
|
RN6-5,回填 R6 报告和提交,更新 R6 报告中 R6-ISSUE-002 状态和本 CSV 状态,说明真机门禁仍暂停;校验后提交中文 commit。,"1) docs/trialrun/manual-trialrun-report.md 中 R6-ISSUE-002 已关闭或明确降级;2) 本 CSV 状态按实际验证回填;3) BV6 备注保持一致;4) R6-ISSUE-001 仍 open/PAUSED;5) 提交信息引用 R6-ISSUE-002。","流程级验证:python3 /home/shunli/.codex/skills/plan/scripts/validate_issues_csv.py issues/2026-05-11_15-50-00-r6-reimbursement-e2e-nav-alignment.csv;git diff --check;git status --short;人工核对报告。",python3|git|manual,DONE,DONE,DONE,docs/trialrun/manual-trialrun-report.md|issues/2026-05-11_15-50-00-r6-reimbursement-e2e-nav-alignment.csv,"[""RN6-4""]",已回填 R6 报告和 BV6 备注;R6-ISSUE-002 关闭;R6-ISSUE-001 仍 open/PAUSED,R6 未启动。
|
||||||
|
在新工单中引用
屏蔽一个用户