Commit 6a181bea authored by liguangyu06's avatar liguangyu06
Browse files

将基础管理等未入库改动暂存到 temp 分支,便于远程备份。

新模块用例尚未具备通关凭证,本次按临时备份跳过本地门禁。
parent f6eec849
...@@ -6,6 +6,7 @@ import { FLAKY_TAG, resolveFlakyRetries } from '../core/flaky'; ...@@ -6,6 +6,7 @@ import { FLAKY_TAG, resolveFlakyRetries } from '../core/flaky';
import { annotateFailureClass, classifyFromTestInfo } from '../core/failure-classify'; import { annotateFailureClass, classifyFromTestInfo } from '../core/failure-classify';
import { applyQuarantine } from '../core/quarantine'; import { applyQuarantine } from '../core/quarantine';
import { attachFailureDiagnostics } from '../core/diagnostics'; import { attachFailureDiagnostics } from '../core/diagnostics';
import { annotateFailureFrequencyForTest } from '../core/failure-tracker';
import { import {
attachRuntimeHealthGuard, attachRuntimeHealthGuard,
getRuntimeHealthGuard, getRuntimeHealthGuard,
...@@ -15,6 +16,10 @@ import { buildPageFixtures, type AppPageFixtures } from './page-registry'; ...@@ -15,6 +16,10 @@ import { buildPageFixtures, type AppPageFixtures } from './page-registry';
type AppFixtures = AppPageFixtures; type AppFixtures = AppPageFixtures;
/**
* 业务 fixture:依赖 chromium.project 的 storageState(auth/user.json)。
* 每条用例由 Playwright 新建独立 context + page,再注入登录态;此处不持有跨用例 session。
*/
export const test = base.extend<AppFixtures>({ export const test = base.extend<AppFixtures>({
page: async ({ page }, use) => { page: async ({ page }, use) => {
attachRuntimeHealthGuard(page); attachRuntimeHealthGuard(page);
...@@ -33,17 +38,27 @@ test.beforeEach(({}, testInfo) => { ...@@ -33,17 +38,27 @@ test.beforeEach(({}, testInfo) => {
test.afterEach(async ({ page }, testInfo) => { test.afterEach(async ({ page }, testInfo) => {
const failed = testInfo.status !== testInfo.expectedStatus; const failed = testInfo.status !== testInfo.expectedStatus;
if (failed) { try {
if (page && !page.isClosed()) { if (failed) {
await attachFailureDiagnostics(page, testInfo, getRuntimeHealthGuard(page)); if (page && !page.isClosed()) {
await attachFailureDiagnostics(page, testInfo, getRuntimeHealthGuard(page));
} else {
annotateFailureClass(testInfo, classifyFromTestInfo(testInfo));
}
return; return;
} }
annotateFailureClass(testInfo, classifyFromTestInfo(testInfo)); const guard = page && !page.isClosed() ? getRuntimeHealthGuard(page) : undefined;
return; if (guard) {
} await guard.assertClean(`用例收尾:${testInfo.title}`);
const guard = page && !page.isClosed() ? getRuntimeHealthGuard(page) : undefined; }
if (guard) { } finally {
await guard.assertClean(`用例收尾:${testInfo.title}`); // 跨构建失败频次:reporter 负责累计落盘,这里基于最新统计打 annotation(只读),
// 保证诊断/健康检查异常也不影响打标;无编号用例自动跳过。
try {
annotateFailureFrequencyForTest(testInfo);
} catch {
// 统计不影响用例结果
}
} }
}); });
......
...@@ -3,7 +3,37 @@ import type { Page } from '@playwright/test'; ...@@ -3,7 +3,37 @@ import type { Page } from '@playwright/test';
import { LoginPage } from '../pages/auth/login.page'; import { LoginPage } from '../pages/auth/login.page';
import { AnaChgPage } from '../pages/spd/ana-chg.page'; import { AnaChgPage } from '../pages/spd/ana-chg.page';
import { AnaDeptPage } from '../pages/spd/ana-dept.page'; import { AnaDeptPage } from '../pages/spd/ana-dept.page';
import { AdmitMaterialListPage } from '../pages/spd/admit-material-list.page';
import { AdmitMaterialPage } from '../pages/spd/admit-material.page';
import { AdmitProductApplyPage } from '../pages/spd/admit-product-apply.page';
import { AdmitProductListPage } from '../pages/spd/admit-product-list.page';
import { AdmitSupplyPage } from '../pages/spd/admit-supply.page';
import { AdmitTaskPage } from '../pages/spd/admit-task.page';
import { AnaProvPage } from '../pages/spd/ana-prov.page'; import { AnaProvPage } from '../pages/spd/ana-prov.page';
import { BasicBranchPage } from '../pages/spd/basic-branch.page';
import { BasicFinanceMonthPage } from '../pages/spd/basic-finance-month.page';
import { BasicHospitalInfoPage } from '../pages/spd/basic-hospital-info.page';
import { BasicNoticePage } from '../pages/spd/basic-notice.page';
import { BasicRolePage } from '../pages/spd/basic-role.page';
import { BasicUserPage } from '../pages/spd/basic-user.page';
import { BasicUserUnionPage } from '../pages/spd/basic-user-union.page';
import { FlowDefPage } from '../pages/spd/flow-def.page';
import { FlowInstPage } from '../pages/spd/flow-inst.page';
import { WarehouseProbePage } from '../pages/spd/warehouse-probe.page';
import { WarehouseZonePage } from '../pages/spd/warehouse-zone.page';
import { SurgeryBasePkgPage } from '../pages/spd/surgery-base-pkg.page';
import { SurgeryOperTypePage } from '../pages/spd/surgery-oper-type.page';
import { SurgeryStdPkgPage } from '../pages/spd/surgery-std-pkg.page';
import { VendorAuthFinalPage } from '../pages/spd/vendor-auth-final.page';
import { VendorAuthFirstPage } from '../pages/spd/vendor-auth-first.page';
import { VendorCertDiffPage } from '../pages/spd/vendor-cert-diff.page';
import { VendorCertFinalPage } from '../pages/spd/vendor-cert-final.page';
import { VendorCertFirstPage } from '../pages/spd/vendor-cert-first.page';
import { VendorEvalIndicatorPage } from '../pages/spd/vendor-eval-indicator.page';
import { VendorEvalSchemePage } from '../pages/spd/vendor-eval-scheme.page';
import { VendorHolidayPage } from '../pages/spd/vendor-holiday.page';
import { VendorQualPage } from '../pages/spd/vendor-qual.page';
import { VendorQueryPage } from '../pages/spd/vendor-query.page';
import { CenterReportEntryPage } from '../pages/spd/center-report-entry.page'; import { CenterReportEntryPage } from '../pages/spd/center-report-entry.page';
import { CenterReportLmmPsiPage } from '../pages/spd/center-report-lmm-psi.page'; import { CenterReportLmmPsiPage } from '../pages/spd/center-report-lmm-psi.page';
import { CenterReportLmmSheetPage } from '../pages/spd/center-report-lmm-sheet.page'; import { CenterReportLmmSheetPage } from '../pages/spd/center-report-lmm-sheet.page';
...@@ -120,6 +150,36 @@ export const PAGE_CTORS = { ...@@ -120,6 +150,36 @@ export const PAGE_CTORS = {
anaChgPage: AnaChgPage, anaChgPage: AnaChgPage,
anaDeptPage: AnaDeptPage, anaDeptPage: AnaDeptPage,
anaProvPage: AnaProvPage, anaProvPage: AnaProvPage,
admitMaterialPage: AdmitMaterialPage,
admitProductApplyPage: AdmitProductApplyPage,
admitMaterialListPage: AdmitMaterialListPage,
admitProductListPage: AdmitProductListPage,
admitSupplyPage: AdmitSupplyPage,
admitTaskPage: AdmitTaskPage,
basicNoticePage: BasicNoticePage,
basicHospitalInfoPage: BasicHospitalInfoPage,
basicFinanceMonthPage: BasicFinanceMonthPage,
basicBranchPage: BasicBranchPage,
basicUserPage: BasicUserPage,
basicUserUnionPage: BasicUserUnionPage,
basicRolePage: BasicRolePage,
warehouseZonePage: WarehouseZonePage,
warehouseProbePage: WarehouseProbePage,
flowDefPage: FlowDefPage,
flowInstPage: FlowInstPage,
surgeryBasePkgPage: SurgeryBasePkgPage,
surgeryOperTypePage: SurgeryOperTypePage,
surgeryStdPkgPage: SurgeryStdPkgPage,
vendorQueryPage: VendorQueryPage,
vendorQualPage: VendorQualPage,
vendorAuthFirstPage: VendorAuthFirstPage,
vendorAuthFinalPage: VendorAuthFinalPage,
vendorCertFirstPage: VendorCertFirstPage,
vendorCertFinalPage: VendorCertFinalPage,
vendorCertDiffPage: VendorCertDiffPage,
vendorEvalSchemePage: VendorEvalSchemePage,
vendorEvalIndicatorPage: VendorEvalIndicatorPage,
vendorHolidayPage: VendorHolidayPage,
reagEntryPage: ReagEntryPage, reagEntryPage: ReagEntryPage,
reconDeptAccountPage: ReconDeptAccountPage, reconDeptAccountPage: ReconDeptAccountPage,
reconDeptNetInPage: ReconDeptNetInPage, reconDeptNetInPage: ReconDeptNetInPage,
......
# Auto-generated from admit-material-list-elements.json
# Source: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productOrder/zrlist
# Scanned: 2026-10-20T08:46:04.224Z
# Page: 基础设置 > 准入管理 > 物料准入单列表
# Note: filter dropdown was opened during scan
# Note: detail view was opened during scan
page: admit-material-list
module: spd-basic
url: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productOrder/zrlist
locators:
# --- main::main ---
menu_home:
description: "首页"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "首页"
playwright: "page.getByRole('menuitem', { name: '首页' })"
menu_basicSetting:
description: "基础设置"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "基础设置"
playwright: "page.getByRole('menuitem', { name: '基础设置' })"
menu_purchase:
description: "采购管理"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "采购管理"
playwright: "page.getByRole('menuitem', { name: '采购管理' })"
menu_opening:
description: "期初管理"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "期初管理"
playwright: "page.getByRole('menuitem', { name: '期初管理' })"
menu_centerStock:
description: "中心库管理"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "中心库管理"
playwright: "page.getByRole('menuitem', { name: '中心库管理' })"
menu_deptBusiness:
description: "科室业务管理"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "科室业务管理"
playwright: "page.getByRole('menuitem', { name: '科室业务管理' })"
menu_finance:
description: "财务管理"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "财务管理"
playwright: "page.getByRole('menuitem', { name: '财务管理' })"
menu_orthoVolume:
description: "骨科带量"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "骨科带量"
playwright: "page.getByRole('menuitem', { name: '骨科带量' })"
menu_general:
description: "综合管理"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "综合管理"
playwright: "page.getByRole('menuitem', { name: '综合管理' })"
menu_yangcai:
description: "阳采管理"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "阳采管理"
playwright: "page.getByRole('menuitem', { name: '阳采管理' })"
menu_processCenter:
description: "流程中心"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "流程中心"
playwright: "page.getByRole('menuitem', { name: '流程中心' })"
menu_logistics:
description: "后勤物资管理"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "后勤物资管理"
playwright: "page.getByRole('menuitem', { name: '后勤物资管理' })"
menu_statReport:
description: "统计报表"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "统计报表"
playwright: "page.getByRole('menuitem', { name: '统计报表' })"
menu_dataViz:
description: "数据可视化"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "数据可视化"
playwright: "page.getByRole('menuitem', { name: '数据可视化' })"
menu_admitMaterial:
description: "物料准入管理"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "物料准入管理"
playwright: "page.getByRole('menuitem', { name: '物料准入管理' })"
menu_admitProductApply:
description: "产品准入申请"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "产品准入申请"
playwright: "page.getByRole('menuitem', { name: '产品准入申请' })"
menu_admitMaterialList:
description: "物料准入单列表"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "物料准入单列表"
playwright: "page.getByRole('menuitem', { name: '物料准入单列表' })"
menu_admitProductList:
description: "产品准入单列表"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "产品准入单列表"
playwright: "page.getByRole('menuitem', { name: '产品准入单列表' })"
menu_admitSupply:
description: "供货目录申请"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "供货目录申请"
playwright: "page.getByRole('menuitem', { name: '供货目录申请' })"
menu_admitTask:
description: "准入任务分配"
element_type: li
section: "main::main"
strategy: role
role: menuitem
name: "准入任务分配"
playwright: "page.getByRole('menuitem', { name: '准入任务分配' })"
admitMaterialList:
description: "物料准入单列表"
element_type: tab
section: "main::main"
strategy: text
value: "物料准入单列表"
playwright: "page.getByText('物料准入单列表')"
tab_admitMaterialList:
description: "物料准入单列表"
element_type: div
section: "main::main"
strategy: id
value: "rc-tabs-0-tab-/basic/dataSync/dataSync/productOrder/zrlist"
playwright: "page.locator('#rc-tabs-0-tab-/basic/dataSync/dataSync/productOrder/zrlist')"
operationDateDateRange:
description: "制单日期"
element_type: datepicker
section: "main::main"
strategy: label_text
value: "制单日期"
playwright: "page.getByText('制单日期', { exact: true })"
dateRange:
description: "开始日期"
element_type: text
section: "main::main"
strategy: id
value: "table_search_form_dateValue"
playwright: "page.locator('#table_search_form_dateValue')"
operationDateInput:
description: "结束日期"
element_type: text
section: "main::main"
strategy: placeholder
value: "结束日期"
playwright: "page.getByPlaceholder('结束日期')"
supplierName:
description: "请输入"
element_type: text
section: "main::main"
strategy: id
value: "table_search_form_provName"
playwright: "page.locator('#table_search_form_provName')"
consumableTypeSelect:
description: "请选择耗材类型"
element_type: select
section: "main::main"
strategy: label_text
value: "耗材类型"
playwright: "page.getByText('耗材类型', { exact: true })"
searchBtn:
description: "查 询"
element_type: button
section: "main::main"
strategy: role
role: button
name: "查 询"
playwright: "page.getByRole('button', { name: '查 询' })"
resetBtn:
description: "重 置"
element_type: button
section: "main::main"
strategy: role
role: button
name: "重 置"
playwright: "page.getByRole('button', { name: '重 置' })"
expandFilterBtn:
description: "展开"
element_type: button
section: "main::main"
strategy: role
role: button
name: "展开"
playwright: "page.getByRole('button', { name: '展开' })"
detailBtn:
description: "详情"
element_type: button
section: "main::main"
strategy: role
role: button
name: "详情"
playwright: "page.getByRole('button', { name: '详情' })"
editBtn:
description: "编辑"
element_type: button
section: "main::main"
strategy: role
role: button
name: "编辑"
playwright: "page.getByRole('button', { name: '编辑' })"
consumableTypeSelect_2:
description: "请选择耗材类型低值耗材高值耗材检验试剂"
element_type: select
section: "main::main"
strategy: label_text
value: "耗材类型"
playwright: "page.getByText('耗材类型', { exact: true })"
purchaseMode:
description: "耗材类型"
element_type: search
section: "main::main"
strategy: id
value: "table_search_form_purModes"
playwright: "page.locator('#table_search_form_purModes')"
# --- main::pagination ---
pagination_action:
description: "共 24 条150 条/页"
element_type: ul
section: "main::pagination"
strategy: label_text
value: "操作"
playwright: "page.getByText('操作', { exact: true })"
pagination_prevPageBtn:
description: "上一页"
element_type: li
section: "main::pagination"
strategy: label_text
value: "操作"
playwright: "page.getByText('操作', { exact: true })"
pagination_actionBtn:
description: "操作"
element_type: button
section: "main::pagination"
strategy: label_text
value: "操作"
playwright: "page.getByText('操作', { exact: true })"
pagination_page_1:
description: "1"
element_type: pagination_item
section: "main::pagination"
strategy: css
value: ".ant-pagination-item-active"
playwright: "page.locator('.ant-pagination-item-active')"
pagination_nextPageBtn:
description: "下一页"
element_type: li
section: "main::pagination"
strategy: label_text
value: "操作"
playwright: "page.getByText('操作', { exact: true })"
pagination_pageSizeSelect:
description: "50 条/页"
element_type: select
section: "main::pagination"
strategy: label_text
value: "操作"
playwright: "page.getByText('操作', { exact: true })"
# --- main::detail:产品准入单详情 ---
el_e5aea1e689b9e8bf9be7a88bBtn:
description: "审批进程"
element_type: button
section: "main::detail:产品准入单详情"
strategy: role
role: button
name: "审批进程"
playwright: "page.getByRole('button', { name: '审批进程' })"
el_e58d95e68daee697a5e5bf97Btn:
description: "单据日志"
element_type: button
section: "main::detail:产品准入单详情"
strategy: role
role: button
name: "单据日志"
playwright: "page.getByRole('button', { name: '单据日志' })"
backBtn:
description: "返 回"
element_type: button
section: "main::detail:产品准入单详情"
strategy: role
role: button
name: "返 回"
playwright: "page.getByRole('button', { name: '返 回' })"
el_e8afb7e8be93e585a5e789a9e69699e7bc96e7a081e38081e5908de7a7b0Input:
description: "请输入物料编码、名称"
element_type: text
section: "main::detail:产品准入单详情"
strategy: placeholder
value: "请输入物料编码、名称"
playwright: "page.getByPlaceholder('请输入物料编码、名称')"
exportBtn:
description: "导 出"
element_type: button
section: "main::detail:产品准入单详情"
strategy: role
role: button
name: "导 出"
playwright: "page.getByRole('button', { name: '导 出' })"
tableDensity:
description: "column-height"
element_type: span
section: "main::detail:产品准入单详情"
strategy: label
value: "column-height"
playwright: "page.getByLabel('column-height')"
el_e9809ae8bf87Btn:
description: "通过"
element_type: button
section: "main::detail:产品准入单详情"
strategy: role
role: switch
name: "通过"
playwright: "page.getByRole('switch', { name: '通过' })"
el_e99984e4bbb6Btn:
description: "附件"
element_type: button
section: "main::detail:产品准入单详情"
strategy: role
role: button
name: "附件"
playwright: "page.getByRole('button', { name: '附件' })"
el_e585b131e69da1313530e69da12fe9a1b5:
description: "共 1 条150 条/页"
element_type: ul
section: "main::detail:产品准入单详情"
strategy: text
value: "共 1 条150 条/页"
playwright: "page.getByText('共 1 条150 条/页')"
prevPageBtn:
description: "上一页"
element_type: li
section: "main::detail:产品准入单详情"
strategy: text
value: "上一页"
playwright: "page.getByText('上一页')"
page_1:
description: "1"
element_type: pagination_item
section: "main::detail:产品准入单详情"
strategy: css
value: ".ant-pagination-item-active"
playwright: "page.locator('.ant-pagination-item-active')"
nextPageBtn:
description: "下一页"
element_type: li
section: "main::detail:产品准入单详情"
strategy: text
value: "下一页"
playwright: "page.getByText('下一页')"
pageSizeSelect:
description: "50 条/页"
element_type: select
section: "main::detail:产品准入单详情"
strategy: text
value: "50 条/页"
playwright: "page.getByText('50 条/页')"
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
import type { Locator, Page } from '@playwright/test';
/** 基础设置 · 准入管理。叶子菜单是 .ant-menu-item,不是 role=menuitem。 */
export class AdmitSharedLocators {
readonly menuBasic: Locator;
readonly admitSubmenuTitle: Locator;
readonly tableHeader: Locator;
readonly empty: Locator;
readonly paginationTotal: Locator;
readonly pageJumpInput: Locator;
readonly nextPage: Locator;
readonly prevPage: Locator;
readonly formError: Locator;
constructor(protected readonly page: Page) {
this.menuBasic = page.getByRole('menuitem', { name: '基础设置' });
this.admitSubmenuTitle = page.locator('.ant-menu-submenu-title').filter({ hasText: /^准入管理$/ });
this.tableHeader = page.locator('.ant-table-thead th');
this.empty = page.locator('.ant-empty, .ant-empty-description').first();
this.paginationTotal = page.locator('.ant-pagination-total-text').first();
this.pageJumpInput = page.locator('.ant-pagination-options-quick-jumper input').first();
this.nextPage = page.locator('.ant-pagination-next').first();
this.prevPage = page.locator('.ant-pagination-prev').first();
this.formError = page.locator('.ant-form-item-explain-error, .ant-message-notice, .ant-notification-notice');
}
menuLeaf(name: string): Locator {
return this.page.locator('.ant-menu-item').filter({ hasText: new RegExp(`^\\s*${name}\\s*$`) }).first();
}
button(name: string, root: Locator | Page = this.page): Locator {
const compact = name.replace(/\s/g, '');
const pattern = new RegExp(compact.split('').join('\\s*'));
return root.getByRole('button', { name: pattern });
}
tab(name: string): Locator {
return this.page.locator('.ant-tabs-tab').filter({ hasText: new RegExp(name) }).first();
}
putPage(): Locator {
return this.page.locator('.put-page').last();
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment