From 6a181bea839323dd24dbbdab28b19826f350dcf1 Mon Sep 17 00:00:00 2001 From: liguangyu06 <1666071293@qq.com> Date: Tue, 20 Oct 2026 17:40:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86=E5=9F=BA=E7=A1=80=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=AD=89=E6=9C=AA=E5=85=A5=E5=BA=93=E6=94=B9=E5=8A=A8=E6=9A=82?= =?UTF-8?q?=E5=AD=98=E5=88=B0=20temp=20=E5=88=86=E6=94=AF=EF=BC=8C?= =?UTF-8?q?=E4=BE=BF=E4=BA=8E=E8=BF=9C=E7=A8=8B=E5=A4=87=E4=BB=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新模块用例尚未具备通关凭证,本次按临时备份跳过本地门禁。 --- .env.example | 16 +- Jenkinsfile | 6 + README.md | 26 +- config/timeouts.config.ts | 6 + core/action-retry.ts | 54 +- core/base.page.ts | 171 +- core/failure-tracker.ts | 304 + core/heal-locator.ts | 4 +- core/index.ts | 17 + core/quarantine.ts | 2 + data/admit.data.ts | 29 + data/basic-hospital.data.ts | 40 + data/flow.data.ts | 17 + data/surgery.data.ts | 56 + data/user.data.ts | 19 + data/vendor.data.ts | 29 + data/warehouse.data.ts | 12 + docs/flake.md | 68 +- ...\344\276\213\346\261\207\346\212\245.xlsx" | Bin 0 -> 19364 bytes ...\276\347\211\207_20261020144646_528_5.png" | Bin 0 -> 233507 bytes ...\276\347\211\207_20261020144714_529_5.png" | Bin 0 -> 175433 bytes ...\276\347\211\207_20261020144735_530_5.png" | Bin 0 -> 283279 bytes ...\276\347\211\207_20261020163330_532_5.png" | Bin 0 -> 377243 bytes ...\276\347\211\207_20261020163407_533_5.png" | Bin 0 -> 200506 bytes ...\276\347\211\207_20261020143712_527_5.png" | Bin 0 -> 434059 bytes ...\276\347\211\207_20261020143340_525_5.png" | Bin 0 -> 370403 bytes ...\276\347\211\207_20261020143641_526_5.png" | Bin 0 -> 264890 bytes fixtures/index.ts | 33 +- fixtures/page-registry.ts | 60 + locators/spd/admit-material-list.locator.yaml | 469 ++ locators/spd/admit-material.locator.yaml | 1280 +++++ locators/spd/admit-product-apply.locator.yaml | 1985 +++++++ locators/spd/admit-product-list.locator.yaml | 451 ++ locators/spd/admit-shared.locator.ts | 44 + locators/spd/admit-supply.locator.yaml | 596 ++ locators/spd/admit-task.locator.yaml | 348 ++ locators/spd/basic-branch.locator.ts | 25 + locators/spd/basic-branch.locator.yaml | 405 ++ locators/spd/basic-finance-month.locator.ts | 25 + locators/spd/basic-finance-month.locator.yaml | 306 + locators/spd/basic-hospital-info.locator.ts | 29 + locators/spd/basic-hospital-info.locator.yaml | 271 + locators/spd/basic-notice.locator.ts | 25 + locators/spd/basic-notice.locator.yaml | 549 ++ locators/spd/basic-role.locator.ts | 36 + locators/spd/basic-role.locator.yaml | 5001 +++++++++++++++++ locators/spd/basic-shared.locator.ts | 46 + locators/spd/basic-user-shared.locator.ts | 46 + locators/spd/basic-user-union.locator.ts | 10 + locators/spd/basic-user-union.locator.yaml | 533 ++ locators/spd/basic-user.locator.ts | 35 + locators/spd/basic-user.locator.yaml | 517 ++ locators/spd/flow-def.locator.ts | 37 + locators/spd/flow-def.locator.yaml | 298 + locators/spd/flow-inst.locator.ts | 35 + locators/spd/flow-inst.locator.yaml | 444 ++ locators/spd/surgery-base-pkg.locator.yaml | 636 +++ locators/spd/surgery-oper-type.locator.yaml | 335 ++ locators/spd/surgery-shared.locator.ts | 103 + locators/spd/surgery-std-pkg.locator.yaml | 688 +++ locators/spd/vendor-auth-final.locator.yaml | 318 ++ locators/spd/vendor-auth-first.locator.yaml | 318 ++ locators/spd/vendor-cert-diff.locator.yaml | 408 ++ locators/spd/vendor-cert-final.locator.yaml | 318 ++ locators/spd/vendor-cert-first.locator.yaml | 318 ++ .../spd/vendor-eval-indicator.locator.yaml | 509 ++ locators/spd/vendor-eval-scheme.locator.yaml | 370 ++ locators/spd/vendor-holiday.locator.yaml | 468 ++ locators/spd/vendor-qual.locator.yaml | 362 ++ locators/spd/vendor-query.locator.yaml | 347 ++ locators/spd/vendor-shared.locator.ts | 46 + locators/spd/warehouse-probe.locator.ts | 40 + locators/spd/warehouse-probe.locator.yaml | 461 ++ locators/spd/warehouse-zone.locator.ts | 39 + locators/spd/warehouse-zone.locator.yaml | 591 ++ locators/spd/wh-flow-shared.locator.ts | 44 + package.json | 72 + pages/spd/admit-material-list.page.ts | 166 + pages/spd/admit-material.page.ts | 215 + pages/spd/admit-product-apply.page.ts | 173 + pages/spd/admit-product-list.page.ts | 143 + pages/spd/admit-shared.page.ts | 520 ++ pages/spd/admit-supply.page.ts | 176 + pages/spd/admit-task.page.ts | 147 + pages/spd/basic-branch.page.ts | 232 + pages/spd/basic-finance-month.page.ts | 224 + pages/spd/basic-hospital-info.page.ts | 113 + pages/spd/basic-notice.page.ts | 242 + pages/spd/basic-role.page.ts | 217 + pages/spd/basic-shared.page.ts | 173 + pages/spd/basic-user-shared.page.ts | 297 + pages/spd/basic-user-union.page.ts | 92 + pages/spd/basic-user.page.ts | 142 + pages/spd/center-report-shared.page.ts | 7 +- pages/spd/flow-def.page.ts | 140 + pages/spd/flow-inst.page.ts | 127 + pages/spd/helpers/reuse-open.helper.ts | 4 + pages/spd/surgery-base-pkg.page.ts | 236 + pages/spd/surgery-oper-type.page.ts | 249 + pages/spd/surgery-shared.page.ts | 487 ++ pages/spd/surgery-std-pkg.page.ts | 450 ++ pages/spd/vendor-audit.page.ts | 136 + pages/spd/vendor-auth-final.page.ts | 36 + pages/spd/vendor-auth-first.page.ts | 38 + pages/spd/vendor-cert-diff.page.ts | 214 + pages/spd/vendor-cert-final.page.ts | 34 + pages/spd/vendor-cert-first.page.ts | 35 + pages/spd/vendor-eval-indicator.page.ts | 143 + pages/spd/vendor-eval-scheme.page.ts | 147 + pages/spd/vendor-holiday.page.ts | 148 + pages/spd/vendor-qual.page.ts | 151 + pages/spd/vendor-query.page.ts | 113 + pages/spd/vendor-shared.page.ts | 435 ++ pages/spd/warehouse-probe.page.ts | 139 + pages/spd/warehouse-zone.page.ts | 159 + pages/spd/wh-flow-shared.page.ts | 346 ++ playwright.config.ts | 3 + reporters/failure-trend.reporter.ts | 65 + reports/README.md | 26 +- scripts/_add-combination-po.py | 259 + scripts/_diag-role.mjs | 41 + scripts/_diag-role2.mjs | 73 + scripts/_diag-role3.mjs | 63 + scripts/_diag-role4.mjs | 27 + scripts/_diag-role5.mjs | 24 + scripts/_list-filter-fields.py | 52 + scripts/add-basic-combination-cases.py | 115 + scripts/add-basic-combination-spec.py | 70 + scripts/align-basic-cases-format.py | 207 + scripts/assert-basic-csv-spec-sync.mjs | 111 + scripts/assert-surgery-csv-spec-sync.mjs | 103 + scripts/assert-user-csv-spec-sync.mjs | 103 + scripts/assert-vendor-csv-spec-sync.mjs | 110 + scripts/assert-vendor-no-reuse-open.mjs | 29 + scripts/assert-wh-flow-csv-spec-sync.mjs | 104 + scripts/ci-email-summary.mjs | 48 + scripts/failure-report.mjs | 84 + scripts/generate-surgery-csv.py | 515 ++ scripts/generate-user-md.py | 795 +++ scripts/generate-user-testpoints.py | 833 +++ scripts/generate-vendor-md-csv.py | 560 ++ scripts/generate-vendor-testpoints.py | 577 ++ scripts/generate-wh-flow-md.py | 930 +++ scripts/generate-wh-flow-testpoints.py | 1124 ++++ scripts/json-to-locator-yaml.mjs | 111 +- scripts/run-ci.mjs | 191 +- scripts/scan-admit-batch.mjs | 232 + scripts/scan-basic-batch.mjs | 332 ++ scripts/scan-interactive-elements.mjs | 436 +- scripts/scan-surgery-batch.mjs | 296 + scripts/scan-user-batch.mjs | 317 ++ scripts/scan-vendor-batch.mjs | 290 + scripts/scan-wh-flow-batch.mjs | 475 ++ scripts/sync-docs-ui-specs.py | 963 +++- scripts/sync-quarantine.mjs | 158 + ...50\257\225\347\224\250\344\276\213-01.csv" | 46 + ...50\257\225\347\224\250\344\276\213-01.csv" | 56 + ...50\257\225\347\224\250\344\276\213-01.csv" | 57 + ...50\257\225\347\224\250\344\276\213-01.csv" | 54 + ...50\257\225\347\224\250\344\276\213-01.csv" | 48 + ...50\257\225\347\224\250\344\276\213-01.csv" | 70 + ...50\257\225\347\224\250\344\276\213-01.csv" | 55 + ...50\257\225\347\224\250\344\276\213-01.csv" | 32 + ...50\257\225\347\224\250\344\276\213-01.csv" | 41 + ...50\257\225\347\224\250\344\276\213-01.csv" | 55 + ...50\257\225\347\224\250\344\276\213-01.csv" | 19 + ...50\257\225\347\224\250\344\276\213-01.csv" | 33 + ...50\257\225\347\224\250\344\276\213-01.csv" | 25 + ...50\257\225\347\224\250\344\276\213-01.csv" | 36 + ...50\257\225\347\224\250\344\276\213-01.csv" | 34 + ...50\257\225\347\224\250\344\276\213-01.csv" | 33 + ...50\257\225\347\224\250\344\276\213-01.csv" | 32 + ...50\257\225\347\224\250\344\276\213-01.csv" | 34 + ...50\257\225\347\224\250\344\276\213-01.csv" | 25 + ...50\257\225\347\224\250\344\276\213-01.csv" | 30 + ...50\257\225\347\224\250\344\276\213-01.csv" | 77 + ...50\257\225\347\224\250\344\276\213-01.csv" | 66 + ...50\257\225\347\224\250\344\276\213-01.csv" | 72 + ...50\257\225\347\224\250\344\276\213-01.csv" | 72 + ...50\257\225\347\224\250\344\276\213-01.csv" | 85 + ...50\257\225\347\224\250\344\276\213-01.csv" | 62 + ...50\257\225\347\224\250\344\276\213-01.csv" | 51 + ...50\257\225\347\224\250\344\276\213-01.csv" | 57 + ...50\257\225\347\224\250\344\276\213-01.csv" | 85 + ...50\257\225\347\224\250\344\276\213-01.csv" | 54 + ...350\257\225\347\224\250\344\276\213-01.md" | 242 + ...350\257\225\347\224\250\344\276\213-01.md" | 339 ++ ...350\257\225\347\224\250\344\276\213-01.md" | 305 + ...350\257\225\347\224\250\344\276\213-01.md" | 261 + ...350\257\225\347\224\250\344\276\213-01.md" | 244 + ...350\257\225\347\224\250\344\276\213-01.md" | 379 ++ ...350\257\225\347\224\250\344\276\213-01.md" | 324 ++ ...350\257\225\347\224\250\344\276\213-01.md" | 202 + ...350\257\225\347\224\250\344\276\213-01.md" | 261 + ...350\257\225\347\224\250\344\276\213-01.md" | 323 ++ ...350\257\225\347\224\250\344\276\213-01.md" | 159 + ...350\257\225\347\224\250\344\276\213-01.md" | 210 + ...350\257\225\347\224\250\344\276\213-01.md" | 195 + ...350\257\225\347\224\250\344\276\213-01.md" | 184 + ...350\257\225\347\224\250\344\276\213-01.md" | 184 + ...350\257\225\347\224\250\344\276\213-01.md" | 184 + ...350\257\225\347\224\250\344\276\213-01.md" | 184 + ...350\257\225\347\224\250\344\276\213-01.md" | 193 + ...350\257\225\347\224\250\344\276\213-01.md" | 166 + ...350\257\225\347\224\250\344\276\213-01.md" | 183 + ...350\257\225\347\224\250\344\276\213-01.md" | 367 ++ ...350\257\225\347\224\250\344\276\213-01.md" | 313 ++ ...350\257\225\347\224\250\344\276\213-01.md" | 387 ++ ...350\257\225\347\224\250\344\276\213-01.md" | 438 ++ ...350\257\225\347\224\250\344\276\213-01.md" | 496 ++ ...350\257\225\347\224\250\344\276\213-01.md" | 297 + ...350\257\225\347\224\250\344\276\213-01.md" | 256 + ...350\257\225\347\224\250\344\276\213-01.md" | 286 + ...350\257\225\347\224\250\344\276\213-01.md" | 403 ++ ...350\257\225\347\224\250\344\276\213-01.md" | 261 + ...347\202\271\345\210\206\346\236\220-01.md" | 103 + ...347\202\271\345\210\206\346\236\220-01.md" | 173 + ...347\202\271\345\210\206\346\236\220-01.md" | 133 + ...347\202\271\345\210\206\346\236\220-01.md" | 121 + ...347\202\271\345\210\206\346\236\220-01.md" | 103 + ...347\202\271\345\210\206\346\236\220-01.md" | 171 + ...347\202\271\345\210\206\346\236\220-01.md" | 180 + ...347\202\271\345\210\206\346\236\220-01.md" | 123 + ...347\202\271\345\210\206\346\236\220-01.md" | 140 + ...347\202\271\345\210\206\346\236\220-01.md" | 135 + ...347\202\271\345\210\206\346\236\220-01.md" | 69 + ...347\202\271\345\210\206\346\236\220-01.md" | 70 + ...347\202\271\345\210\206\346\236\220-01.md" | 68 + ...347\202\271\345\210\206\346\236\220-01.md" | 70 + ...347\202\271\345\210\206\346\236\220-01.md" | 69 + ...347\202\271\345\210\206\346\236\220-01.md" | 69 + ...347\202\271\345\210\206\346\236\220-01.md" | 69 + ...347\202\271\345\210\206\346\236\220-01.md" | 72 + ...347\202\271\345\210\206\346\236\220-01.md" | 70 + ...347\202\271\345\210\206\346\236\220-01.md" | 68 + ...347\202\271\345\210\206\346\236\220-01.md" | 257 + ...347\202\271\345\210\206\346\236\220-01.md" | 168 + ...347\202\271\345\210\206\346\236\220-01.md" | 147 + ...347\202\271\345\210\206\346\236\220-01.md" | 170 + ...347\202\271\345\210\206\346\236\220-01.md" | 174 + ...347\202\271\345\210\206\346\236\220-01.md" | 128 + ...347\202\271\345\210\206\346\236\220-01.md" | 160 + ...347\202\271\345\210\206\346\236\220-01.md" | 161 + ...347\202\271\345\210\206\346\236\220-01.md" | 216 + ...347\202\271\345\210\206\346\236\220-01.md" | 132 + tests/setup/auth.setup.ts | 5 + tests/spd/admit-material-list.spec.ts | 82 + tests/spd/admit-material.spec.ts | 122 + tests/spd/admit-product-apply.spec.ts | 112 + tests/spd/admit-product-list.spec.ts | 82 + tests/spd/admit-supply.spec.ts | 104 + tests/spd/admit-task.spec.ts | 89 + tests/spd/basic-branch.spec.ts | 113 + tests/spd/basic-finance-month.spec.ts | 99 + tests/spd/basic-hospital-info.spec.ts | 74 + tests/spd/basic-notice.spec.ts | 124 + tests/spd/basic-role.spec.ts | 193 + tests/spd/basic-user-union.spec.ts | 189 + tests/spd/basic-user.spec.ts | 251 + tests/spd/flow-def.spec.ts | 147 + tests/spd/flow-inst.spec.ts | 205 + tests/spd/helpers/surgery-assert.helper.ts | 45 + tests/spd/helpers/user-assert.helper.ts | 22 + tests/spd/helpers/vendor-assert.helper.ts | 33 + tests/spd/helpers/wh-flow-assert.helper.ts | 7 + tests/spd/surgery-base-pkg.spec.ts | 225 + tests/spd/surgery-oper-type.spec.ts | 254 + tests/spd/surgery-std-pkg.spec.ts | 279 + tests/spd/vendor-audit.spec.ts | 228 + tests/spd/vendor-cert-diff.spec.ts | 83 + tests/spd/vendor-eval-indicator.spec.ts | 62 + tests/spd/vendor-eval-scheme.spec.ts | 81 + tests/spd/vendor-holiday.spec.ts | 68 + tests/spd/vendor-qual.spec.ts | 101 + tests/spd/vendor-query.spec.ts | 97 + tests/spd/warehouse-probe.spec.ts | 209 + tests/spd/warehouse-zone.spec.ts | 238 + 277 files changed, 57169 insertions(+), 69 deletions(-) create mode 100644 core/failure-tracker.ts create mode 100644 data/admit.data.ts create mode 100644 data/basic-hospital.data.ts create mode 100644 data/flow.data.ts create mode 100644 data/surgery.data.ts create mode 100644 data/user.data.ts create mode 100644 data/vendor.data.ts create mode 100644 data/warehouse.data.ts create mode 100644 "docs/\345\237\272\347\241\200\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\347\224\250\344\276\213\346\261\207\346\212\245.xlsx" create mode 100644 "docs/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256/\345\276\256\344\277\241\345\233\276\347\211\207_20261020144646_528_5.png" create mode 100644 "docs/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232/\345\276\256\344\277\241\345\233\276\347\211\207_20261020144714_529_5.png" create mode 100644 "docs/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232/\345\276\256\344\277\241\345\233\276\347\211\207_20261020144735_530_5.png" create mode 100644 "docs/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213/\345\276\256\344\277\241\345\233\276\347\211\207_20261020163330_532_5.png" create mode 100644 "docs/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256/\345\276\256\344\277\241\345\233\276\347\211\207_20261020163407_533_5.png" create mode 100644 "docs/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206/\345\276\256\344\277\241\345\233\276\347\211\207_20261020143712_527_5.png" create mode 100644 "docs/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206/\345\276\256\344\277\241\345\233\276\347\211\207_20261020143340_525_5.png" create mode 100644 "docs/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206/\345\276\256\344\277\241\345\233\276\347\211\207_20261020143641_526_5.png" create mode 100644 locators/spd/admit-material-list.locator.yaml create mode 100644 locators/spd/admit-material.locator.yaml create mode 100644 locators/spd/admit-product-apply.locator.yaml create mode 100644 locators/spd/admit-product-list.locator.yaml create mode 100644 locators/spd/admit-shared.locator.ts create mode 100644 locators/spd/admit-supply.locator.yaml create mode 100644 locators/spd/admit-task.locator.yaml create mode 100644 locators/spd/basic-branch.locator.ts create mode 100644 locators/spd/basic-branch.locator.yaml create mode 100644 locators/spd/basic-finance-month.locator.ts create mode 100644 locators/spd/basic-finance-month.locator.yaml create mode 100644 locators/spd/basic-hospital-info.locator.ts create mode 100644 locators/spd/basic-hospital-info.locator.yaml create mode 100644 locators/spd/basic-notice.locator.ts create mode 100644 locators/spd/basic-notice.locator.yaml create mode 100644 locators/spd/basic-role.locator.ts create mode 100644 locators/spd/basic-role.locator.yaml create mode 100644 locators/spd/basic-shared.locator.ts create mode 100644 locators/spd/basic-user-shared.locator.ts create mode 100644 locators/spd/basic-user-union.locator.ts create mode 100644 locators/spd/basic-user-union.locator.yaml create mode 100644 locators/spd/basic-user.locator.ts create mode 100644 locators/spd/basic-user.locator.yaml create mode 100644 locators/spd/flow-def.locator.ts create mode 100644 locators/spd/flow-def.locator.yaml create mode 100644 locators/spd/flow-inst.locator.ts create mode 100644 locators/spd/flow-inst.locator.yaml create mode 100644 locators/spd/surgery-base-pkg.locator.yaml create mode 100644 locators/spd/surgery-oper-type.locator.yaml create mode 100644 locators/spd/surgery-shared.locator.ts create mode 100644 locators/spd/surgery-std-pkg.locator.yaml create mode 100644 locators/spd/vendor-auth-final.locator.yaml create mode 100644 locators/spd/vendor-auth-first.locator.yaml create mode 100644 locators/spd/vendor-cert-diff.locator.yaml create mode 100644 locators/spd/vendor-cert-final.locator.yaml create mode 100644 locators/spd/vendor-cert-first.locator.yaml create mode 100644 locators/spd/vendor-eval-indicator.locator.yaml create mode 100644 locators/spd/vendor-eval-scheme.locator.yaml create mode 100644 locators/spd/vendor-holiday.locator.yaml create mode 100644 locators/spd/vendor-qual.locator.yaml create mode 100644 locators/spd/vendor-query.locator.yaml create mode 100644 locators/spd/vendor-shared.locator.ts create mode 100644 locators/spd/warehouse-probe.locator.ts create mode 100644 locators/spd/warehouse-probe.locator.yaml create mode 100644 locators/spd/warehouse-zone.locator.ts create mode 100644 locators/spd/warehouse-zone.locator.yaml create mode 100644 locators/spd/wh-flow-shared.locator.ts create mode 100644 pages/spd/admit-material-list.page.ts create mode 100644 pages/spd/admit-material.page.ts create mode 100644 pages/spd/admit-product-apply.page.ts create mode 100644 pages/spd/admit-product-list.page.ts create mode 100644 pages/spd/admit-shared.page.ts create mode 100644 pages/spd/admit-supply.page.ts create mode 100644 pages/spd/admit-task.page.ts create mode 100644 pages/spd/basic-branch.page.ts create mode 100644 pages/spd/basic-finance-month.page.ts create mode 100644 pages/spd/basic-hospital-info.page.ts create mode 100644 pages/spd/basic-notice.page.ts create mode 100644 pages/spd/basic-role.page.ts create mode 100644 pages/spd/basic-shared.page.ts create mode 100644 pages/spd/basic-user-shared.page.ts create mode 100644 pages/spd/basic-user-union.page.ts create mode 100644 pages/spd/basic-user.page.ts create mode 100644 pages/spd/flow-def.page.ts create mode 100644 pages/spd/flow-inst.page.ts create mode 100644 pages/spd/surgery-base-pkg.page.ts create mode 100644 pages/spd/surgery-oper-type.page.ts create mode 100644 pages/spd/surgery-shared.page.ts create mode 100644 pages/spd/surgery-std-pkg.page.ts create mode 100644 pages/spd/vendor-audit.page.ts create mode 100644 pages/spd/vendor-auth-final.page.ts create mode 100644 pages/spd/vendor-auth-first.page.ts create mode 100644 pages/spd/vendor-cert-diff.page.ts create mode 100644 pages/spd/vendor-cert-final.page.ts create mode 100644 pages/spd/vendor-cert-first.page.ts create mode 100644 pages/spd/vendor-eval-indicator.page.ts create mode 100644 pages/spd/vendor-eval-scheme.page.ts create mode 100644 pages/spd/vendor-holiday.page.ts create mode 100644 pages/spd/vendor-qual.page.ts create mode 100644 pages/spd/vendor-query.page.ts create mode 100644 pages/spd/vendor-shared.page.ts create mode 100644 pages/spd/warehouse-probe.page.ts create mode 100644 pages/spd/warehouse-zone.page.ts create mode 100644 pages/spd/wh-flow-shared.page.ts create mode 100644 reporters/failure-trend.reporter.ts create mode 100644 scripts/_add-combination-po.py create mode 100644 scripts/_diag-role.mjs create mode 100644 scripts/_diag-role2.mjs create mode 100644 scripts/_diag-role3.mjs create mode 100644 scripts/_diag-role4.mjs create mode 100644 scripts/_diag-role5.mjs create mode 100644 scripts/_list-filter-fields.py create mode 100644 scripts/add-basic-combination-cases.py create mode 100644 scripts/add-basic-combination-spec.py create mode 100644 scripts/align-basic-cases-format.py create mode 100644 scripts/assert-basic-csv-spec-sync.mjs create mode 100644 scripts/assert-surgery-csv-spec-sync.mjs create mode 100644 scripts/assert-user-csv-spec-sync.mjs create mode 100644 scripts/assert-vendor-csv-spec-sync.mjs create mode 100644 scripts/assert-vendor-no-reuse-open.mjs create mode 100644 scripts/assert-wh-flow-csv-spec-sync.mjs create mode 100644 scripts/failure-report.mjs create mode 100644 scripts/generate-surgery-csv.py create mode 100644 scripts/generate-user-md.py create mode 100644 scripts/generate-user-testpoints.py create mode 100644 scripts/generate-vendor-md-csv.py create mode 100644 scripts/generate-vendor-testpoints.py create mode 100644 scripts/generate-wh-flow-md.py create mode 100644 scripts/generate-wh-flow-testpoints.py create mode 100644 scripts/scan-admit-batch.mjs create mode 100644 scripts/scan-basic-batch.mjs create mode 100644 scripts/scan-surgery-batch.mjs create mode 100644 scripts/scan-user-batch.mjs create mode 100644 scripts/scan-vendor-batch.mjs create mode 100644 scripts/scan-wh-flow-batch.mjs create mode 100644 scripts/sync-quarantine.mjs create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 "testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" create mode 100644 tests/spd/admit-material-list.spec.ts create mode 100644 tests/spd/admit-material.spec.ts create mode 100644 tests/spd/admit-product-apply.spec.ts create mode 100644 tests/spd/admit-product-list.spec.ts create mode 100644 tests/spd/admit-supply.spec.ts create mode 100644 tests/spd/admit-task.spec.ts create mode 100644 tests/spd/basic-branch.spec.ts create mode 100644 tests/spd/basic-finance-month.spec.ts create mode 100644 tests/spd/basic-hospital-info.spec.ts create mode 100644 tests/spd/basic-notice.spec.ts create mode 100644 tests/spd/basic-role.spec.ts create mode 100644 tests/spd/basic-user-union.spec.ts create mode 100644 tests/spd/basic-user.spec.ts create mode 100644 tests/spd/flow-def.spec.ts create mode 100644 tests/spd/flow-inst.spec.ts create mode 100644 tests/spd/helpers/surgery-assert.helper.ts create mode 100644 tests/spd/helpers/user-assert.helper.ts create mode 100644 tests/spd/helpers/vendor-assert.helper.ts create mode 100644 tests/spd/helpers/wh-flow-assert.helper.ts create mode 100644 tests/spd/surgery-base-pkg.spec.ts create mode 100644 tests/spd/surgery-oper-type.spec.ts create mode 100644 tests/spd/surgery-std-pkg.spec.ts create mode 100644 tests/spd/vendor-audit.spec.ts create mode 100644 tests/spd/vendor-cert-diff.spec.ts create mode 100644 tests/spd/vendor-eval-indicator.spec.ts create mode 100644 tests/spd/vendor-eval-scheme.spec.ts create mode 100644 tests/spd/vendor-holiday.spec.ts create mode 100644 tests/spd/vendor-qual.spec.ts create mode 100644 tests/spd/vendor-query.spec.ts create mode 100644 tests/spd/warehouse-probe.spec.ts create mode 100644 tests/spd/warehouse-zone.spec.ts diff --git a/.env.example b/.env.example index 7279e4d..ff79a0d 100644 --- a/.env.example +++ b/.env.example @@ -23,6 +23,8 @@ TEST_PASS= # 操作层瞬时重试(click/fill/goto),默认 3 次;断言失败不会走这里 # ACTION_RETRIES=3 # ACTION_RETRY_DELAY_MS=250 +# 单次操作重试总预算(毫秒,含首次与退避);到点即停,避免 次数×超时 叠加;0=不限 +# ACTION_RETRY_BUDGET_MS=45000 # 整条用例重试默认 0(不要全局打开);仅排查时可显式设 PW_RETRIES # PW_RETRIES=0 # 已知瞬时 flaky 用 testFlaky,额外重跑次数(不含首次) @@ -32,6 +34,11 @@ TEST_PASS= # RUN_QUARANTINE=1 # FLAKE_MIN_RUNS=4 # FLAKE_RATE=0.3 +# 失败频次统计(跨构建累计):失败次数与失败率双达标才算「经常失败」 +# FREQ_FAIL_MIN=2 +# FREQ_FAIL_RATE=0.2 +# 累计文件路径(默认 reports/flake/failure-counts.json) +# PW_FAILURE_COUNTS= # PW_WORKERS=3 # 分片并行(npm run test:warn:shard / test:spd:shard):SHARDS=3 WORKERS=2 # 排序全列等 @full 用例:RUN_FULL=1 @@ -40,7 +47,14 @@ TEST_PASS= # 分片:SHARDS=2 或 PW_SHARD=1/2(多 Agent) # 默认按文件并行;不要开 PW_FULLY_PARALLEL=1(同账号会互踩) # 冒烟快速失败:PW_MAX_FAILURES=8 -# 关闭 Allure:ALLURE=0 +# Allure 默认关(与 HTML 重复保存 trace/截图);ALLURE=1 才额外生成 +# ALLURE=1 +# JUnit 默认紧凑(只留 message);需要完整堆栈时设 0 +# PW_JUNIT_COMPACT=0 +# 归档内只留体积最大的 N 个 trace/截图附件(默认全留;0=不要) +# PW_KEEP_TRACES=0 +# 本机只保留最近 N 份 reports/ci-*(默认全留;0=跑完即删本次归档) +# PW_KEEP_ARCHIVES=1 # 纳入调试 spec:INCLUDE_DEBUG_SPECS=1 # 跳过站点探活:SKIP_PREFLIGHT=1 diff --git a/Jenkinsfile b/Jenkinsfile index ae9c130..2bda2c0 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -120,6 +120,9 @@ pipeline { string(name: 'RETRIES', defaultValue: '0', description: '整条用例重试次数,默认 0;瞬时加载由操作层重试,勿全局打开') booleanParam(name: 'RUN_FULL', defaultValue: false, description: '包含 @full 全列排序(耗时长)') string(name: 'GREP', defaultValue: '', description: '只跑标题匹配这些用例(Playwright grep)。空则按 SUITE 全跑') + // Allure 默认关(与 HTML 重复保存 trace/截图);需要时勾选 + booleanParam(name: 'ALLURE', defaultValue: false, description: '额外生成 Allure(体积约翻倍,默认关闭)') + string(name: 'KEEP_ARCHIVES', defaultValue: '1', description: '本节点保留最近 N 份 reports/ci-*;-1=全留(占盘最快),0=跑完即删本次归档') string( name: 'REPORT_EMAIL_TO', defaultValue: '', @@ -137,6 +140,9 @@ pipeline { RUN_FULL = "${params.RUN_FULL ? '1' : '0'}" PW_GREP = "${params.GREP ?: ''}" PLAYWRIGHT_BROWSERS_PATH = "${WORKSPACE}/.playwright-browsers" + // 存储:Allure 默认关(与 HTML 重复保存 trace/截图);归档只留本次,latest/ 始终可用 + ALLURE = "${params.ALLURE ? '1' : '0'}" + PW_KEEP_ARCHIVES = "${params.KEEP_ARCHIVES ?: '1'}" SPD_CREDENTIALS_ID = "${env.SPD_CREDENTIALS_ID ?: 'spd-ui-test-account'}" JAVA_HOME = "${env.JAVA_HOME ?: 'E:/Program Files/Java/jdk-11'}" } diff --git a/README.md b/README.md index 4bcad54..ce7c20c 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,17 @@ Chromium 缓存在项目内 `.playwright-browsers/`(已 gitignore),与 `@p 默认站点:`http://spdtest.cmic.com.cn:8080`,登录页 `/spd/login`。账号只放 `.env`(已 gitignore),不要写入源码。 -`login` project 不带登录态,测登录页本身;`chromium` 依赖 `auth.setup` 复用 `auth/user.json`。 +`login` project 不带登录态,测登录页本身;`chromium` 依赖 `auth.setup` 写入并复用 `auth/user.json`。 -**登录态复用**:`utils/auth-session.ts` 统一编排。`auth.setup` 与同页 `describeWithReuseOpen` 共用:token 未过期则复用 `auth/user.json`,过期或被踢回登录页则重新登录并重建上下文。需强制刷新:`FORCE_AUTH_LOGIN=1`。Jenkins / `npm run test:ci` 默认强制重新登录,避免 Agent 工作区残留旧会话。 +### 登录态复用(storageState,大幅提速) + +- **单独 setup**:`tests/setup/auth.setup.ts` 一次性登录,调用 `context.storageState({ path: auth/user.json })`。 +- **业务用例加载文件**:`chromium` project 配置 `storageState: AUTH_STATE_PATH`;每条用例由 Playwright **新建独立 BrowserContext**,再注入该文件中的 cookie / localStorage,直接进入已登录态。 +- **隔离第一原则**:复用的是**登录态文件**,不是同一个 context。禁止全局共享 `BrowserContext`/`Page`;厂商 / 准入 / 医院资料等新模块每条用例各自 `open`,互不污染。未登录反例用局部 `test.use({ storageState: { cookies: [], origins: [] } })`。 +- token 未过期可跳过重复登录与探活;过期或被踢则 setup 重新登录。强制刷新:`FORCE_AUTH_LOGIN=1`。Jenkins / `npm run test:ci` 默认强制重新登录,避免 Agent 残留旧会话。 +- 存量报表里的 `describeWithReuseOpen`(组内同页加速)**不要**扩到新模块。 + +`utils/auth-session.ts` 统一编排登录与探活。 ## 套件与 Jenkins @@ -65,19 +73,25 @@ scripts/run-ci.mjs Jenkins / 定时执行入口 | 层 | 允许 | 禁止 | |----|------|------| | `config/` | 环境、超时、套件 | 业务操作 | -| `data/` | 路径、列名、样本字段、mock 值 | 选择器、click/fill | +| `data/` | 路径、列名、样本字段、mock 值 | 选择器、click/fill、密码 | | `locators/` | `getByRole` / `getByPlaceholder` / `getByTestId` | click、fill、断言 | | `core/` | 等待、操作重试、诊断、健康检查、BasePage | 具体报表选择器 | -| `pages/` | 业务操作、调用 core 等待 | 用例流程编排、裸选择器 | -| `tests/` | 调用 Page 方法 + 断言业务结果 | `page.locator(...)`、访问 `locators` | +| `pages/` | 业务操作、取数/快照(如 `snapshotList`) | 用例主断言、流程编排、裸密码 | +| `tests/` | 调用 Page 方法 + **`expect` 断言业务结果** | `page.locator(...)`(反例探测除外)、访问 `locators` | + +厂商管理样板:`tests/spd/vendor-query.spec.ts` + `tests/spd/helpers/vendor-assert.helper.ts`(断言在用例;PO 无 `expect`)。 **智能等待**:查询按钮 loading、Ant Spin、表体有行/空态,统一走 `core/smart-wait.ts`。动画沉降用 `waitUiSettle`(双 rAF),不要在 Page 里写 `waitForTimeout`。 **重试策略**: - **操作层**(默认开):`BasePage` 的 `goto` / `click` / `fill` / `getText` 对点击被挡、节点卸载、导航超时等瞬时失败重试(`ACTION_RETRIES`,默认 3 次)。`expect`、健康检查、`guardedSkip` **不重试**。 +- **写操作安全**(默认防重复触发):导出、提交、删除、打印等 `{ operation: 'write' }` 的点击**只尝试 1 次**、且不试 fallback;仅当业务确认幂等时才加 `idempotent: true`。 +- **超时总预算**:`retryAction` 的 `deadlineMs`(默认 `ACTION_RETRY_BUDGET_MS=45_000`)限制单次操作总耗时,避免 `次数 × 单次超时` 叠成几十秒慢失败。 - **用例层**(默认关):全局 `retries=0`,Jenkins / GitHub 也不自动打开。已知瞬时 flaky 用 `testFlaky`(带 `@flaky`),断言失败仍立即失败。不要设 `PW_RETRIES` 掩盖断言失败。 -**Flake 治理**:失败自动分为产品/脚本/环境/数据/瞬时,写入 `failure-context.md`。CI 累计 `reports/flake/history.jsonl`,`npm run flake:report` 看趋势与隔离候选。已知瞬时问题进 `config/quarantine.json`,主门禁默认隔离;`npm run test:quarantine` 单独验证。细则见 [docs/flake.md](docs/flake.md)。 +**Flake 治理**:失败自动分为产品/脚本/环境/数据/瞬时,写入 `failure-context.md`。CI 累计 `reports/flake/history.jsonl`,`npm run flake:report` 看趋势与隔离候选;`npm run quarantine:sync`(预览)/ `quarantine:apply`(落库)把候选写入 `config/quarantine.json`——只隔离环境/瞬时/脚本类,**不自动隔离产品缺陷**。主门禁默认隔离;`npm run test:quarantine` 单独验证。细则见 [docs/flake.md](docs/flake.md)。 + +**失败频次标记**:跨构建按 `STAT-` 编号累计每个用例的 `失败/执行`,失败次数与失败率双达标即标为「经常失败」。`npm run failures:report` 查榜单(`reports/flake/frequent-failures.md`),CI 邮件摘要自动附该小节;用例报告里带 `frequentFailure` / `failureRuns` / `failureRate` annotation,可直接筛选。 **数据驱动**:每个报表一份 `data/{page}.data.ts`(`CenterReportPageData`),Page 构造时注入;用例标题带 `STAT-xxx`,报告里自动带 `caseId`。 diff --git a/config/timeouts.config.ts b/config/timeouts.config.ts index d4c4173..3559d97 100644 --- a/config/timeouts.config.ts +++ b/config/timeouts.config.ts @@ -24,6 +24,12 @@ export const timeouts = { actionRetries: num('ACTION_RETRIES', 3), /** 操作重试间隔基数(毫秒),第 n 次等待 n * 该值 */ actionRetryDelayMs: num('ACTION_RETRY_DELAY_MS', 250), + /** + * 单次操作重试的总时间预算(毫秒,含首次尝试与退避)。 + * 到点即停,避免 次数 × 单次超时 叠加成几十秒慢失败;0 表示不限。 + * 默认 45_000:约为 action(12s) × 3 的 1.25 倍上限,慢页面下也能及时收敛。 + */ + actionRetryBudgetMs: num('ACTION_RETRY_BUDGET_MS', 45_000), /** iframe 挂载、查询按钮可见 */ pageReady: num('PAGE_READY_TIMEOUT', 20_000), /** 表体 attached / spin hidden */ diff --git a/core/action-retry.ts b/core/action-retry.ts index d4c7d5a..316b8bc 100644 --- a/core/action-retry.ts +++ b/core/action-retry.ts @@ -1,12 +1,33 @@ import { timeouts } from '../config/timeouts.config'; import { logger } from './logger'; +/** + * 操作语义: + * - `read`(默认):查询、导航、读取、打开面板等可安全重复执行的操作,瞬时失败可重试。 + * - `write`:导出、提交、删除、审批等会产生副作用的操作。超时后无法确认服务端是否已执行, + * 盲目重试会重复触发,因此默认只尝试 1 次(除非调用方显式声明 `idempotent: true`)。 + */ +export type RetryOperation = 'read' | 'write'; + export type RetryActionOptions = { - /** 总尝试次数(含首次)。默认 `ACTION_RETRIES`,通常 3。 */ + /** 总尝试次数(含首次)。默认 `ACTION_RETRIES`,通常 3;写操作默认 1。 */ attempts?: number; delayMs?: number; label?: string; onRetry?: (error: unknown, attempt: number) => Promise | void; + /** 操作语义,默认 `read`。写操作默认不重试,防重复触发。 */ + operation?: RetryOperation; + /** + * 写操作是否幂等。仅当业务确认「重复执行安全」(如按固定单据号覆盖式保存)时设 true, + * 才会按 `attempts` 重试;否则即使设了更大 attempts 也只尝试 1 次。 + */ + idempotent?: boolean; + /** + * 本次重试的总时间预算(毫秒,含首次尝试与退避等待)。 + * 到点即停并抛出最后一次错误,避免 `次数 × 单次超时` 叠加成几十秒慢失败。 + * 默认 `ACTION_RETRY_BUDGET_MS`;未设且时间为 0 表示不限。 + */ + deadlineMs?: number; }; type ErrorLike = { @@ -68,30 +89,55 @@ function sleep(ms: number): Promise { /** * 底层操作细粒度重试。只重试瞬时 UI/导航错误;断言与健康检查失败立即抛出。 + * + * 安全边界: + * - 写操作(`operation: 'write'`)默认只尝试 1 次,避免副作用被重复触发; + * - 设了 `deadlineMs` 时,退避等待不会让总耗时突破预算。 */ export async function retryAction( action: () => Promise, options: RetryActionOptions = {}, ): Promise { - const attempts = Math.max(1, options.attempts ?? timeouts.actionRetries); + const operation = options.operation ?? 'read'; + const writesAreRisky = operation === 'write' && !options.idempotent; + const requestedAttempts = Math.max(1, options.attempts ?? timeouts.actionRetries); + // 非幂等写操作:无论传了多少次,都只执行一次。 + const attempts = writesAreRisky ? 1 : requestedAttempts; const delayMs = options.delayMs ?? timeouts.actionRetryDelayMs; const label = options.label ?? '操作'; + const deadlineMs = Math.max(0, options.deadlineMs ?? timeouts.actionRetryBudgetMs); + const startedAt = Date.now(); + const budgetExhausted = (): boolean => deadlineMs > 0 && Date.now() - startedAt >= deadlineMs; let lastError: unknown; + if (writesAreRisky && requestedAttempts > 1) { + logger.warn( + `${label} 属写操作,已禁用重试(防重复触发);如确认幂等请传 idempotent: true`, + ); + } + for (let attempt = 1; attempt <= attempts; attempt += 1) { try { return await action(); } catch (error) { lastError = error; const retryable = shouldRetryAction(error); - if (!retryable || attempt === attempts) { + const isLast = attempt === attempts; + if (!retryable || isLast) { + throw error; + } + if (budgetExhausted()) { + logger.warn(`${label} 重试预算 ${deadlineMs}ms 已用完,放弃重试:${errorText(error).slice(0, 200)}`); throw error; } logger.warn( `${label} 瞬时失败,重试 ${attempt}/${attempts - 1}:${errorText(error).slice(0, 240)}`, ); await options.onRetry?.(error, attempt); - await sleep(delayMs * attempt); + // 退避等待不越界:预算不足时按剩余时间收敛,至少不等待。 + const backoff = delayMs * attempt; + const remaining = deadlineMs > 0 ? deadlineMs - (Date.now() - startedAt) : backoff; + await sleep(Math.max(0, Math.min(backoff, remaining))); } } diff --git a/core/base.page.ts b/core/base.page.ts index 9e22bed..bac1964 100644 --- a/core/base.page.ts +++ b/core/base.page.ts @@ -1,7 +1,7 @@ import { expect, type Locator, type Page, test } from '@playwright/test'; import { timeouts } from '../config/timeouts.config'; -import { retryAction } from './action-retry'; +import { retryAction, type RetryOperation } from './action-retry'; import { runWithLocatorHeal } from './heal-locator'; import { waitAntSpinGone, @@ -88,7 +88,7 @@ export class BasePage { async click( locator: Locator, stepName?: string, - options?: { timeout?: number; fallbacks?: Locator[] }, + options?: { timeout?: number; fallbacks?: Locator[]; operation?: RetryOperation; idempotent?: boolean }, ): Promise { const timeout = options?.timeout ?? timeouts.action; const action = async (): Promise => { @@ -114,6 +114,9 @@ export class BasePage { }, { label: stepName ?? 'click', + // 导出/提交/删除类写操作默认不重试,防副作用重复触发 + operation: options?.operation ?? 'read', + idempotent: options?.idempotent, onRetry: async () => { await waitAntSpinGone(this.page); await waitUiSettle(this.page); @@ -132,7 +135,7 @@ export class BasePage { locator: Locator, value: string, stepName?: string, - options?: { fallbacks?: Locator[] }, + options?: { fallbacks?: Locator[]; operation?: RetryOperation; idempotent?: boolean }, ): Promise { const action = async (): Promise => { if (stepName) { @@ -147,6 +150,8 @@ export class BasePage { }, { label: stepName ?? 'fill', + operation: options?.operation ?? 'read', + idempotent: options?.idempotent, onRetry: async () => { await waitAntSpinGone(this.page); await waitUiSettle(this.page); @@ -164,4 +169,164 @@ export class BasePage { async getText(locator: Locator): Promise { return retryAction(async () => (await locator.innerText()).trim(), { label: '读取文本' }); } + + /** 搜索条作用域:有 iframe 用首个 iframe,否则用主文档。 */ + async queryScope(): Promise { + const frames = this.page.locator('iframe'); + if ((await frames.count().catch(() => 0)) > 0) return this.page.frameLocator('iframe').first(); + return this.page; + } + + /** + * 通用多条件筛选:按字段名定位搜索条,一次性填多个条件后查询。 + * 用于「组合查询」类用例;单字段用例仍可用各页原有专用方法。 + */ + /** + * 通用多条件筛选:按字段名定位搜索条,一次性填多个条件后查询。 + * 用于「组合查询」类用例;单字段用例仍可用各页原有专用方法。 + */ + async applyFilterCombination( + fields: FilterField[], + options: { + /** 搜索条根节点;iframe 页传入 frameLocator */ + scope?: FilterScope; + /** 触发查询的按钮;默认按「查 询」文本找 */ + queryButton?: Locator; + /** 字段不可见时:true=跳过并记录,false=直接失败 */ + skipMissing?: boolean; + } = {}, + ): Promise<{ applied: string[]; missing: string[] }> { + const scope = options.scope ?? this.page; + const applied: string[] = []; + const missing: string[] = []; + + // 筛选区默认可能折叠(如差异对比页),先展开再填,否则后半截字段不可见 + await this.expandFiltersIfCollapsed(scope); + + for (const field of fields) { + const item = await this.resolveFilterItem(scope, field.label); + if (!item) { + missing.push(field.label); + if (options.skipMissing === false) { + throw new Error(`筛选字段「${field.label}」不可见,组合查询无法执行`); + } + continue; + } + if (field.kind === 'select') { + await this.pickFirstOption(scope, item, field.label); + } else if (field.kind === 'dateRange') { + await this.fillDateRange(scope, item, field.label); + } else { + const input = item.locator('input').first(); + await this.fill(input, field.value ?? '', `组合查询:${field.label}`, { + fallbacks: [scope.getByPlaceholder(new RegExp(field.label)).first()], + }); + } + applied.push(field.label); + } + + logger.step(`组合查询:已填 ${applied.length}/${fields.length} 项,未找到 ${missing.join('、') || '无'}`); + expect( + applied.length, + `组合查询至少要有 2 个条件生效(已生效:${applied.join('、') || '无'})`, + ).toBeGreaterThanOrEqual(Math.min(2, fields.length)); + + const queryBtn = options.queryButton ?? scope.getByRole('button', { name: /查\s*询/ }).first(); + await this.click(queryBtn.first(), '组合查询:点击查询'); + await this.waitForTableReady(); + return { applied, missing }; + } + + /** 搜索条「展开」:折叠时点开,保证所有筛选字段可填(如差异对比页默认只露 3 项)。 */ + protected async expandFiltersIfCollapsed(scope: FilterScope): Promise { + const expand = scope.getByRole('button', { name: /^展\s*开$/ }).first(); + const expandByClass = scope.locator('button._btn.collapse').filter({ hasText: '展开' }).first(); + const target = (await expand.isVisible().catch(() => false)) ? expand : expandByClass; + if (await target.isVisible().catch(() => false)) { + await this.click(target, '组合查询:展开筛选区'); + await this.waitSettle(600); + } + } + + /** + * 搜索条中的单个字段:按 label[for] 精确定位到同级控件容器。 + * antd 的 label for / 控件 id 一一对应,比 hasText 更可靠(避免「物料名称」误命中含同名文字的外层)。 + */ + protected async resolveFilterItem(scope: FilterScope, label: string): Promise { + const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const exactLabel = new RegExp(`^\\s*${escaped}\\s*$`); + const labels = scope.locator('.ant-form-item label').filter({ hasText: exactLabel }); + const count = await labels.count().catch(() => 0); + for (let i = 0; i < count; i += 1) { + const lab = labels.nth(i); + const id = await lab.getAttribute('for').catch(() => null); + if (!id) continue; + const item = scope.locator('.ant-form-item').filter({ has: scope.locator(`#${CSS_ESCAPE(id)}`) }).first(); + if (await item.isVisible().catch(() => false)) return item; + } + // 退化:label 所在表单项(仅当 label 文本完全相等时) + const fallback = scope.locator('.ant-form-item').filter({ has: scope.locator('label', { hasText: exactLabel }) }).first(); + if (await fallback.isVisible().catch(() => false)) return fallback; + return null; + } + + /** + * 打开下拉并取第一个可选项。浮层挂在**当前 scope 的 document.body**—— + * iframe 页面的浮层在 iframe 内,主页面查不到,因此 scope 必须传对。 + */ + protected async pickFirstOption(scope: FilterScope, item: Locator, label: string): Promise { + const dropdown = scope.locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden)').last(); + await this.click(item.locator('.ant-select-selector').first(), `组合查询:展开「${label}」`); + const option = dropdown.locator('.ant-select-item-option:not(.ant-select-item-option-disabled)').first(); + await option.waitFor({ state: 'visible', timeout: timeouts.expect }); + const text = ((await option.innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + await this.click(option, `组合查询:选择「${label}」`); + await this.waitSettle(300); + return text; + } + + /** + * 日期区间:antd range picker 输入框是 readonly,必须走浮层「今天」选择, + * 起止同一天既保证区间合法,又不依赖业务日期数据。浮层同样跟随 scope。 + */ + protected async fillDateRange(scope: FilterScope, item: Locator, label: string): Promise { + await this.click(item.locator('input').first(), `组合查询:打开「${label}」`); + const panel = scope.locator('.ant-picker-dropdown:not(.ant-picker-dropdown-hidden)').last(); + const today = panel.getByText(/今\s*天/).first(); + const todayVisible = await today.isVisible().catch(() => false); + if (todayVisible) { + await this.click(today, `组合查询:「${label}」选择今天`); + await this.click(today, `组合查询:「${label}」结束选择今天`).catch(() => undefined); + } else { + // 无「今天」快捷项时退化为键盘回车,避免写死日期格式 + await item.locator('input').first().press('Enter').catch(() => undefined); + } + await this.page.keyboard.press('Escape').catch(() => undefined); + await this.waitSettle(300); + } + + /** 列表就绪(表头或空态出现)。 */ + async waitForTableReady(timeout = timeouts.expect): Promise { + await this.page + .locator('.ant-table-thead th, .ant-table-header th, .ant-empty-normal') + .first() + .waitFor({ state: 'visible', timeout }) + .catch(() => undefined); + await this.waitSettle(400); + } } + +/** 搜索条作用域:Page、Locator 或 FrameLocator 均可。 */ +export type FilterScope = Pick; + +/** CSS id 选择器转义(antd 的 id 形如 table_search_form_keyword,安全,但统一走转义)。 */ +const CSS_ESCAPE = (id: string) => id.replace(/([^\w-])/g, '\\$1'); + +/** 组合查询的单个筛选条件。 */ +export type FilterField = { + /** 搜索条上的字段名,需与页面 label 文本一致 */ + label: string; + kind: 'text' | 'select' | 'dateRange'; + /** kind=text 时的输入值;其余可省略 */ + value?: string; +}; diff --git a/core/failure-tracker.ts b/core/failure-tracker.ts new file mode 100644 index 0000000..30043d5 --- /dev/null +++ b/core/failure-tracker.ts @@ -0,0 +1,304 @@ +/** + * 用例失败频次统计:跨构建累计每个用例(按 STAT- 编号聚合)的执行/失败次数, + * 用于标记「经常执行失败」的用例,便于优先修脚本或提单。 + * + * 存储:reports/flake/failure-counts.json(累计文件,非单次 history,重启不清零)。 + * 标记:失败/成功时写入 Playwright annotation(failureRuns / failureRate / frequentFailure), + * HTML / Allure / JUnit 均可检索;同时可写 markdown 榜单。 + * + * 仅统计「真实执行」的用例:skip 不增加失败,但会增加执行次数;断言与瞬时失败都计入失败。 + */ +import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; + +import type { TestInfo } from '@playwright/test'; + +import { extractCaseId } from './case-meta'; +import { logger } from './logger'; + +export type CaseFailureStat = { + /** STAT-XXX-NNN */ + id: string; + /** 最近一次看到的原标题(去掉编号前缀后可能变化) */ + title: string; + /** 最近一次所在 spec(相对路径) */ + file: string; + /** 累计执行次数(passed + failed + timedOut,不含 skipped) */ + runs: number; + /** 累计通过 */ + passed: number; + /** 累计失败(failed + timedOut) */ + failed: number; + /** 首次失败时间 */ + firstFailedAt?: string; + /** 最近一次失败时间 */ + lastFailedAt?: string; + /** 最近一次失败分类 */ + lastClass?: string; + /** 连续失败次数(一旦通过则归零) */ + consecutiveFails: number; +}; + +export type FailureStore = { + version: number; + updatedAt: string; + /** 判定「经常失败」的阈值(失败次数下限) */ + frequentMinFails: number; + /** 判定「经常失败」的阈值(失败率下限) */ + frequentRate: number; + items: Record; +}; + +const DEFAULT_MIN_FAILS = 2; +const DEFAULT_RATE = 0.2; + +function num(name: string, fallback: number): number { + const raw = process.env[name]; + if (raw === undefined || raw === '') return fallback; + const n = Number(raw); + return Number.isFinite(n) && n >= 0 ? n : fallback; +} + +function storePath(): string { + return process.env.PW_FAILURE_COUNTS || path.join(process.cwd(), 'reports/flake/failure-counts.json'); +} + +export function loadFailureStore(): FailureStore { + const empty: FailureStore = { + version: 1, + updatedAt: new Date().toISOString(), + frequentMinFails: num('FREQ_FAIL_MIN', DEFAULT_MIN_FAILS), + frequentRate: num('FREQ_FAIL_RATE', DEFAULT_RATE), + items: {}, + }; + try { + const parsed = JSON.parse(readFileSync(storePath(), 'utf8')) as Partial; + return { + ...empty, + ...parsed, + // 阈值以当前环境变量为准,便于调参 + frequentMinFails: num('FREQ_FAIL_MIN', parsed.frequentMinFails ?? DEFAULT_MIN_FAILS), + frequentRate: num('FREQ_FAIL_RATE', parsed.frequentRate ?? DEFAULT_RATE), + items: parsed.items ?? {}, + }; + } catch { + return empty; + } +} + +export function saveFailureStore(store: FailureStore): void { + const file = storePath(); + store.updatedAt = new Date().toISOString(); + try { + mkdirSync(path.dirname(file), { recursive: true }); + writeFileSync(file, `${JSON.stringify(store, null, 2)}\n`, 'utf8'); + } catch (error) { + logger.warn(`失败频次统计写入失败:${String(error)}`); + } +} + +/** + * 多 worker / 多分片安全落盘:先读回磁盘上最新统计,把本次进程的增量合并进去再写, + * 避免各进程用「启动时的旧快照」互相覆盖。 + * + * 合并策略:同一用例取两边的较大值。正常情况下一条用例只会落在某一个 worker/分片, + * 因此不存在同用例并发累加;取 max 既能保留先写进程的增量,也能在磁盘更新时不回退。 + */ +export function mergeAndSaveFailureStore(local: FailureStore): void { + const file = storePath(); + let onDisk: FailureStore; + try { + onDisk = JSON.parse(readFileSync(file, 'utf8')) as FailureStore; + } catch { + onDisk = { ...local, items: {} }; + } + const merged: FailureStore = { + version: onDisk.version ?? local.version, + updatedAt: new Date().toISOString(), + frequentMinFails: local.frequentMinFails, + frequentRate: local.frequentRate, + items: { ...(onDisk.items ?? {}) }, + }; + for (const [id, stat] of Object.entries(local.items)) { + const prev = merged.items[id]; + if (!prev) { + merged.items[id] = stat; + continue; + } + merged.items[id] = { + ...prev, + title: stat.title || prev.title, + file: stat.file || prev.file, + // runs/passed/failed 为跨 worker 累加量,用「本地-基线」增量不可知, + // 这里以两边的最大值 + 增量差值近似,避免重复计数导致虚高。 + runs: Math.max(prev.runs, stat.runs), + passed: Math.max(prev.passed, stat.passed), + failed: Math.max(prev.failed, stat.failed), + consecutiveFails: stat.consecutiveFails, + firstFailedAt: prev.firstFailedAt ?? stat.firstFailedAt, + lastFailedAt: laterIso(prev.lastFailedAt, stat.lastFailedAt), + lastClass: stat.lastClass ?? prev.lastClass, + }; + } + try { + mkdirSync(path.dirname(file), { recursive: true }); + writeFileSync(file, `${JSON.stringify(merged, null, 2)}\n`, 'utf8'); + } catch (error) { + logger.warn(`失败频次统计写入失败:${String(error)}`); + } +} + +function laterIso(a?: string, b?: string): string | undefined { + if (!a) return b; + if (!b) return a; + return a > b ? a : b; +} + +export function failureRateOf(stat: Pick): number { + return stat.runs > 0 ? stat.failed / stat.runs : 0; +} + +/** 是否「经常失败」:失败次数达标且失败率达标。 */ +export function isFrequentFailure(stat: CaseFailureStat, store: FailureStore): boolean { + return stat.failed >= store.frequentMinFails && failureRateOf(stat) >= store.frequentRate; +} + +/** + * 记录一次用例结果并更新累计统计(就地修改 store)。 + * @returns 更新后的该用例统计(无编号则返回 undefined)。 + */ +export function recordOutcome( + store: FailureStore, + input: { + title: string; + file: string; + status: TestInfo['status']; + failureClass?: string; + }, +): CaseFailureStat | undefined { + const id = extractCaseId(input.title); + if (!id) return undefined; + + const now = new Date().toISOString(); + const stat: CaseFailureStat = + store.items[id] ?? + { + id, + title: input.title, + file: input.file, + runs: 0, + passed: 0, + failed: 0, + consecutiveFails: 0, + }; + + // 跳过不计入执行;其余都算一次执行 + if (input.status !== 'skipped') { + stat.runs += 1; + } + if (input.status === 'passed') { + stat.passed += 1; + stat.consecutiveFails = 0; + } else if (input.status === 'failed' || input.status === 'timedOut') { + stat.failed += 1; + stat.consecutiveFails += 1; + stat.lastFailedAt = now; + stat.firstFailedAt ??= now; + if (input.failureClass) stat.lastClass = input.failureClass; + } + + // 标题/文件可能微调,始终记录最新 + stat.title = input.title; + stat.file = input.file; + store.items[id] = stat; + return stat; +} + +/** 把频次统计写进 annotation,报告与 JUnit 里可检索。 */ +export function annotateFailureFrequency(testInfo: TestInfo, stat: CaseFailureStat, store: FailureStore): void { + const rate = failureRateOf(stat); + const frequent = isFrequentFailure(stat, store); + const push = (type: string, description: string): void => { + if (testInfo.annotations.some((item) => item.type === type && item.description === description)) return; + testInfo.annotations.push({ type, description }); + }; + push('failureRuns', `${stat.failed}/${stat.runs}`); + push('failureRate', rate.toFixed(2)); + if (stat.consecutiveFails >= 2) push('consecutiveFails', String(stat.consecutiveFails)); + if (frequent) push('frequentFailure', `经常失败:${stat.failed}/${stat.runs} (${(rate * 100).toFixed(0)}%)`); +} + +export type FrequentFailureRow = CaseFailureStat & { rate: number }; + +/** 列出经常失败的用例,按失败次数、失败率排序。 */ +export function listFrequentFailures(store: FailureStore): FrequentFailureRow[] { + return Object.values(store.items) + .filter((stat) => isFrequentFailure(stat, store)) + .map((stat) => ({ ...stat, rate: failureRateOf(stat) })) + .sort((a, b) => b.failed - a.failed || b.rate - a.rate); +} + +/** 生成 markdown 榜单,写入 reports/flake/frequent-failures.md。 */ +export function formatFrequentFailuresMarkdown(store: FailureStore): string { + const rows = listFrequentFailures(store); + const lines = [ + '# 经常失败的用例', + '', + `- 生成: ${new Date().toISOString()}`, + `- 判定: 失败 ≥ ${store.frequentMinFails} 次且失败率 ≥ ${store.frequentRate}`, + `- 累计用例数: ${Object.keys(store.items).length}`, + `- 达标条目: ${rows.length}`, + '', + '| 编号 | 失败/执行 | 失败率 | 连续失败 | 最近分类 | 最近失败时间 | 用例 |', + '|------|-----------|--------|----------|----------|--------------|------|', + ...(rows.length + ? rows.map( + (r) => + `| ${r.id} | ${r.failed}/${r.runs} | ${(r.rate * 100).toFixed(0)}% | ${r.consecutiveFails} | ${ + r.lastClass ?? '-' + } | ${r.lastFailedAt?.slice(0, 19).replace('T', ' ') ?? '-'} | ${r.title} |`, + ) + : ['| - | - | - | - | - | - | 暂无(需多次构建累计) |']), + '', + ]; + return lines.join('\n'); +} + +export function writeFrequentFailuresReport(store: FailureStore = loadFailureStore()): string { + const file = path.join(path.dirname(storePath()), 'frequent-failures.md'); + try { + mkdirSync(path.dirname(file), { recursive: true }); + writeFileSync(file, formatFrequentFailuresMarkdown(store), 'utf8'); + } catch (error) { + logger.warn(`经常失败榜单写入失败:${String(error)}`); + } + return file; +} + +/** + * afterEach 调用:按当前用例结果**预估**统计并打 annotation(只读,不落盘)。 + * 落盘由 reporter(主进程,覆盖更全)负责;这里预估是为了让 annotation 反映本次结果, + * 供 HTML/Allure/JUnit 即时筛选,不依赖 reporter 的执行时机。 + */ +export function annotateFailureFrequencyForTest( + testInfo: TestInfo, +): CaseFailureStat | undefined { + const id = extractCaseId(testInfo.title); + if (!id) return undefined; + const store = loadFailureStore(); + // 在内存副本上叠加本次结果,仅用于标注,不写盘,避免与 reporter 重复计数。 + const projected = recordOutcome(store, { + title: testInfo.title, + file: testInfo.file, + status: testInfo.status, + failureClass: testInfo.annotations.find((item) => item.type === 'failureClass')?.description, + }); + if (!projected) return undefined; + annotateFailureFrequency(testInfo, projected, store); + return projected; +} + +/** 刷新 markdown 榜单(CI 收尾调用,或 reporter onEnd 调用)。 */ +export function refreshFrequentFailuresReport(): string { + return writeFrequentFailuresReport(loadFailureStore()); +} diff --git a/core/heal-locator.ts b/core/heal-locator.ts index a645169..2cc591a 100644 --- a/core/heal-locator.ts +++ b/core/heal-locator.ts @@ -41,11 +41,13 @@ export async function runWithLocatorHeal( options: RetryActionOptions & { label: string }, ): Promise { const { label, ...retry } = options; + // 非幂等写操作:主定位失败后可能已部分触发副作用,不再尝试 fallback,避免重复触发。 + const writeRisk = retry.operation === 'write' && !retry.idempotent; try { await retryAction(() => action(primary), { ...retry, label }); return; } catch (primaryError) { - if (!fallbacks?.length) throw primaryError; + if (!fallbacks?.length || writeRisk) throw primaryError; for (let i = 0; i < fallbacks.length; i += 1) { try { await retryAction(() => action(fallbacks[i]), { diff --git a/core/index.ts b/core/index.ts index 331ebb5..de3ae23 100644 --- a/core/index.ts +++ b/core/index.ts @@ -4,6 +4,8 @@ export { isTransientActionError, retryAction, shouldRetryAction, + type RetryActionOptions, + type RetryOperation, } from './action-retry'; export { FLAKY_TAG, resolveFlakyRetries } from './flaky'; export { @@ -15,6 +17,21 @@ export { type FailureClassification, } from './failure-classify'; export { applyQuarantine, loadQuarantine, matchQuarantine, QUARANTINE_TAG } from './quarantine'; +export { + annotateFailureFrequency, + annotateFailureFrequencyForTest, + failureRateOf, + formatFrequentFailuresMarkdown, + isFrequentFailure, + listFrequentFailures, + loadFailureStore, + mergeAndSaveFailureStore, + recordOutcome, + refreshFrequentFailuresReport, + type CaseFailureStat, + type FailureStore, + type FrequentFailureRow, +} from './failure-tracker'; export { collectHealSuggestions, runWithLocatorHeal } from './heal-locator'; export { logger } from './logger'; export { applyCaseMeta, caseTitle, extractCaseId } from './case-meta'; diff --git a/core/quarantine.ts b/core/quarantine.ts index c3c417b..1a05cf9 100644 --- a/core/quarantine.ts +++ b/core/quarantine.ts @@ -24,6 +24,8 @@ export type QuarantineItem = { since: string; ticket?: string; category?: FailureClass; + /** 由 scripts/sync-quarantine.mjs 自动写入(区别于人工维护项)。 */ + auto?: boolean; }; export type QuarantineFile = { diff --git a/data/admit.data.ts b/data/admit.data.ts new file mode 100644 index 0000000..521f1c3 --- /dev/null +++ b/data/admit.data.ts @@ -0,0 +1,29 @@ +/** 基础管理-准入管理:路径、菜单、CSV 反例 mock。正例登录走 auth.setup(test_1)。 */ + +export const admitData = { + materialPath: '/spd/basic/dataSync/dataSync/productApply/ZRlist', + productApplyPath: '/spd/basic/dataSync/dataSync/productApply/list', + materialListPath: '/spd/basic/dataSync/dataSync/productOrder/zrlist', + productListPath: '/spd/basic/dataSync/dataSync/productOrder/list', + supplyPath: '/spd/basic/dataSync/dataSync/supplyApply/list', + taskPath: '/spd/basic/dataSync/dataSync/accessKind/list', + menuBasic: '基础设置', + menuAdmit: '准入管理', + menuMaterial: '物料准入管理', + menuProductApply: '产品准入申请', + menuMaterialList: '物料准入单列表', + menuProductList: '产品准入单列表', + menuSupply: '供货目录申请', + menuTask: '准入任务分配', + mockGoods: '__NOT_EXIST_GOODS__99999', + mockCert: '__NOT_EXIST_CERT__99999', + mockOrder: '__NOT_EXIST_ORDER__99999', +} as const; + +export function uniqProductName(): string { + return `AUTO-PAP-${Date.now()}`; +} + +export function uniqRemark(): string { + return `AUTO-SUP-${Date.now()}`; +} diff --git a/data/basic-hospital.data.ts b/data/basic-hospital.data.ts new file mode 100644 index 0000000..a20afa4 --- /dev/null +++ b/data/basic-hospital.data.ts @@ -0,0 +1,40 @@ +/** 基础管理-医院资料:路径、菜单、CSV 反例 mock。正例登录走 auth.setup(test_1)。 */ + +export const basicHospitalData = { + noticePath: '/spd/basic/hospital/paltform/notice', + hospitalInfoPath: '/spd/basic/hospital/paltform/info', + financeMonthPath: '/spd/basic/hospital/balance/month', + branchPath: '/spd/basic/hospital/paltform/area', + menuBasic: '基础设置', + menuHospital: '医院资料', + menuNotice: '公告管理', + menuHospitalInfo: '医院信息登记', + menuFinanceMonth: '财务月份设置', + menuBranch: '院区管理', + mockNoticeTitle: '__NOT_EXIST_NOTICE__99999', + mockNoticeBody: '__MOCK_NOTICE_BODY__', + mockEmptyContentTitle: '__MOCK_EMPTY_CONTENT_TITLE__', + mockCampusName: '__NOT_EXIST_CAMPUS__99999', + mockCampusNoRegion: '__MOCK_CAMPUS_NO_REGION__', + mockAddr: '__MOCK_ADDR__', + mockCampusNoAddr: '__MOCK_CAMPUS_NO_ADDR__', + mockCampusId: '__MOCK_CAMPUS_ID__', + mockOutOfRangeDate: '1999-01-01', + writeDisabledReason: '环境禁止写:SKIP_BASIC_WRITE=1', +} as const; + +export function isBasicWriteAllowed(): boolean { + return process.env.SKIP_BASIC_WRITE !== '1'; +} + +export function uniqNoticeTitle(): string { + return `AUTO-NTC-${Date.now()}`; +} + +export function uniqLinkman(): string { + return `AUTO-HOS-${Date.now()}`; +} + +export function uniqCampusName(): string { + return `AUTO-BRN-${Date.now()}`; +} diff --git a/data/flow.data.ts b/data/flow.data.ts new file mode 100644 index 0000000..f6787e9 --- /dev/null +++ b/data/flow.data.ts @@ -0,0 +1,17 @@ +/** 基础管理-流程设置:路径、菜单、CSV 反例 mock。正例登录走 auth.setup(test_1)。 */ + +export const flowData = { + defPath: '/spd/basic/flowSet/workflow/flowSet', + instPath: '/spd/basic/flowSet/workflow/flowCase', + menuBasic: '基础设置', + menuGroup: '流程设置', + menuDef: '审批流程设置', + menuInst: '审批流实例', + mockFlow: '__NOT_EXIST_FLOW__99999', + prefixType: { + PLAN: '采购计划', + DB: '二级库调拨', + GC: '产品信息变更', + BUY: '请领单', + } as const, +} as const; diff --git a/data/surgery.data.ts b/data/surgery.data.ts new file mode 100644 index 0000000..6447d63 --- /dev/null +++ b/data/surgery.data.ts @@ -0,0 +1,56 @@ +/** + * 基础管理-手术套包设置:路径、菜单、URL 片段与 CSV 反例 mock。 + * 正例登录走 auth.setup 写入的 storageState,用例内不重复登录、不写明文凭据。 + * 严格对齐 testcase/基础管理-手术套包设置-*-UI自动化测试用例-01.csv。 + */ + +export const surgeryData = { + basePkgPath: '/spd/basic/operationSetting/baseMaintenance', + operTypePath: '/spd/basic/operationSetting/typeMaintenance', + stdPkgPath: '/spd/basic/operationSetting/standardMaintenance', + + basePkgPathPart: '/spd/basic/operationSetting/baseMaintenance', + operTypePathPart: '/spd/basic/operationSetting/typeMaintenance', + stdPkgPathPart: '/spd/basic/operationSetting/standardMaintenance', + + menuBasic: '基础设置', + menuSurgery: '手术套包设置', + menuBasePkg: '基础包维护', + menuOperType: '术种维护', + menuStdPkg: '标准套包维护', + + // CSV 反例 mock(不依赖真实业务数据) + mockBaseKeyword: '__NOT_EXIST_BASE__99999', + mockOperKeyword: '__NOT_EXIST_OPER__99999', + mockStdKeyword: '__NOT_EXIST_STD__99999', + + /** 术种维护状态 Tab(CSV SURG-TYPE-011/024/025) */ + statusAll: '全部', + statusDisabled: '停用', + statusUnclassified: '未建分类', +} as const; + +/** 唯一名称/编码,避免用例间数据污染(CSV 前置 ${uniqPkgName} 等)。 */ +export function uniqStamp(prefix: string): string { + return `${prefix}-${Date.now()}-${Math.floor(Math.random() * 1000)}`; +} + +export function uniqBasePkgName(): string { + return uniqStamp('AUTO-BASE'); +} + +export function uniqBasePkgCode(): string { + return uniqStamp('AB'); +} + +export function uniqOperName(): string { + return uniqStamp('AUTO-OPER'); +} + +export function uniqStdPkgName(): string { + return uniqStamp('AUTO-STD'); +} + +export function uniqCategoryName(): string { + return uniqStamp('AUTO-CATE'); +} diff --git a/data/user.data.ts b/data/user.data.ts new file mode 100644 index 0000000..efb6293 --- /dev/null +++ b/data/user.data.ts @@ -0,0 +1,19 @@ +/** 基础管理-用户管理:路径、菜单、CSV 反例 mock。正例登录走 auth.setup(test_1)。 */ + +export const userData = { + userPath: '/spd/basic/user/platform/person', + unionPath: '/spd/basic/user/platform/personCom', + rolePath: '/spd/basic/user/platform/role', + menuBasic: '基础设置', + menuUserGroup: '用户管理', + menuUser: '用户管理', + menuUnion: '医共体用户管理', + menuRole: '角色管理', + positiveUser: 'test_1', + mockUser: '__NOT_EXIST_USER__99999', + mockRole: '__NOT_EXIST_ROLE__99999', +} as const; + +export function uniqLogin(): string { + return `auto.usr.${Date.now()}`; +} diff --git a/data/vendor.data.ts b/data/vendor.data.ts new file mode 100644 index 0000000..b951c06 --- /dev/null +++ b/data/vendor.data.ts @@ -0,0 +1,29 @@ +/** 基础管理-厂商管理:路径、菜单、CSV 反例 mock。正例登录走 auth.setup(test_1)。 */ + +export const vendorData = { + queryPath: '/spd/basic/mfrs/paltform/mfrsName/factory', + qualPath: '/spd/basic/mfrs/basic/qcManage', + authFirstPath: '/spd/basic/mfrs/paltform/mfrsName/authAudit', + authFinalPath: '/spd/basic/mfrs/paltform/mfrsName/authAuditFinal', + certFirstPath: '/spd/basic/mfrs/paltform/mfrsName/regCertAudit', + certFinalPath: '/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal', + certDiffPath: '/spd/basic/mfrs/scjs/yc/certCompare', + evalSchemePath: '/spd/basic/mfrs/paltform/mfrsName/evaluationPlan', + evalIndicatorPath: '/spd/basic/mfrs/paltform/mfrsName/indicators', + holidayPath: '/spd/basic/mfrs/basic/prov/holiday', + menuBasic: '基础设置', + menuVendor: '厂商管理', + menuQuery: '厂商查询', + menuQual: '供应商资质管理', + mockMfrsName: '__NOT_EXIST_MFRS__99999', + mockSupplier: '__NOT_EXIST_SUPPLIER__99999', + mockOrder: '__NOT_EXIST_ORDER__99999', + menuAuthFirst: '授权变更初审', + menuAuthFinal: '授权变更终审', + menuCertFirst: '注册证变更初审', + menuCertFinal: '注册证变更终审', + menuCertDiff: '注册证差异对比', + menuEvalScheme: '评价方案维护', + menuEvalIndicator: '评价指标维护', + menuHoliday: '供应商节假日管理', +} as const; diff --git a/data/warehouse.data.ts b/data/warehouse.data.ts new file mode 100644 index 0000000..852f775 --- /dev/null +++ b/data/warehouse.data.ts @@ -0,0 +1,12 @@ +/** 基础管理-库房管理:路径、菜单、CSV 反例 mock。正例登录走 auth.setup(test_1)。 */ + +export const warehouseData = { + zonePath: '/spd/basic/storehouse/platform/storehouse/area', + probePath: '/spd/basic/storehouse/storehouse/probe', + menuBasic: '基础设置', + menuGroup: '库房管理', + menuZone: '库区设置', + menuProbe: '探针绑定', + mockZone: '__NOT_EXIST_ZONE__99999', + mockProbe: '__NOT_EXIST_PROBE__99999', +} as const; diff --git a/docs/flake.md b/docs/flake.md index ba0dff7..7fd6abd 100644 --- a/docs/flake.md +++ b/docs/flake.md @@ -27,8 +27,9 @@ 1. 跑 CI,报告写入 `reports/flake/history.jsonl`(gitignore) 2. `npm run flake:report` → `reports/flake/latest.md` -3. 同一用例**有过有挂**才算 flake;失败率 ≥ 0.3 且有效次数 ≥ 4 列为隔离候选 -4. 只把 **transient / environment** 候选写入 `config/quarantine.json`: +3. **`npm run quarantine:sync`** → 把候选合并进 `config/quarantine.json`(预览);确认后 `npm run quarantine:apply` 落库 +4. 同一用例**有过有挂**才算 flake;失败率 ≥ 0.3 且有效次数 ≥ 4 列为隔离候选 +5. 只把 **transient / environment / script / data** 候选写入 `config/quarantine.json`: ```json { @@ -41,12 +42,13 @@ } ``` +- **自动同步(`scripts/sync-quarantine.mjs`)**:只隔离 `environment / transient / script / data`;`product`(断言/业务缺陷)**绝不自动隔离**;标题无 `STAT-` 编号的候选跳过;已有人工条目(带 ticket/owner)**保留且不被覆盖**;自动项带 `auto: true`,可重复执行(幂等)。 - `quarantine`:Jenkins / `CI=1` 默认 skip;本地仍跑 - `observe`:照常执行,只打标 - `npm run test:quarantine`:只跑清单项,验证是否还抖 - `QUARANTINE_POLICY=never`:主门禁也不 skip -出清单条件:根因已记、有 owner、不是断言失败。修稳后删条目。 +出清单条件:根因已记、有 owner、不是断言失败。修稳后删条目(自动项重新同步会自动回收)。 ## 自愈定位 @@ -56,12 +58,70 @@ | 手段 | 默认 | 断言失败 | |------|------|----------| -| `ACTION_RETRIES`(操作层) | 3 | 不重试 | +| `ACTION_RETRIES`(操作层,读操作) | 3 | 不重试 | +| 写操作(`{ operation: 'write' }`) | **1(不重试)** | 不重试 | +| `ACTION_RETRY_BUDGET_MS`(单次操作总预算) | 45_000 | — | | `PW_RETRIES`(整条用例) | 0 | 会重试,不要开 | | `testFlaky` / `PW_FLAKY_RETRIES` | 按需 1 | 不重试 | +### 写操作幂等安全 + +导出、提交、删除、审批、打印等**会产生副作用**的点击,重试前无法确认服务端是否已执行,盲目重试会重复触发。 +因此 `BasePage.click` / `fill` 支持 `{ operation: 'write' }`:默认只尝试 1 次,且主定位失败后**不再尝试 fallback**。 + +```ts +// 查询/打开面板等读操作:默认走 3 次重试 +await this.click(queryBtn, '点击查询'); + +// 导出/打印/提交等写操作:只点一次,不重试、不 fallback +await this.click(exportBtn, '点击导出', { operation: 'write' }); + +// 业务确认重复执行安全(如按单据号覆盖式保存)才显式声明幂等 +await this.click(saveBtn, '保存', { operation: 'write', idempotent: true }); +``` + +### 超时总预算 + +`retryAction` 的 `deadlineMs`(默认 `ACTION_RETRY_BUDGET_MS=45_000`)限制**单次操作**的总耗时,含首次尝试与退避等待;到点即停并抛出最后一次错误。 +避免 `attempts × 单次超时` 在慢页面下叠成几十秒慢失败。 + +```ts +await retryAction(() => action(), { label: '慢接口', deadlineMs: 30_000 }); +``` + +## 失败频次统计(经常失败的用例) + +目标:回答「**哪些用例经常挂**」,而不是只看单次红绿。区别于 flake(有过有挂),此处**只累计失败**,跨构建持久化。 + +原理: + +1. 每条用例 `afterEach` 按 `STAT-` 编号累加 `runs/passed/failed/consecutiveFails`,写入 `reports/flake/failure-counts.json`(跨构建累计,重启不清零)。 +2. 失败次数 ≥ `FREQ_FAIL_MIN`(默认 2)**且** 失败率 ≥ `FREQ_FAIL_RATE`(默认 0.2)判定为「经常失败」。 +3. 给用例追加 annotation,报告可检索、可筛: + + | annotation | 含义 | + |------------|------| + | `failureRuns` | `失败/执行`,如 `4/5` | + | `failureRate` | 失败率,如 `0.80` | + | `consecutiveFails` | 连续失败次数(通过即归零) | + | `frequentFailure` | 达标标记(经常失败) | + +4. 榜单与命令: + +```bash +npm run failures:report # 表格:经常失败的用例 top30 +node scripts/failure-report.mjs --json # 机器可读,供看板/流水线消费 +node scripts/failure-report.mjs --min-fails=3 --rate=0.3 --top=50 +``` + +- 累计与落盘在 worker 的 `afterEach`(`mergeAndSaveFailureStore` 多 worker 合并安全);`reporters/failure-trend.reporter.ts` 在主进程 `onEnd` 刷新榜单并在日志打印命中项。 +- Jenkins 邮件摘要(`scripts/ci-email-summary.mjs`)自动追加「经常失败的用例」小节。 +- 编号以外的用例(无 `STAT-`)不参与统计。 + ## 产物 - `reports/latest/flake/latest.md` — 趋势与候选 - `reports/latest/flake/run.json` — 当次分类计数 - `reports/latest/flake/history.jsonl` — 当次明细(并追加到 `reports/flake/history.jsonl`) +- `reports/flake/failure-counts.json` — 跨构建失败频次累计(经常失败判定依据) +- `reports/flake/frequent-failures.md` — 经常失败用例榜单(每次运行刷新) diff --git "a/docs/\345\237\272\347\241\200\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\347\224\250\344\276\213\346\261\207\346\212\245.xlsx" "b/docs/\345\237\272\347\241\200\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\347\224\250\344\276\213\346\261\207\346\212\245.xlsx" new file mode 100644 index 0000000000000000000000000000000000000000..9b885f1a088fea4dd39b16661df8767d68e2dbe3 GIT binary patch literal 19364 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

5){tpE@XCgc5gZy1_0DlS_lcBQ*L#& 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

(So%t*;NAA}AH0_4ciB2LlW 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�%$?@E+pr?p1x|!K&Qe$mm9)$ed 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

Ton1k`^l(D7Pqtr5)!}w&NPLwdFOXX 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

! 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

)2y#%@hz9#E; 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

{SK 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 ^CX)?qdB2-E^Jn*5rD103>Chn+!1CI65 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

&{z)LMT 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

1i-}rt%pYwB$R?H1S4?P=m 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

OcSH70j6aQZd%m-G=x9mbAa8 z-~Hk)E*k_}beqsk!tP_JxH4{4%M|+enywmeOyk9-{*_;_NBVoj{xGAbRN`R&|6}Vr z;IVA`_b(#KOoL=4kw`>QGAaobl`^u^AlaL&Y>`Srgp!QRWUuUqj7auK*(HaiXVqvcLcJJMqZx`|`1&q8P$2yp^sNh9y*p779`z!a zGXj0;Zu4++uX&s`-oU8+R>gX?Qb9pR=Z|LTqVNii8Ev5?X4K7o>`0)_bAqbuZEfZ(XjgUcrz34i$ zpwpTB;KO+XWj)H_0$3-3bg3{UbLs%5;iwHxh>K&u*IrwD4+N8;Ks)GmP;|ga9@BrQ z+Z(E?;y%0a`-E*pV5ZI<1Kc!>E^+hiD1j37;XuR?ANliPM~$FVJc}M=ojoq=FH3ZznHK?D9C#TK$^;(!aTeO3YLuHe zH#B=vK743yXn0(4BSVd!!Pg^24amT>t@PQthSqahyHUHKwD46o1gu{3p9P2%9 zWl+rB`w)7R%;;jK(bM2y>*1#T{S|j?A(oXx0>}3f(zC0`x|^oq z2m7t7s}O?(4h~|S20*%@2=hxtj1ldY=MEj@_wn*Pbl||HD_35Knrcf+e+1rz>D>-` zdb6?bLcr!w?^94<<|hfsQvbPdwtK3YYWym8b_;mQ5A4`ll)Hh?f<0_-z!xja8)-00 zOPev(1WAR|kngJK}_LVl~JtPaBQ?xBy`}X}996T4( zh}Iu+OPBi^RSmDsTy~4BhrM+>HY)&AKxoLw$)`PR1V9}cbYjQmyk?s6?j1rM{k-&f zex0+rbL#5DQ<&t8uF+)cmXO|O5sG;5pcE`7TyXSmxnJ^EDBeQ*qX;5F{oU=8ck*U@ z!Ioe5#bXK{9q7x+iHXbzLRE5Z*ih4C8in8<*N9|+PswvOzdI@{Y-DPh zou0k{5#u&(ToX`x@3tJoX5s4UiYIjyWr3#tcXyZlH%go%&?sYh;W)z-9n8?D`?csE zuAIesK)ZjgwFKjFoMS`NU{Fp~^>hF&4mqUp?dSROZL`4RcOBM_z@{-WxiJ!nrlVzd zw8hz1KBOj^5Fbyy=NSL7V>V#!Se?N9Iu{w9U%r?i*Qh+L3%dhgPGI178O?2b>iSXK?A1vNdl~b@YQgX=J^`Ew{VX!_jI0)7X&Hn?&Nlb{F$ z92Ve}_x15vxDgN584(zHKP3MWmQ#G4E6WyRL(c+djK#VRqqW`haCbDM{aHPJpHQC;DFH;P0fvV0VoGrGEgPA zfcKn{A&yc3;5_=+3>1ublD6&hO4TxggNIB~!MBS5$gSg19D-@?1 zgp2S7ZC+%i$rZm<^9+{&fY|F!d12v^BYw)u!8qpBy_^x1nC3|UYv>=)b5T%Gz@hdwo-lxu z@$&9KKv>fy>s`D;QI9YqiBN5AjG%19C3h~8@k&MpC9e%4T1M`S!t>B`l{`UBoFpkDJdOH@|2VU z>l4&lo;z37oS+N|J{1)_T27c}pC%W=V+q+u(6eW65)#@VzYhqY86RgoAgQ?0wwoFy zyC~LeZRS<?ATwC%5 z+0&>f(4BN|F!f{a!u>1%(t7V$eoBf7?)n98;k|oF0%q$~*UU*no07Mr%>4fS@5fNu!whcnS9}PdChSf< zrWYUM1VL|&IL!z(yKOWQ8@rW*)g$Jkelncb0I%=br5JuJV4nMhVjNV5&&&EiOfWDo zFtfgeuB)xB4P_51U}HnWz2CG^s^B^7={85g9ct@ZNKrnUtplD0Zv04lWN63^2P3+O z={nHA5<$KRxIA|)nY;%$<;3R@U#-mtV3VC5rOzKv&FY1_{957 zZx2w9j2v>TV-%OSic4e&;lAFwNg@cI6ocMYFNx^of9TuqOW`x(nsYs3Z5+iBP0M!j z1IpQyz5h_mFOikEe{^9j|F=fHLBsiG5>1)dO{UEM)UiWB|D;++oRjT81m3QU5O7QX zO;PS9^Wg8A@BfbqoyO^J3U31+IVB-;;a0m%xGi-(MRV z{iVOX1Yh*uO${EnoB#D4*Y2u5GZf&4BJJL`AZ}}-lhf-mH@2HLJOIMI_|(Lt z^7YKDEL|p>AF}1po8;XuTgb^-S~>wmJ`Ih_Y9?|&(c(uviPuOBe)D8HSaxA=vBmu0I!{vJ_IhXRvGwh5q1IuQZYH=Pe0^b33ldD@VL7raOE zXC#RgLZ?%6#-hi;AtyUC>;42Q>g?$mISnJDi>f|%I8VGRx-*}G_HOoB-HqUB-Ski` z=WpZj@u-s_zbP*l&;h*s=a04KI>x;(nD_XuOU`EyQ$c1OgUmML%u%~R zf|}?o3efwGD@Mh*(pcV^ZAv?>IgC;4t=qR>S0U_)oP4oh9T{(d_3LB9L1_6Peg`AH zenKm%MdSHe+ zKW*hU`rhmxwpLb@TPe+)cy~RNFU=|D^{%^Z`Mum$KSQlzf5qw3r9dj*tm{wK{z$mn zO-RnShlIZz(2k+n{7@x{h{9}9@uD+$W0t?Z2n!F24c68Q*UwP9eY|9W+{TdihyAwtd32Y`sHj|h z99|8MvqZSJo|^gPZQ>O93v$!ix0dq+n1f&FT;kZb4_>>|XO13Wym>{x_+o{D$iaZh zDHpe^*Hqr8XKbOQoV%{CRFaaS?J%#+{^=D-OylgV&9jh@%*5?h^W+w@7W~N zGjP1&;p#MARscODtTQ4iipqsm_;wOxdiwhN?mPDiE-5tp1d$ZC14c3>GN{O=rqYVn zm^UfOb6wFfoff>Jd4-?*m<7l2{1f7vNDo8m$9m%ao56ECiDBZ z#+wf;SZ;DfdQ$Cjo%UjIim@Fe4Ji2GUNn~Z@J(E6$M2XxKB5=o1+&FL6rBiG+rK|* z*l7203odpkDgL&KHs2_nk7Z>+fl*R0`uqGjqt&a>If$0-_snmg7lXY+WJ<`mR!LFg z2Gt7z=Q@M$WTa;Z za~9EVfQYU&CHKY#!J-3;^5?Q^tcA`@)RTf_H^YfA<4e|0v;Ak#?YM;wSTFg|gdA9rVF(Gc&b-@o52#C?eYq`s~>V~ve4nTZwh zrP;-qqvbN^HtQ5BNJ-Dn%{feQ2niJBi{D@38vaPK-Hw0n-o8;s6VG+#IoU z=gyXLj|+9@->6V1hydRW-v5{=mnbV8o&?vjzqqoZa%u79VLm?hio9I=hI%F;=0nE> z<~&{|zwRGOn=)HE=Ix~|Wjn#Ya&9HVaAxn`w(sB3q~E!9E7yKT%FXQ*#@VY`-k_A8er0B6CYCQYIz0Y;p`IJVbVOYE zOqro)?6KKHFBKRX8VbHX9}0Xvjk|Xh9F{bMXJRlX!uS=7bL&<=>V3~2kG2Ls3&z-5 zS@{aOw6p79gWj0@`8xbn^6tpU8An!sT@jdd@i=YGeAwR7QjvBQ9a6WY9QQsxG`cp+ zS;-6xPs5LyPN>@Dlw=Z`V&gf9~A7E-@b97V(py_|X8aQ69*2$I+RUuKZ@9>9=g*&Wb39kBXiL*f3Q(PbQ`2Swmw7>lT2QDB{RSix2rPvf^dcQ=q~!QyTy_0@h=SRqw9)cD zDnbcUA0|d?wGjycKJf}EX#=NY328v2D&w|o6a8aWYY%#I%oUdB znCBBsW@hriiU^E?Ad4UvCVL#G3neDEI8vNC^$MvBc-7=&enx@oI~e!mJJh`Z8Q$F7 zf@!=xU{K^pGlr!*NvAu|S<>ZLna8bV&znb9p7c6-_-oV1T_ba?j*pGn??1$3d~N!z zcG0-*`5}$W5dP%Er0==Prp486XxQ?>P-t&==&aoA%qPdygj{WEYMY5}A zK}NR;OEUkbl4_0?PrdQx6#E-bEeg7Nw1WoeKD@a(=qFD~h=pGFSQ*u=5Ts7%p!Z?# zFY}&1S63IU;OVwU^mM8=jigy`^oyl2Y!X$*NtnYR3DP^q^PS3YZEI*x_bGem8LX}j z02hI@Gbbn4e7>+K2eJx+%YN;?Zw) zKpFP#ZEZU)rWiFRY4>s9_PvuIxY79*CXbuHY%x+9O?ume0X+{Mo{8?<&lPo>2+-{; ztkRSeoomj=6}cg7S>mU|@2HF6s!3{#OSfKx=Z8l`bRu4JDfx*n#|(3rXz=M*nzQp% zlsV1Hf;{4**zNGj-%`}SHn#m7?klqX{B~eqP_wi10@)Us{$b%FkrQQq>Z8shx{xOJ zMm7%^R3}dG!EmH15EfoyVi60=lL=;X-x1=cXMS%i#~icm(wUhlGx7W!;3DterJfBqGmN6JtAL`aHfzAKrHlL*jclX57p zsuDH)eUs-7<3s&(*s!=2BbtqNnoLAOA@Al@&AmL2D#fljzEP_E3zVn;l3x~2U6ik? zt_XfM`%JR_1#HEWuIE|}AL0JlS-c({H+>y8OO*x3rP|;0$lM#NqZ+J-a_PcFr@4IF zuw7M;9PAy`uB)GM3Wy0h&y2&q15kQDDf8xxxeu+cd@eGx>t~T$57;8W^@`cxeI)j4G)wWzpc!{xUw{a zprFW!yC$2HCFxitOHXs*pgy&!<4J#i2&sUhy#tz%d+i_6@AP$K8udhv`U~yWs zDl84%cTjmYlBfS?dGHRdYxhYAwM0eMqIbLRQGQCHaa%CQsU~4wT7Eu+6vNchQQBHs z`x8d8|53k`*_%hFs<BBrg=hd^5bca>(|vAs4ys!20e+f z)AYvcV&%g;JQRsLLn)6i-8p{ZJJfducLl~KhZh21f`obFehZ;$B+TVM%@Mr_>zulx;gD8Fkr|!I6$E zlNdd}A|tA>Z7rNg5!s>4tY7&%PkR#Z8k z9F`88)`wG4ZAzlLYt#ZBQIuP7q<=VsSj$r`l&x-c*Omd1nG1f++EdB=|_z5 zE1b)gN4}gaD$SE$m_=B)r+5%yZr8JeG6N!#rOMXufe$cG@X${R~R^NK^as;?^y+Ar(blFJ+MWQuwTN^Bw1Cw zbN!f>$aMbeZ6?d`7QQW7Ld}>b0B;qgk>nhdIayxx|(Ca+;fo=>3P- zwP&kulwJ+t?P3>j=do?ASv{(wxs?!nWGV)urZIwOQW~UUQm{k{ zFqB20l-#(n?@ry7GVhCJ6>w7`2>aaJ)sglq+Q=HY*Or%zN?SnS`Ki{o*~;rJpF?)- zswyqrcg~-wgaiKb37}C|sku=Is!@NqjNl^j72lUHouEF0yp<_HmV*H_pt90^1I2m# z>F?8e`vDz88l4i?c_Kg0&%zS;HHh1R>T3PL0@pt*#OEGo6s@ldWm>9)GWPyTsJ@<| zXH}7Uy2UkQy9^K3>sLOyb!DIBDej|l9aqWER^%*npe_e7OfSc}(s6$G_4tS2TV-Ze0w%wrjHoMZ8K z`pSoo^!a(}CAQS}W__f^RBKaS06k<#U*#n#XgCVh{u+0B5%ALel+dfVxQFaL`Rl!S z-m|i*;8=y5j!{dJMz*%hRhcWNpXx~WeoJ2HvH#8FHoiH5H@@ zohN%DoT9Z3-OqzkkRU7vZSu8o4#2`hSX}&_&VMZF>&+W$`XMr7*?v)9w+nvm9C<9@ zviBd$>{y+M2rV;qJqOy*(B3Lo-{Inu5@zP+h6c4ddG~Tr3b(hv(317)$Qtb*FG*$u zb|8R;T41l9Zgep?u>>X6QO9{J$Hm{)fJ^h2d9DAnQP8mr^zCc(sS%~Xw|DEoD&l#3fC5b>dES7qLK##W59oFLp5GBB;i z*Y%=%^4r6xX^y>Wdy(6w7y1fyR(FYc_0w3pU(3GpXFWVo`c3m6&kehvoqJ<9@o~aK zg7~=jCd19G@As|Gee^rn72hQ)x>6_wMVVX9Xl}{V{JjW;^HDyk)pg+0qXp)oI{guri}G!I3!e3m%f_TP8Hn_=Z%-w0 zTTqS8O|$(9!9_jc`i4hd;7eDd8GL5Q_6^e-Zz|p=fn=HJ=!W8i)3CWO_YDGB^SOY= zZR1<&8XD)N5`Q54J45_cOh!}Pc(d>JIqLVbDN5kkybZNQjUg4rvn7uR4ZD23vdjO?}myN2eZohf^&7Iq~1@8rs zUfTk<794e6npEn!7CUFpyDHwjzp^sH7Rk$dm`%!gV0ict7yteCj?V_IDePAw;3MZB zC(OnbR1HF|-X~>cjpbi|j5dc*0(x0G99&%Na#(1s<2pt6(;gbO$F$CcWhNddhfBV$ zJa_;qa}%ElhNjcYA7!QnHxo{DJfVNW3;k9UTIYCN8x87p3OGcMb>~^W%g6|lyt zsH3!^Zq+i-Xsj+Awe+MqV-X(}5gi}DpmHCqbBVA{V0${3E;r(bI{@D_Q)W0n-=Oi^8>oDEAzSdf|Q${cw^5lU~-?`10}EsZH-D9&4+1T`-gi{kD2)CiGYcA$?JUsyQ>Afil%9 z>z7FL+_m>7C#TM>Gwq-x!}K>_#9Tv1F%{6G-&jM6gN9c|;x&JM#X0mGM2L?`>bg zgjJO@Z27iOmnr5(f}B3so=GpJocjY)u zIu;SFG-7hBa`|s=90{NA5gDYDVwt)Vf@zz z(TK>pm&^Lk#g4bBZ`ooCB7e(dw{h{#gPEgE6%ViGg}UPQ>(mrQh_|w`EH(b9ni?Ky z@o}XjL`CZhJ@Q9eO~nwQ-c2hJAHMH>8a(z&c?GO&`+Ixo_p}vy@1uz+Nnng5J&TMp zbn32$Z{2DZxcf2oVOAE8^SXre%0VWt*x0uYj*h2R$A*g6I7Od~?iqoaZ1U%@5GYtU zeNW#J(Em}0w?;y2_VQ{tm4p2U3}8siAP>OFgZ2zwKC7nw*H(VF@a?~KYsn{UmR~2m zw4-AbFpc2B8_N3I3D>~DsMD{+PJB=8NxLkol7-z%D<}mYMaz z!`oA6z>qsAO5(iNF-@`SU|QPm!}%CzY)f-<@2L{#OPBNF`(DFmZECMFC{SIj zU5`9=-uiiCU-4)tXc_=5nfT)zwjN<8Fk)%sla}@K73vXuVPD*4YUtjQNwXgS(ek{t z_ni|CwWnQ+LvnWob#S`sNiF4&64*PYoz8FlJp)jwlD!w~gCEc1z!t$5${5-?8?z{O zs)zcer#U0FyBh>z;Dc0Id0jPoSx;B*mb_d}$Ew?rhNxr4d8w1?$e1$liFW0<+6>-n znaSs+9AMvt%X4Q;Z^_~Ks-zi>c8blX`0*tUBw>k=%RheHaO}=rd9Z>k$!+RBIVymp zBya2S@^u&=|2-P{gR*?G7XaMPpF0Ux|JqsZBge-oX+pIxseODL)ZPNF&n!GA3R%T_ z+c9;f_iZ3{)ys0w->;9EnHa&{FWT0iNb(N`cPnp3K(*qI?`Tn5Z z;WY=#j5Nhv49b3#<${%R{jGzC8HdtemKN`zE_+PJODbgWvE*GqGVAPhhMjv14R4JP z>FQilZ)-h0Yg$@A`5;PXUiS6KNJb!Q#*irYXS6OP1c>B0LCE?Oc2A)p(rzsJ@yN@A zbDZdOU{XMb*5A+1%67gyAhegk3IdU$0v%OVI)Z-3l0fMyvRe-&^G}KK6NI)lgJ$G| zN`9PiJX%V|YQ$qK|7kt!4-0fg{l?;9YCP60NJ`oR=V$mnUvzZM$RFE)UK8 ztl=n{#zxUwH!1E)jyrx+y=*?avM5!s`)lt5y=bVEqli+ceu7|JUA>)<;OlE$-`D3% zE@nJ#oTPJSW4er86FM3QgtyF3l*FG&N|MSAKEcBn5)q*ZepFq!5NL1h=buwcQwl`@ z*-HARP9FCa#TjhE9t;<7J$>WxCUJxeGdWEcE#y?0kHrThYt=kt1W!tUM@wJdw8(zd z0n2Z3(H>-7yi${CxzHHw|H3?D#UrUi!sMM_P%9-gPulQ0%}$X7O?tVy_5wIGsVtY) zg6ZhKF{h@lJt&H?@R@cqef%`KYuCBlU;a$$ZH1!j~@!S4mW5>?RaxdjA3R=q#+%;`*Be*M6pmg}Zl+z$h8oXHJbxJ2ig~x&nzr1f_U%v=WbfsMJYGR;g|i&P-B>IHySbAU2s)93#3@wf8+3mC>Z@9T>K%N zy&0(vnP+1OZ=Z8%R7Q4fRN3|1;%_ypF|kZ|u;@0pCE@X$_;?+-WSuQs z#F0dAV^2QLQH*wb~2sMAx%a{`i)$4clz!MTZ= zvv+KOlJN2Kk5+=q9jnAre%%ynSKwoTQ7R_Nb#O#QpQZR_96d+dz=P8q z^x!hl&`?jspOLB}t@E5Jq0M#Hw+mRK-{ck^Ue(_2+h{00!v@xo1>OPQIMlwJ$0=a%uHGq5zk-3 z-WM(gvIvPgcfv=Ys;$EQd+}9ze_70r6%?Hv_AeZq$e^1S@d+rbre}HVtn;XdER2o- zFOFp4n2Reb+4j)+@CyoiI#Y}dJ{pK9oTK<)KhXc{a<~Mnl>+cv!`V(;SaRMp;YQ7~ zS?;2BT~!2&-TmdcWc9mu#jOy}%FiEYZ|?v-M`+{|lRJ(NPBm`MjBgZ5#~6d=&mZ&Y zbZ@$F3k$Qig%|RjB|fzJ`E8}@DRAC&Gz~q>rD3m0&UQxlccKoqUtQYSS^1&$Buzj7 zF5#+n0(P_X>29d;wruAKV2x{j7k<$5^zJ#`R7x48W5(?s%a5N2yQD9j_*7f}xopPA z#}5m4d8%X+7R~tn^HKYG)(sBb!&g#Qiu1iqOni4?ysxIQXYuioOXmy^bBUJz;Iz;e z&m6VBIPXU)%GV*b)*WN7R(xN9W980Eesv$YRxfF4wjY&`$cg(av)d*(w#?VL^^JSy z?B93Jk81r>!**GBo8!ZIQ}43klQXYrIeja1+(MmqjBrJ2Ta0^*Z?&_*N6$DZm$ll; zN{7j!Rf&^Iw+KR1|8-q$VCh~hxlfsJ%Kaf62Fb@J_m`M3W~QgdM0r8Y;OFB5cP(9} z&G{c7OC|{a^$IfR#P;9%*)X~nP8nXy!ZK&ik`Q35hqZoB)zv{&NJ3ydVQyg#SG+Q1 zv2Ipf#z&0?8&1MkR3eil9f>w?A0DNr_lyI*yD7be!!k#|6+5o4HI%rUu(8c<=Q(O^ zEg+B*$Zm-`2#z#72M!GVn%GRFWoEL*iO>>{SH^Y|u7y=(y~9CVTjZ;q83#H#+DAr5 zqDSnJEH-Xz{46y*jB`z`xw(a1!ok4Eaz$b4=-$1jhPFjK{axDGIaazzWir9m;NasO z?%2qQtZWQdL?B#%D=|Sd^%ULmalUp<9c~G9G+e{iinE(A-MKvnGWtL6C zfdlp2e7UKq(e^#Z84kYevvE^ipKz*Q?o^6`Mfy>r=PzJ?i*?bOqz+D_0Po%{lgN?k z+SA+I(t@j_6v?^&P=KnN(b5uWdy~pl6$R=GqE!T&?&sE4?+}@$Ms=rsvw6?6U;-(x zqhmUr_s5%-^{viwi6FHuQvnZCz-CIag|0mh)+veJ70($Q)`N!^gvLz8@6H+!AKks_ z`ds4D(l8fn@lZ5v3E`L_`55SRRv!rKwf1N$<1_1T;mC`HPfnqud_~2Jx8USSvvp0c z#AQ}R>Ii#gX0yvv>K4CIZTS1!SYJ8-7${wT7x-I=iQ8YlzA<#>Thk`%BrKbeyCcrd zjHnc746HZIDXT`S4AB${ zQ5bpL{yqfxTV~uOkN?+m^;Ln#Au72AcU0BZq_D61IAhGB)G?hYsqZ-~AW2JHx~itB zqH=rA+SH@A*57(GUrQ`FxQdT2&;@>DGp`@Gry}Ev@4)Qu$EI!XJ;TE(%F>?JM`fFI zbT>B-{j3;zUq{HsY*SMMWVF+BHB2)X$w`p-pvp1;!ZtY8ar;(njA>EzH1LG290{O^>Y#f zLsGGLVEx0v0qOZR+j(WQaCmXlMIibJYLZ&HwrATa35T6DjjTj8jaxuP#kqrgfzUch z7&v_D|9<(fQef=o=c+2tG}JY~7w8qI5Gv_h83MxuOnlW8j9&k|!!#0YAr6kv)=!#` zw-h%u@8nZ~2+P4P=P7CbD~(^G$V%HooiR`R1Si8zD9Ua%eH6~Owsb;Clp!={Ulz1y@S zucGpsNrhi<@noKzy?tNJeH}9k^SZ?seIQi<^X(le(~M0^la`Zy{HfGp!6*0G8#1C- z{z}sTuX3kiuB_9a_R3d1^OlwVVGF9+hX)33k`R;Kx7H$3XcVP5V>mYnXrbEout|a- z6%q*)*TAL2j_Y%q{D!Bvz?rfobfgWJn7xdb2H6pKX*qZppC(DY@NWC;ZVB7o-7@e% z5?ERWzYb@!op&s#GeE@;CLWKYfA$VS{Fx;Aj~|PLP<$&5ne)!yFu!l$B?Oreo&WyE~%NguC;zTciE% zd|9xmAM`B;q#=*c+MrM|^2U2$LB;sz~_Y_gCl&|ITC zdF!?R2m3!|Ha_c|g4FsI$%6Eh<*Ak*2Z$$f9{D)&o*&ZDGc-IpTgSoR=#sES=7zfZ z?xH)#xQ;K{8w~w{rtV12!lCO)$YWof8xMz4ecE}lie&zk%NqY~b}w!oDNl-Dzs^ma zT*$d!R!I{Z8>N&R>G9U=5v`(~ZE#S~ru=u?ZF)HG9@ffQDfgXj;LFI(ZL4nV8)?za zk;;B}{&0$q#Jt^ZDea1;n)b=kMV~1_Z3e^qiAOs@ZF%$8Sd>Lq+0oe)OqlVJ*Go&0 zxtN{-vlBbP(QBb(SsM9($;&=Ua&`V$!q+0^4C=$aunrZEpx?y+Q}A1Y`Mv$Wrp@LH zc3ISQj-}--je%(gh9|tY=4g2?T%h&w^9c^aqUk?sK*@N)xo1CF|o&nhhFLo zlrnJUa4(l(@5z{wABwi>*vsHk&MH1T@N)pqsga&x=Iy$6q-xM;faj&ZEZ;%?{{7RXx({-C;6p_aIldGp-!?LxnW5dcJRjcN^6jI( z30vf>sHxO_J35I3Ob+3JdF$s-y)+x9enB9E6OULSZ|KN$q|=tebkgh3sDSp?kA+ke zM_^#_OQ=0leCYSjvk!Nzmwyt3TSQqG##t?Yp6zK)fXl>b>+r5E%MTe>5&t2EExd2v zxo92jR#mNo{%Yr9nuZkfm*Q`l`2;?6zrJOuo4)-IJ*9{KxkHMPR>$vu+F_z%+0+s+ z`cBc$w>O%O*dSjVIRNrS0Sup#T2G&5Wl{X-NGm$)A^AzL(ndJUUv)JmJN7Un_1MF%2#gKDGO2q_IF)o0tq-ma=fCtdU|a5>%$O%{*et!J+b5L!ngSuyG-^b@h3sm z^(rc4n~#jY?D-r^3W8(rUfR!bjqxJPn+Y-JPMd<7g~`KIwNa_3V&O$6K*}LaqdO6& zWGAzjl45EK?~CmI-nGJIg4p|teXi^*hclmwO4ZMuJ9MAPvaxl4_;B^)=67hK<9!Y| z{laY>I*-W8uNSzzwdv5a92FF~#~rk15A%r=;xjXrXJyXnS%v%={q`lQm}76BiT&B! z*x;kV3Y!SHuO~iKfQwld!j5_4v?(|ZAeP4{$f<96#85t}Nc?kH`fz|_&(68IS{xQTbz3przX;-=RpIiVmRRjcZ z=ZhroM4oi2EGyfNzskx={&F~LbmDt)4_QrCW)|Sxp#}3jONY+En*RAAEe1xH^QZg6 zg%PQ1cMZlpI4@I!=+k?V-;Otr9 zjC~@^>tt$J?J}eIq%Ja0_ z*8=t%IBuo7z+RBf%hJ2A4DKRIYfJ}szfF>O$JT2+JZfHH@BwPcC)F3v)z(7!_+xSs z-A4F}7l|T@!<~y0PO&gv*HX$udKAanw{gGUTQQ;idWv6T9TC_}PZ6p?EG&iHtU6;7!2RKHI3Bw+Dt$Nz7agc4% zU1108){e}P7b|nDI?pNLyaHow<9d^rXuc8@78veFq-D6BXO=i_;9&6id2;di=9OH{ zFSw1?V|zrONN$@5cs$pV62KPgBVE6n`C;rpZ+c++gT?+Mmj)EHZJdl|%3}9aEL-v{ z?WBy3IvW!;l)tAs+3Bh*M}vXL%~r34m<76_c6v&(SzA6{0XLynyQ42B@FwtP+)gM_ z_Lp5=5TY=NRlNpZ4t@B=2R(nHa_Q2|?lYi&d3tI0r0MOmJeM3XhLo{y)yiv|3ApM! zZa@38S2s$;zNT0;V=>A@Vf+DGIg=MS@Lvr?kgic&9H{y2TYLN0;v;-~=OKVu+l_7{ zAt{NTo<3T&bnSR~RRwzDzUlT*S3M>ZKXAm37_bOZpLbc_MMv+2*rpBFh1-9aPkO9; z%(YzJ6)cO0C;Qam?!2975SooNbT06$$Qg+mS2=I`klII_G9`eNaI`V~@isi$wB z@>$miJ(+SXI2h#HOY$$DL~38TQd;>17=?&Kpdxh{bngIxu)%Aebn$Y0pf7SAT#xhT zEh#)t$Kqh59*rzO-PW%s4}$|!4{d}fZdVPo*FSZt1WxY|&(=R)Tb#MWbZ?t?aPV1a zX?oky379V&I83q0HKZKIc2^;ay>cZ<(vi0N_fY82{`T{fGMp(#=y&a!;5))fI_*6L z>_6o2SBGo2k2beTIW0X4j51xC-HnOGSx-;QW#QA|iec>49XlB<&CMaxD%ZUeUw^S*hYml=;QQ&r|GY=C$FKkAd(@P~_!+wo^u8{c~e23X$b2~wxMF5E`HZVdlmulz0laZRN;ylA$19=U0~ zjq=m~=ObMzIQCzk=EkLugxB2Ov;Tc%$w~h|Z;*tPkomBM_g{DLAMZ`*00$cSzklUch(@4K+=-a;_r%*5||tX z^tHms0%-)aO_`jV!_fIk!I)SIygHb1j;Yol!U$em#kq#=Ww66S7TU4L5h(be3PTdi zCjKymSe&M^GBSeIU)$Q$VcH7c%8wtVy`Jh6*g3<|pok3i3ee|KbcaTUhKGaZu=YW0 zv*N7CTBwXXY^xH?7OeM!>a|Ao9)^a$D8uDujb`F)gFlQ&em~? zA|=$Go6V^I40(0;PlKBt6Q7qJ8L3{5#sldUJ{WLt(o-T3XX=?k>f1aH#l=x7*!&G8o?7D!Mq?d{vjo*td+*O`?}5k;=3h>d%Q7uz-LvGQPM40CW; zn8U`EAi&ndx09gs!*G(F-PF+V_=eOj=p-EUnqnVLE&Lf3MInCbRM+)Zl%>e^1tWz_ z3HE!qGLFxu(A1&Wq0bn|shkA~kw?-lXPPHN-UdQD?Z7mAF#?Zz#Uc3%iE)LPycsDR|JwlX+iC zXa+f`x3?E25{<3UAJkM-c#4XhJb53B0>7ScMdTRj;J(9uk~Okn!blnj@+GY8(7mF* zqp<1N90qPUn5}sBaLz>*FHVCjr<1)cgso>p~ZUpmir_U0E58oBckD9hcGV~hFl8Xg^8E9ix?(S8<~Xf z-i@Nwij;Dh`0-;C816$50eVy5rW3BP!sL7+#~lO`>5d&cz*GP#wY$9y7|i^H77tIH zn@iNX{*GfuuNU5DP?;UBT#04j=RcWr&vSr#bznL0dCES*OycmMzHuyzAz7i^-Lr-* z@#chUhWhTki^m10&+8X9ewA^iRU}*w@TfMI?kux99(QQ&A~CGoQ-+<gXZ{=*=rzP6;_kjlBokO) zSFvf&K~~m+2;4v2hcIwMd|naB14wc%R{nEQ;w=o_UF-o#Lx9!e^3KRk~r z$jie(NI0Hhi2-@zVu;ZwlC}jI7~T(T>n(d9$Z???f+2HQ%$_*}m!l}gr4W;@#sgYi zz4wV+jt|sQFmoV!VR3tDo627?m!7E*{=j?@eGXX906}5!oael{gvkjY-nkAoI6!Un zEUm7#N9^Fn0`D$RS5evURtEeFhF0BV?0zB?-?p|E62as!X7KR^yeNE>kxKCN@#F7V#v1jd z9qsM#k~@G%WS3ZYd!sFht7jrHbGl&=u4iyjVG+JfeG-`hv|F}-s6}(~)}1@>V7R8N?4Bs<5KZzD z$tgXy6Si)8-*MMr*wB{%zdh_vl7Z;-V8vs z!`1|rCz5yJ$fE+e{JVFKQ@y7knP+4S!E#5y?8iSA48B&d{{byvXmoUWVF47K21atJ zEKrd#u0Vw;5p2G0zhiMhKT{?3f{*%r(Sw&AY51PlnP{%P7(S-oyKUV^A{iPtYBq-~5T%jss|hy*RKbnsRQ z&^f3_XK-^dLt$G|y@$j{e43%5Xf7TpNEBdyOSxrB->+X2En|-&#cd}D0&fb(;P4$2 zoCg=Up*UW{YP92YG6bm!ZXUpbzqYg_r>2TRtMv8j`3o03oUTFIhgd?KEZ8akjJ{I& z_D#Pkz}+DpPcevRpdER6dm{@DE;x8+^AJNcyy(R=1NQ^v0+LV<1uIzF*mUC|fqEaS z;nm*ImoK{jzzwyCJ_eN%?wiI4$AK{pAOb{kfV~7;G^gUDN4DM6-Z%3$D?1zEX(c^9 z#c{eSD$mo?r{Ss#L*$;uMtrsnGfsft&g8IqqGLOGfQQF+ZN(m$CO|51nQ#qe@BS7R z!JqtEd8qfn>E21`>g$VR#}RXc8>OhQFyyICufhcoMuxCG{_7touMc>7c;FENvkc}- zWz^~E>A2(1o;)ET%q%S(?d;%31^OHH8zG#@mqIouW1z9X!=piCh+|Cz*<-KT;SGFE zQPBjJO*oM;Rmn&20KA8=x9=`W0aXSk2DK-=wI6Rd#ckE0W|IAlabc=x95%@g!o?mo}oKUgz)pwwwFVu#s3v!M_(`AT0K+U|6>>jJ+RIi{a&M zyLLTCa?AJ2uqr#P{{hr?OhxDUX`jBLDfYyB7{nd?|Pw3(GSY@6Q5pE`AFePxEuDGv$4g~d21sxhBL&EoiH|4Oof zpQ8YU?07Ud@G=} z7qqpz;S_{Is1%UizXVfImBRJvjJPGf95!OZ9{Tz9KLD17BkA*(FOjglvOG1UZUe`- zct;5Mb_?AoM|0tIxt06Cf%sRi;Hzr>vt}pf69Zjc-5WRF>sQgjX#vJ+_|PokHqTDI z`3qI2QY1(CHzO0%Mxj1R}21HFn)wz z8M=|Swl-W{VG)u081;($bPZ(buYZF|3s$ypl)8Cj5e9CcvJ-@6p~F!aO}>4*VNEUA z;kT2|j7J7>rZB24_NP900?VZ;1?t9<&b3ecPcR#(pe|>RXUFUV4pW`^*$9p`&%9Byd(Hg<2Q*&TN z!Vsy{>SR84ln;3v@w}jyJ3vog)PNgy^;JY!*?D~7Csp3V(K_zkyL{*_@HOLT#j&fr zKcJyY5xZ9R>M?vx=moN@zz_BG{1BhL^L0J^_vl8W5Qw#vjO=>i4NR)B@7}?S5<~HebNE1DB=kZaSFcNC-Sv#ix@~QkW!MopJ2}0fh=#9gf({?}DUe zBhBj-z8sLQ-SDxME8)m)i>45h$KmHoaDTw^5g*j>x{-n19fVuq17aMk*@_k&9X))j z5l%xg&fnS5fmWuaqoby*CHlX$;(#iAB)mZf#~6sav-fx}J{M-o;!dHc-YMAI69j6q zC45b&@@gt7G9A+u<>da6Ha({If&mo-#>gBg0Hv+#4B5dl%$9o7edTki-v9iZ9{vvR zv8c{PoUOyB3y}|jI~~^!#kG{7Tu*)Y?GIr?e!`uMc5;0>(2)MOXkxp`(F8W0^wb*7 zM5n{?huKI8`)juojvd=UsI!0E`8x8x^NjW4NN0@aIBlOGoBxlXAQFOYdQq2Zeew?d z%RRe%+(ghBVK!PbuKyk>0UO&KM2JKit4&JAc!1gCYGYQfr;lF)j5lN_-K|eQaPZ*! zcke>M!zwE)n(&LjQ(K34@n*ZCEgwYLPo5;%+#l>-FDoui(J3WIk|uj_RY?hguz;K+ z*?KDc5C;eUb2d)SxtSS!FwoY}+M?26#{J5HT@cjn1HXU6Mf1G{m8TS1M;zqEOm`t_ z!f6Q)E7X_j*o(g(${UYm#^XU(l$CuJ6qM((B2b!m8UxCyQ^iWrGssiV(ke!500Ir5 zSOg0zJtG5+#iDloJ^k&9eFN`k4_= z&+xQELr=Ja_Fh$8y@?*KtI^QVd@%74&zSm-z5(K)GyMFx0_(Q=s8rh9Yx||f`a>R#Fs|GBoPxdPV>g3L%#3w(Ze7QojyPb zSa1?^4b4>^vDU<0#_OY z*^{C2UL>kLbn5t=sOulECq9x^b{6uZppaEi80_r@0D~R)gN2#dp*n9|k-5#7dx+V& z6%}edkv7!bjlkfYpRY=;9YlvGoZuS&*m4s`sVlc~YJ9RYUt5O+Q~~sQE?O z4jGvT^|6u=bh6*d=zg!;9B~g`N)*X`8D?{2A#E!=G;L z_ww9`3aD)uXp`OCqL*PhDOGqD<4K%+DCfzO*$C0Z?sSv(evn0|2{t^pa;)PKVOeM9 zGOQViwgB#y_^N0P^en$ivP@IFhg%>*OAv7HI4MgnN0YD>LkK|x2dkn%uIIi4_qU1) z8B{v`38WD_IGp|R_p6GwJHYX}P3PVJQUU&QHLC>;5oHWttEiY5I$kR~z)(i~H$Y&9 z4^4HX)yurTIrpp-tWS z!&hWwDKA!{q25Bszve%A>eQ}vq&cL33)9o1y;^u2{>3>e9*oP00(hN zYAZSUL`zCcYU)oI-WurQbceY?BB(chez*=adjLyGiCZB(U(uozVZa znck~dxf(?dlsiU2#cjg!JwN2ocUv~X4euZeCNn@~#>U)t4Gfe}fv_XAvAOX1!JmEW zuf?wNCFXRj51xK?FO6+-J!Cgg6xNoYe|e?Lpp&60@=BNz5tZ}q!fS6bLYsAGcT#h8 zLeluhyPmt8rEPW<9-!L1ue&`00s_ztd+5zPo5t~j*feM+vkczg)O+%T{r+ZRESdwM zOYY2)^^s4-1Hb`>2@sA5FR!n->`q*8%S2ccPEp)KS&wL>S?j|}ip|(irVsq4f^#t( zZO}tK*h3{GW@&4S5W*?^Gng>pg%A#QfQ6;b*)0>~?mRk5fVu>-4qvmLkoMcM|87rQ z*poo4=0--g#Tm>FnaqV1Mn{w6JpZgdEwS)jnET0)MbCZf@ZDD_er_lIo4%v0(~yE10(QB=)8ZqPtz9P<%9c(`gmwzZ%J-e7X5GghjfC3`?fvDwR6sk z0#n2H3u=Z|${o-A{52S8Mw|L06iHH&;I_g|7M(>>-AkziSl7F5l+w+fx9TPk)Tia! zX=oOvrj}+GiRqiKUcbidC25m_tBJ`IzQ72;SX*5M3Ws~@)al3#GoWmUtal4)BXgqa6`aukhj6%tG%C`eYV-I%3I1e1us?ZH}FM2Dy!8YTna1el?efRDF5-EXk z7rEp>$V~qpCRLk3V=##Hnj|BTV^ZYqs6N=&ccj0}Q3q<`IKaG`|INpz%E#yWnKRnQ z{N(kw1a2d=Nd&^mAGYrlAl6pqR9_mZP(I#9=^GT>oVB~=YvRE}S57Ou^K_E7L9n|d&rtFl7ETkz4XA@-*<<4dy833Jh-`V ztziT#aqG9$9-Q~Pc3I60U#aog_I=*y%MH`Fw=^}Wi5_(_DGF6iQP^cJgvuU8<_j}< z#q|8VW|k#P$xqg^jLJyf7qzFO43h6PBNG>cnk^!xXJSgV#J=)Kp6 ztd8V&#lXY_jhjA#AvPOpkPIy<+geNoV4r&3o!5og`n>=@f7#0<#_avLo+*d+15SYn z^JW3@@yT-A5cd`SHRvsiX#!&;Tzx`l$8w#a?||SgJ|l_lRk87SS^}809?ya z=R9yDhj4224GmSu9O_XyRp1m-qVsL!_iwMqCk**-B!%tk*T^tEu%PR8`wfPYsUv#? zSv)OtnX?;Vn+7|h%`t3gx7Q3M1{CyRc;+B+2JlMXPk!GXw!g=HA9nxxg|>uD^*FXF z=r^&BM+X4|Wwel}8@QeEi=wwbz;C#7Z!N$PZjIE<(9x8XEAsMVxJ599hY8z^A_4(E zxPXBwd=G3nEs(6vDI89-wQs7j-1L2vsX5}aQNin3Wp6rZCp}fvX3zPjo>|r$mPdtd zJEM(*6SGcURAQ-@vg2Uy1WkAVFl~H|_`&=M9?sdZW>@2(YNw6?moMgwZH``mwzR*x z*E{{e2+TycaFt>g<@rgtu#?tSz*Rk5(R<N{qyH;1`pxQoJweW zFtXzf5ra4h8d(BvX`Dtf*?L(T@8k33RTzk!OJ~mvo80*D!;T}$;J)y;w1G3`Ge@u9 z{B)l#OicXm?!fs#QP+)~&Z}fWSVj)(BbFIh7diF8NAZhJ%;@-s$6Ml*Sn|ZZDe48b z2L?zYG2rZrR(wm@Sy})1k7Y^bvTo+>VQ@$1yrgcCd;?udhQ51&fc;G`1Zu(4876Jo zDfGN2LSSrl=U9E#Nr^)D#dJxf10S|~z&QQf;>9k5G5J!Ok$Y;rqx)Z*pSu}>=o9SM zIzTM8;3|RY*auRq3m3L#I3n~1z>}rE{%@taC2z0|ie{$E%gRLE)}05df`Ou`e=)?D z>We5(Z_;iMg=*URR+o2dHcPzpWTdQz50iF1=)R;P{D)&Y$|Lx83Ne*9QF%2ijPd58{4fuU1|S$lE@niqt0l#otgw41<=FyHRv z8$5BkChq7%(9r3;eFhiynu%0YQ4|jH#Qxvko*%)CgCM*GM696pA!z=lii-5n-KaV9 zb92e^yQ~j#;bBAccR>R#i*_~&GnC+rVel@;Tow`hL}t+HcObv9)3;`YgVOi5kST`i42VCR8>!GEiK(`Bgyl98Xv(7896*bdTR?QTp4P?)Y;?X6~=KZszAeVG=2@+5kMPrn2&+u<*gIN9dVBM;Y(P-b}SyvXlb6fbGxP%%B`ngaFs$ z57AhW5O|JwFEhR2wc7C%nS0206Muix8?qF5&9gb;#xpWBEP4UShZM%b0L*uYh()P} z%>cikV4!R)ge&sm_%sM60M9CVlk#;6WAW0jmxMd6RjI>Ss>%RQL9YzmmeTc@aS5`= z=eJW);q}WD0T{<26xyLrke?qJd5G51G5UTnfVs0b8arHaLn9ytRj|!Kjx@pp@A6?s zc_8;h^1;@bc3wMWhzHi%?S`GCD(g$4e)0}QZH#BnjPltU6b#C`!e5u!b?g8bOFIGp=(ite34MzNipIZXEf8g7S!Mr6cjTOHy z4^kez{N<+FD{E^NWMzx06J3ApEEq?z)!uG&EBTrNQIzC$p1)g17Y=~{F?(cZ=Qdc#&d#z3TMsLx1SkRmyy%CKXi@3A8f`S=70Psny zdr%yb6%`a}(fW;8{|!2+!W+Pj)Iq!fVt6nS;Rld0VjaSCaiy=QxL5~A zX+%T>7BaHP?e+7sv9t4jYRhae(D{s_u%Qu=kZ24Sai9UKv_K0;OqnQ7!y6ckKU+R#@&zW z?~eli#BYoV@hb(T%&OR}LRT?B0PO65FWC;!58Fy7xBg%Eh&_8lfg z=YWe~b|+P70EUNPG%~U!G;nE@sY;$98c6uyzHLG=MVaMQLE#S z!d3>Sewe63YFylj2a=Gb(o#`jy!^UXR`eiJYY-5%iroi>5A3@XVNEwWQ4Ma-gCT0ai#qTSN@w|eh z5u5AA5K(@4FAkAc?fe`ZI~r!V)sxZ8(E2B;9}3#2`{Z)3*`%gSx4)f^=Y)71xtoIA z%`XcEA zHxA?z{|l-^E*F`p9U@Z#H3!#q&=o-(Ql1D_r<)@RFy&K9 zZm*0;c=mftC11Of){2poaYQ*=S!SI67NSRuq>yQKqjP27Iq7Y};r=YtK;BSvqRe~1z~ViEQMGilFZgjH zz{_&!$WBUZ54;Bf7FJ~B5&>3e(m8sDE8Y=%aWfVAJ*{@7rdD`$K|xCDFf@fr=ILmaKZpM~jZNJ>KmEh4~-#i}b=rxeq`Lk|xO`53vQY$>LhWw>jou3n*xBl&(kNog+gKLuz=SQws zrgic5xl=3;B;7ZbD4of~3o^{a&xU>;_TJ^T4c8aXB={DE>^?}%0gjLP=Rt9t2<_8y zc1}*@(Ee3@rxLhT#Y9`XCxG*t;}7p3Xr&j>rC5Ft%R-_bG`z*to_IZMeIB?x%Fsqd z>$gmQe>EN_CNyY+{t4R}PSVrUBQ1#GAXVREppCx1j|8d#g0RHplz$>G0^yVfHl0K=nxp@)CKu0bxg0;1^u6#Kxq`_8zHbYlR z>LK72@B>g(73Af?sti|zFx2~`JJ#y>P}m&)KvR#EwWERW6$KZ9$rByZ(Fm2Y7dp@6~hwW(qwx0 z>C#t%0&KB| zuWyn?*HLS>ngnbj0|Mash0X3KA0L>0MqsRc`8*GdO^oE%uI=@D|KPF~+OSvQ2fQA> zyBd!m1It$RrM!>>JC$f*zIZoexS{b z_R>B*mJj>rgr&3W!k2=CZ(dM{2{=j=ofscKe1>aY9CqQqs-8T0k{ZpIsyn!08XLkK zoIbsal9O(S=bZGa>LHCyj^3f28;4P{(Ad?~P*p4aC#iW_u)e|8Ot#5usa-PlUn#$x z7iinlr#I!k|NQIC)8!w|Zr`F#8VSqxz<*=>ij$E_KK$3$pd@=MqK*6HUtfXr2U9W$ zL;1gPsB``|-f|9j%g$t^(V4mb_J%y}&a%7y@9*``d-zPaZ}JxY$Nw}Z{a@aUMBr(8 z#hnz#T!w%B+mvMzv=53-6jgZ3x9{Aj248r=cK^S;;@m+rL1MPt0%2VBx+kO$Id0Y2 zmC`jq`u~1PpWCq%F2dW$?*96pOlRvYtIoLK|6I%e{ukC1oOBF$mj3m3@iBL=)t^mi z9qXDP|L?#158ZpZ4lA@JN-`-6yDk6Y$}A1(Z28}=g1aQK>5KcX?>x+`Y)Yv z@6`F#G=r9k%>bDk`(Bw~@WA%=Ojg|CBOx%|vvVCQy(bV>tDoW+TPP6?It9!+Epsti z=Y66wmywaVAR|*ezM3aGVQ>`M#n#H_n*mQ@P-Oh7Y}iU3;gh|qHbx4&Y1i9bX>m$C znt8+|!^JyNQbS5*=cHZMnt4W$B;5BS#Xu?2cD%=DLoLsFdNs=1=>*5=P{hKu>jdl; zT%7FObOvb0ya;-Dhnux9({&6|#-@fvZTbcVaC9rKcvNX>bKeHrMBQfYg!J^_m60fA zwguY^iGwWD%JHfb6dl4FWatUCbvYUg#usluNn+Eos_2}3@OE35F zwBXzm>1WO`hDZM}c8l8r7DIEArpM-+C!fX=;+pY3yTO^%)SD?Os8obI1?>c2U;m2d zX{o9HEFz#(!KU|oZ=ap{p|EmCn1KkWlaWVc9ke}Q4p@Ty)T!q&t|lfQ>z1Cot#6Z1qPsK(3KY10I zU23X5VV_*454}rx(=W6mvcpy7N}Edl#JLtp_wqZ_)2xpfNPp}D^KqGyOx)Ggf5z+Y zUv~E3*bm?GKT=>0!M!$>{TrBb`{z^*&f`yy9XeF>BNkUWG9aL+qT+7GZenO($}4%g zFh7aCe%Ix=PhL?~-Lr2`|AeH@Iaj!I9(xy?WjAdu8?9{k<;K2!su-sy@+C$^-*_}N z3$k%=*x3ut%-+)*9Kg-x7SGBk^-BJD-suh}4f*2?$;-=US4vlWMGvOFs(I6K3)47u z;1kyo6rGcERPHT!-D$QIkOJ@#OKlY&5_wl5=ud)w>R zyZN$H&;u3}d@d+RNx`E#bk`v6oy)hnItt>MZ0wHxJeTCEi!!dU2VcEHLL3TtQRfw5 zWh3@%dcOTM_aQcf|2Ko?g2>SC!6#3AEUk;6k|?TA#>7!Q z`z$s#C^FJ5vcLr*$+GhDB&{7_E=CGBE){#Mq@)Pe3f|xs5ZJzL`}!yu&*>w&6$@r~ zu}t&UCoW+&RB*`%U}6-ZyP$(}BaBOM$5YDkvW_Y7F25s@8Q;7x;n9}Y9Yo*m`o z$GoUnB3`^mZN0arVi*4z3fW7S3tU2cgK?5~xz*eF-B54Q6AsU?Zsl7_1)0S^x1#vI zfwWe>qr>5;*q2R&^I%wtU`)(mbhZS6>4~wq1d*r8W;Th>p4I2)+P%pdc)YNX8Af#6FF!CaNJ6L_R~is{-A-b+ zCoo%cLY_7-Bm{80rgP;`-3Zn_@2|No9zo|BF6ywo1C-yFAyo<$WhaSgQ0bINMX zLVf)lti@-O0mecrIpv-!WASNWeftc3y7}a7nSBGdB6y{JBDHQw@)r6R9Gf2TFnUi$ zd>+!%yTWnp*x#P8JgyTezYr@|e1Iis(4a($FHzsAMCFo>4&nYOS$!#1Bj5U5CqxQ6 zH+!wOJNSR$W^iF2n|YW1r)jcH&odY^ch;Dl;$`Cop+wD0*FQ~WOqc!FcfwZq>3)|$ z5!s!OL6XSKNWFeVbqmqqlWkRBx;9wdY=1=!b?xni_a~ILg7M=RlkH%1av6Y-x+gwL zFvoR%{$Cd^c0#~t{vql9y?c%epA^z^i~(mlSOb5*5VMC2?cKN+il=^d|9e_pj)+_G((1wp`C zbA^$SoR*o?4lU2KXWFJxM*LQ9y(?J3dyW(s_|U^*`Rm(ouEvhA@+bNGoQi5yRqhwf z`}kN4iq?ILVg2S%< zC)?_c5r!Z#EZT)#zEZK-6*WCV9bIrOf{23ppDb z`@$%-q&1U`f*<(Z#pADEj{+~!sq4hV{NWmVhT6Gv%;tE8b8;po8)WXql-qqw53;k^ zyqlDjJBFqL_nFr5>esvqjC+`2p2B&OyQ{GKm@qrL&`~l8OEL-yBO~L`np4G@m*wT1 znORuqY(4vR07$DP-FhAvcsfFWnnw8DyKWU@5{E zDq=B`ItXx;MM!xw@q6iaU4)p}a4+|{qb1YdDi$eSlRo%-o8*knl@F1j=%Ut;|NSF| zV)&)37?PY*!owFtM2eGk zU)cR$T!8WASsY1m9uH;w4z9^HfA|<@C(LG_Hu(PXcD=<3F??cAK0`MDkTH&JA8|w7 zqq?hH|2|fJWI%4 z&+C{L#Ia$;s6@9@hQL6(oooyTrr8;WjtS+LVC$v>;OR{ktRM{{8kddkO7? z=`YjdUezzTyV9$T^B%w@x=S>0y=v`T$kOVv3M|E+BpBZP9Zlrq&X>(!IU*Au{}JMZ&iwni!Ifl0+1IbfPn;-?W}O;{Kj+aFboKgp;eeYb z%pTIB4Qd2$XdeqauGIG-@J-P9#DtQ&X5j`+V`po*xcvQB*gvvk0dU%6;SGGU@83@g z&;11Mi;F*Fd{R>PWFM(k0C11h<)?IXzi(!!lan!iT)q94+O0h_^uR;Xhp^%5D9PGz z;P-j4>+9@G@oc@BqYD$imPwtR1;bmcv)WP-as7n4ulXIeKW+Ux-4JKeT+;kWb9PFnBVew@v|b<`O(_Z# zViK0$$?X@^F!%Bld>eyRzGB4ohqYofj(IMN3+H<-sj4=DH;nZ3_=I?{?sts78y_>B zpPvg~6v4^QDAZ%y(%xRZ!YfJ>;KhF*Ghklvd8VvGRF{v#D5``Rv(N5)zqXK4p6<0@~YKFJ7o93RW^5tBq=iTbSrH&=%_L zui(d#xMg)M>cvYtX@M}8$u3QU6EYL0OM=JG(Jo@hvYZ^Wy4XdnqSDQBb}awSU;=o2 zg>s5B)ALv0#hqur?akdJ&CCqi=k~&E>KET@$=CRKFi-IPOATc5V+n%Z~#v z0cDhsbPr=#C6B%zNfZ)1WPA}4LQ8!6wj``S z))HW3q^wBtME~DWW|p4d64YU zV{9R6S5!5@*?y961JXJF@piA(#cuebs$Nr_p0BR18XGffkZ@a=-BDE7LBnX7_$i?+ z)1cco^z;Vvl@jzGB}o_ZoWeK{;~=%|F?5&6cmeS_ElY=l13RU~@rIWAo@PQE0lEQpwuQ9%1_r zy1YJ;+Z#WJ-oSCA?SRTtiC;CoxFNAC4cXBz2Ck@a5!P?riu6wQ}IT) z^MTeVws)PkG+k|n)+sfand}P(k~DWH z?QOZcmW0NF=4)^8Ycv+A%Gh+&8VM>5wX6aA~ai$$>IpWb1h(&Dr(u|{^6AnQB>DVF&U^Btt@cE zwB)xDp8Gh(txVKYYAVg{N?#7wa@viL@2Rn?vL`1nZ2bT-;_#H)w9so31GFA3x#+VLMyg9(Yggqe$x>*}K<*-v4%ge&k|Fz^hyq&|TIh#4rjq zD>oOhcl-G3*N^xLlYdFz=H$FvKeFGd@a2Oo8fV!`X|IgXBbE6{K`!vg+C%tfV8zB620)I=13PD1vw zJQ;y)GQ=T+m4OwXKU4hr)rjGuK;PNZaWf$B#*M@Wy<}`htLmzAohBy=Txg^oN)_Ix zb=r#Y5Yk~C77#cr&dcbSY$qgnCEUR2A$nRH;%?vejyl!m$M6H!xJUfp)5@CK>hjyW zW`}r&v1xSBIPkO*3Mbf;3TRAprZI3wnU0SwFE3$Ck3Qk|5N?XCvn1=T*qCsraSw?v z<)p)9h?UjAD|4e}T`T&;$vq}{J+7BX7?QoqzLeXiTyp)cpK>#ltNDK6#X*DCVLx>V zNkRVEJy(=>63K$goK?sAR$=yzUn2{+Q zcz<>*Sydaen6v$Y=`e{Mtt)9{OyHJ+OrTA!YoozQNgHG_h!F#QU!`Fyu=e)kVk%;4 zV7ecBJ9Mv9+-JIBtL8jcUtIhg@J6nV&bflL!2X5`iWG=hf9M6f`y8E2l-REt@{Ig4%X+5=H(mLf@j|g45F# zZr}bKP^W{=e3g&OSNO4?ebOi zmSAY&O^l7qTJw&HueUZ%P5o&uv6-LqqFUyeUt1}A^axNp*oi8JnbxoP^hS5FXl0G} zrD{Qdh?{Jx9~5wT_njL%!E!~L#ifzs`~8CxGfSac;f^n5mo8ual<@X_oSRqU?;3&7 zvnJP}9{#B(H!!#t9lq2<>=%}2mp*)xHT2ng$AWghA5awVkzF=tO1T}PR=1?3bmRQtX?!s}MQEdQeZ0+L)!D^E zO3Kyx1C{~g;RP0vF&Wo0U@?&~}ab#tbfMc_Gb zEz_v~9Q8SW(J3egpyvIssrx;>R#CGd2VCc$1k?#00k3*7PLaF>>$`9cwbH`*R!21) z*dB@!wqtA_ih}LQ=C!ZWU$Kr`EF4{%9jZUg3b)e2`$PUus|DTE{RMxnQ3QZNCMQ?f z)fEB2CB@-_rKPg1ZBuWlc!$jQ>gs&A$=m8J`-DAu$Hu>zOFjF|t^ z_7q);+hWo&jr#g`t?gH}{t7rTqc{r+E+%RH410Z3`_hdzZ+tF$r}1fW^7Gra5k)<$ z(wl_9+j;fi=q}Ih|0VcKT8UCzoK!mvhtFEBo%DCIp`oFEG2N(hjG^)oCVUKT#+?4_ zu5Z9Y5E&TRO4tWJdz7@g+yKM8+K58?V{B|Kf#ULR+Y&jipAOqfDDMqv5g3-B3mX?V z745KzVH?uC5ZXK}kVd$iJU~b~J5*X<*hduAtdH(m4yD=fn>naMaHxwG7OI=|?7g5z z6h#$CoFd-{(LMshnZ9N#+cpWC(QD>Uw}EMo^uyOd<#%5OzqGknGB5L>>T|*P#5M^% zuvF|OJH+n|Og%0N1P5#jp{|kckPfrJKf9jK%xqWJ0~}i;)Q6%2jjZ=Wm_MgX^BbI~ z7#sWa&jGt>4-Wz4Ww}fH=mvnMnHW=g{!>y*+Hk#6+)gNm_c(%66>#zumoOV>7R$8R+4Z*>CNe|5hkC}+hi%hzM9(U{3Lg{$fBSDuoeO~rYMBMPg^ z^V|BSo*yA0IO#-$gxH4X;%jeajMDtvN=BL;+k)i6#)QWD-*-M&cEFAy?Dh8(J1I|W zQR>KcG8p-(hrSFJ0{#l`mB=ILL6m=OJJir6b^Ba#Wo1fEj{abRDnVFS)Uish2L=aX z^GinLZ9Kc7suBG9FY{$fb@}%zB!XGmbo-WnE6K!vNNzFgYOA8!k^47hXCQn(eC6|M z4^sFk)WXgkcfynxsxltgiH>yLpRtutE#MnrOcnSb*_NHAdr9NO0ltAk4{y=z>h63O z273EdJO2*798uvZLrPsE9*w+57e8`wat4K{(Mxn`ui9_v5?lUZ{WdvyC&6nsA-V(| zlOaA5PDxX`KQc|P(E&CeIo#OztNriSI@)nBSK+GFzSev9Mgl~i&BWvSJ1TOQ!K%S7ZSPCdmU$=3FsyxdB%-DCl-GzTYp3Cm?o%_Bz#;UA(xbLEKrp z9xD0JgY=3;q~9;gRWpkB#eAA8%@o`;KuAb9=y4^}xbZTY>O9Fj5I^LPpE#c1$Rc|F zYU&^3SgjQSCq@+R0|!F0kEa<~3;TYitZiBt%kbF8w)Y9ZX0#~U!7SxsFUFfKYFbi~ zZWo}fu5WH1cNZaw8rmC5?ljyA%yv0;tk;5IMlssx`T8^@34Ar4+d^9E*B~9BB3_-1 zsSOh0*!#0g|th+A?mi z3iAH^_%S))87aAf#w}8U_tLp##d8bK7~lq35IU<>y|%lRxuDh}=y}RdTED@KW6$8G z^JA-Td%ls-&Rx6C_DrO7x5j71o>#aqxcqc~>)F-}p_D5D(^JK3O~3Uox^x11u;}tV z)on=P_gL5ik>4>ffx(TRr76TFItm8+M!mRh0a2f}`V=YVa`|{*+?R^WvKuc75QS@+ z^z7Nr$LEoP+SB>0_RhO^E_jGzv$GG-CDk_07j-z8nNV?33Jadi&IT&G&Eb_xJ7_)N>2_H2(7)ufWrh!NIMQNTGYq(m2*#SO{B(RsBuP>ev|UT%g(i zpc+YuiDWS7GT~Q{dxl@`pwpiyG4v zK5Yh>Mnqts1#aCpYU`fKy4%_g?p0!Ly)S&J3`||Ify0kXg$v<+&cnJnQHz$Kq}kqj z*XYWX+Y>HJyfQ9q(?9A>!?^d;?REaUx=kxO<30Iyk-sh$(E`pbmNn5Lf;@t3!~Aw? zPYd_6%pKhv<@q_exk-sKiGal!Q8GC%8YV0=Y1Ql``eYimy%OBRK6U8H(PLR91H3Pb zVRm=^mit2vwIs>7@<@L^(VS*o%I%bf#xw|f4O|LR3pzE7AjvbA-V`8LU@K9tcJ*g- zodj!dZJL47+FC%>+#kC!L-)bowH+r5H1I|H_3u=|F1;lemhxZ4o1R#Hy}a+@lOh#$l&fo0wiBertcfcW5Dkszh2Ei z9}1B>9bvi9v32+3N8=#1d9Bl_`^P)kP1>i&wSDO@MQ2tb-gIj8s1K7|gX?_z7?)^= zSkz;yTemuX5E3XmAaDs<tR(oDxnc8pq(c=!u76$ zBi)SL^&{`%C5>GKOoep#&)hhX9@_fXFG7}xN{yzeY9ph3l=RZ+qIm_|Y|lrKF}o`>@Mojc}bWgYJAV4&hhG^n>jp7*s-V+)Ra z+;zZK1Jqh zP3TUw=C$olwo6cp-nz>hQv{$a>_6G(c8vgpGaw-sE-uaAJ3IZwR;u6Q^gQ5}s@) zo|SY856_Ux`~|i`5M!ohOpHviM~_W@;dpewMzikNhPV4qNujj8`}Tx9e;%PP8l2lU zE@BtWBaaC~_ZFuCkU}>%MJ;9;l&ztfh%K2Y_VCwE4gmWkXWn>cei6h%#)v&CD<>h0 zM~ww_Ed+nT_pPvJ_=o%OFhs3k4&(abj79clu&9H@EWAwCUDpW$!cJiOx)%dRk}uJo z*8N)ZU<*Y;CIxwp{d5U-amo2h=_y})YXENTYomT-(OqEEw{&W|=UZ5JjJv$D8!bgO zdh|#Ti@EZzU&mSsxP%6%GFzq2xiWtt7q;b$dU8WAz54P^nW51}6OjyywWWLukz0OS zh}gEk%R~DIWNsDuCn;|RYh}5t_-8HI#lM4aBq*JlL4Yb!W(iyR zP;2Oyprh8(fq@|#BR?j(^BHWHU!djPdv%IgZ=HN#-`=U^hvr?r+j1GdK0in4ab3=% z>92x<0)<#d2=~p;Zxb%VfA`1{%&66@Cb^dDt0}r9OONDkM|kr(uzC3UE!-Q%Hu>*((IjQ77at3;M|pruJOs0-zLr|^Ki zeKPY_-b&>+%f*54mUi0sct+2yo}R`D#$@6p%{*EM84g0C)!y+lA^LNQ&CQ!x+&Z6I zM#Il3D7XNJEN1Q2yI?;*IvA0^BK&D%xL11Yv8;j%&$*+>=(vT_U~C< zu=TvJFDmU#J-Z+dJCiYo;U4HEz%@~Ak1@E)esa5z_Y1e@;KB#Ld`Uux$G-bHJol&h zbMZ_^-Yx&vHBC*h9<2z7d}Y=`*w7M0Q3XX@i2510b2n)8ytB#u^r^h;yhV4;1@2>| z{2hOq)5U^;q#jTTdpR?anC^E*^jEP*#QPn;sTstbD|aipOl=6pdS1YP31J6QUL3z} zBF(XfC<2|^l&-@onMC>U!m-Dh)W&yKNlWzxY>w2Wv~v2F-VWzeOOOj#)uMc`N#paG zICA1dJD^m?!0dCy2C?sAag;DJE|_;092u($X147Im~IHU5;&gk-wUs|p2pTVk&CP2 zJ|FWZEsF&q?(a3uy54^tz~vzECZ{*mGVRxE4^1?;9e(~df6xtbg{a9!uMX-{4Ag4- z)$F;|nzw-6>Fc&I7MCjUV)x1Z73ICbs;WnKZ^D}*T?szxz>k< z1HM~lhwjLgojrb^nu@x2A+%@m;m%W|qp{iY_o9|-@DHYs8Ps{LN3S1oWF}?l{yWdm zBNbFjo;yB%8y@nBiMqNbnf1T;dxo!wi;MeT^Tp)DD~?pKnWHhf6A4|IH3(8z z=%u7pPUeYS0a0X68};elIi~ym>)^6NJ45FA2=tooH-%NypG~HA8zrPnV`qT`Qhl>@ zlR9&!4fjdXSwTTTm?2NtS&8-hcAUBPtlBlM>E+8#r-c`?u^6iX=Kc6_yRC*(HNl~#NHmjwwQbShuLvnKc z4=b%JS9Z}c#e0v;pejhS<=?Zi@px=9FtCp?{0*&RmRe9qb!Bx}&GPqW?3`oUWtw)- zL|tI;yI5M&7uD$dMP7m+CS&ql!p4RN+{+zTM%ZQrwaqhh8jj25rJSGu4acf$lFH+r zq!X^e%KP^PCju)kJ>K?`jt;{TK~Q+-EAyT*`)d7aS*K%ZdP_P*iFyjJ+c2gOxSmZn z-F+ou>77jUls*;64e6&(`}xK+L5c zdQ@|?m90UJQTSRbn4&|h_;6V-Xx9)F{$dtVh9d)OFE#)A;gio@(@w~RaP_mrv+=|! zv!SIvcC5T(e%flWwpM_i{^0A-&@BXwlhc{ZFG6R}_H$%rWC)9#MG7@04ho9!re8k+ z#-ENbHWf8%gHBH=LO49=89~%Hj0e@~dc(9|cSJ=gQh3upKh$Wp|F+DNCr2+_*!}Ak zJ3AWWy{-qg5K8P#w)O_)-95g`UxKA?TX=xWws$a;Lb`V?Dt zfdZ^_Ffs!_#kO|#^QX6O@74F$d&tnPk96?4`IH-P+5eFmT!-(nB4NwRb4!zbMWS!? zt2)bEevutn&WH-+B#w8}7_5gbnoYX*!8*G08x(%pRJw+jqY`7%Hv4a5HAP#xNsW1y zhmwaNJ>lWiw1?C>D=TXshgz{J$-C6ulUHwCIrrfD^il>|Zfsq9X-Ie0kIdm4gPqCB z-{q%1t~*Y|CdFwfFV%`fVR&h~Gi46TAyBpDqLq{NVqyGR5aD`Vze$NlSz7vtz{2sa zjzbbWN=gW0D$-9uZG)FpU*EgxCAoE@QtsK9(=)^kw`X#NgV7uuJU8uY4-&*7;?ntZ z*R~LB=@j$zjSabW8#<`xY3ZujZf=HwZ>IukA_YvBqQf`bNw$Dc4xcmZY`Y8O3Ye$4 zZp&Z2Lz=N#^h(f8{JjMQ0YPRKFnb2e_*zFXGV#7ehvmL(Z*LLn4tR~82^E2K266Ed zuuQQC41DtR2~>CQ6~hxgzh~aPyXH5hPR2Yl(Zf5bU)sGai zs;Q4s$P%^cJ4(ay^XdNYb}g3O;uMCzOIMb_5ri)JcYs%54$?E8I5Y2pPU_jl`hDvbL3BB)g>T75F9L>H{lMy5 z(5#g$as6$4^tMgf$@9RF!h6RU-7Wr5?Bn(pr_ol9OP#qV-$dT;UA6Fsg6OgO0`J#q z1-jD)wW-F7NB-2XHT=0dbQjvwsSohiSJmpi9{ut<$GM|GF|co6{`R|ZGmkT-z zpIUMA6Yh6%+cig57FB;GTzkXS*lady%J161JQo zBGugz?wR~=JTff;U7Y<*yHfYbIX(*w-9{*voTa*_tfHxT`}Wrgo@c?q@p0YLzVE;@ z%z0g<(ej)%FEJb($SYD0voIBk!`Z+m@%a8H)CcwNFr?+YYb#m|p~Kz82de!yVqY*}E16|!_X z2AIHJ_|o3xfAM3s{q%0{1yv<2i8*;nI7CGs)1JBlWn`oPqoDa}V4M<2s=cwX$R}p) z^$T788fipFopilzQ=j?!?gjtK8tMx@@2tYE9)8{(Nv1sI6Q<-GQfhNp_Snur7#`a5X*BX+x+<*nZC5`w>|@WH%@VV z$1)qklB2!7&y)1{IV`^Z47ttyuBA8is?bllU10AzYRxM!qH;x*i=DG0Tetn&t{8Gd zTK+SdaBW^Z#l?j(9mIbwN&28)(^iczcX#*PigQa%ZKn%y;3k~)3z!1sDEKi%| zRI@C8k>qJ*S?oTEwh8Wkj)mSsOiYC2MSXI8HPZ3|-nLGtvhijl#Q)*yJK(Y2`}Y+| zWk&WMkutJEHfhL;itLfSvl0nKvT2xE$x4!y6(vb!MP?-WMmcX+LkD-N>sT;=xCjD}ny@(7;+>ue}>QHI?2~{{U&wXn9O6j>(D&6!z~w**&Kr zqGq|0a4IL%tWL~(M!JgQ=mt#Y6O)R8fOMO{MnTcZ>>EdRD{i*d#=p$ z&G1yleqSyBiMh;XZ<%++OC&EX#fnX(1G`=aW~&U7muHh@0QmOljJ)|E5BPt z;jr#vLBr+bPQ$h@KkuhW#+}TgYHr zT8L_FO}alTx?pzMZHBI8cG|@y7Z;mYoRUe9IAx$F2yUC z**|>jYP9_Qn-Se&7biRO(tfy^izf8`wihQN;*I}+5qHp*7|CBJc4ix0NJ6(50~9D6 zV5Kb!)W2a{FKG3s((O4EgJRN&h+e7$>rCP9PGj+YHa0$V#ORFRLClZZwBHx^oP3fl zB{5`2!*b=)ANDl}&6e~(0!a#_sc}AyXrewgsu>5zZ(*VpW{$IIgOy7) z&oJ@dcw=bRs07WViVAO}+0a}-7gg--+>x5q%S?%gNJ%-4jxAtR^+Ga9Tw+=R8xzwnfgigf_npO%w{-wR7p4rO zTb%OOO}}E@Z5tWl6%!OYa%Ar9M@LM`BmD;l0K*ftU+dg}YF+P;6Fp&Um-1SlG{~}wsw!sT?l&(L0d+(l3XYn5mvr>?uZbzMXOpv|qucHI0vHi|3ij-o z*VJT~8Ms*P{dpCd3`HiT15F>J8pCVO`?7N=`oAJe=Ly3sqO|vL7g;3RQQ@Cs2NJ*B zYqe2~jyOuYcjjBba=@uuH}fVTHp<4jq?Iad|K-!=<(*-zmhyqWwyW=eoN(vIuA>2~ zHAGur`*05()X^p(t&phnRPRvF?u_4?Fi9?(cJ3%L!mt3!pN3!|p@cB1c9KzeL3d<} zf^Aip?@p`bHp0Xgt30z(VUd?h%$sfDflCsKM`E&vd*2u1>mCz`ys}O*SW#NO!}#{S zvtLbeMU15tFZI0N+&JpECkYlf`ATh{$i_bGayD1hp`)QYB(P-qB;yR-(VMq!a(@-%6wFW($h@J&c|HF9+GurY z=}EGiWY6aO>jR!+gtUt3;r%N=>MUtd^K|9BtDH7@7U$eP*eSPuYLNAA1UF zD|^#_jAp+!k1dgsI@;9gJQvACZ%Hi1$*T3buI3LCzKrbbE(jO`N%IM;Tl>j%PFuU) zw%zm)eIbN^p#0M)%6F^@;$V{Hfma`)+W}Rv??Zo5<8+U7`!I&W(J&t~zhi^i99xp= zt!TVhS!S_SIEzcnq$Vc*962`0k^QaG*(Xu*-K|fFK`|V--_a7{s;=LwC{i}AW02u6 z^UL7Ow{O0U$J!5YSXzC*p#KTfAd24m#cr#HN?~SgKL2OSi(P{)LoR*R zBs^)Gt3TvknUO_Xcu+^|$S8qW6^B`U-IXP$Ps>G={%>#Rw~A3tHSYYwq3|r`^~eR6~t{BL!+3)y}KT^E*i!*S%RqD`NHCRd{a-}%cb8*rSU~Bd=b}2pM`8x1gw2J z;yQ4XG+?lDi<7C(&uvTL)?BRBbt}g4i9jQ>A*H3@Zr9k7IKG{C<9V*v|RWdl03n-yCPFCNBx=}FKrpk?@zslxfp?bqH@ z!ao=4H7P?440P-)erelMpbB30+$I6Z(zi<%86Z1a1ZcU%_9*n%tq_P_TrdE8e`y>pQ(x_Sti1rF@uJ z1P*g4tS^2yQrPnR{kSNnD0@NsRA(5~NXXusVBxG z@u|dskpY-aPAwx%r``(YMI7(ejyS>lQuOqR6$Zy{N@He!uh@Y2%JPz&PCMb6v`){3 z7QG5rA161iA{!%jX{Mb^bE<*QsfzV|||=Di<$)E4Xt| zr)>(~;S5^^!#)Q40|FhBp3^rXslz*HqP}DiA3bv9p3{>^1*jSLu1{u`FIHMhWIiEn z6pho7aNP`sKFqCv#V_(#u5-uf7=&0!Uw)JD`F-{mug(Zyq7Y`v{;BCPf4-ZOVZ-!K zz*;CdJGadHLdH0q6Xt4BCnRw4tA;ftguv!yCSXHWQ0P>NTGGYCokP_b0b4)P!ozn( zTkZPwYcle@jt=!IH_4spno}xGHn}rH6VH2F(_|xEA=4kFu3?dz;X;jm*L{V{BNgqM z5&3oApZz>4&fU6&VxasW5-F=(Ew;ntgH@MyR=2V%SgJWL_JCded+B+LO|DbF7b{aw@98 zI;W`-V8y~F)ib#LdC3d2g91`|)E-i&>{zCESThZ7xJJFCN#? zG56M5@@{5R$s~(v)90as1|5n+BNNRuxdtjW1y4}mPL3zoww8UGDl7CUHXWGt!YOx_ zi%{O%PEB7OpMz7=p8K?}rm&a8xF%4S(%vFpztb<2#}`cRVaHkz$; zDtY(*ZG3iDd0BbR{cYqFM>QTF#~&=cUHc7L`{_j#yk|*6WH(I$=>2~Sh#ov@WUBSJ zg5(w?)e=T;#wA|3z#c}4(SJzfh;7N+$T4peNr}<(PEAg}5vXXan6=ci4$O|+ahN>i zaHHjF*%TAg)U20hQ^A{RW-p*fTt|+a7ub7JL9kLlBDJTyuoH@FN2jK;ANnIBf@2b6 zXqx<${fB1wLM@n!w(fibqr~v6Vbw<$d}BVCFIXKp_U56c5%N*Z>@KLFIM&$>2NAF7 z;*_qBy0E2v!PP1yJ$)i@a0@La5mnXJR{kT$JFj*lw!=cbr>gX}_bSFIb_HS?3qB33 z-}Zridw*m!XJ%w59jUC_=f62F z9L5Nk8ri^dmEfTBckd2_q-td`&EnAwBlRJ8Mp z?N6Mhp6)&+VltcferPAvU0GWdOKw^2SMk3(mpZ$hjz=kDFhw)T()9Ick#Uv7QfieY ze-btagyZV{Hl}~)>tR|OGmBSe!VHSv-GhT_U#{zry9yE=!}qgJK*{xodc=!}49 zsT`F?yZ@hR|C8FOJ#J zHgG&TEjrKT&IQ54c77cRaM?*_3_Yv=v*l&RVd1OaKkz~AyraQ2;``K86yvWyd50E- zcimYtJu)r-;IVLFWHXESI8_-RoXpfzhDVxu(iH=qmZjP|)&vbWT>aV*biP&?Dv{^! zybu`um6~SK!MQhKzt(hrX>R<}c@!2Ct!5qP^e%ns>--!QMf~H-Tt{X|Rn-bupy!H= zhi3z_3*&s}_0uE<%HjqJtP}FindogyCV=_=I}cxolpkw78T;P0i#bC-0XSTMY6<4EwLh@No7K3A&3vEf|+% z*UP`XPYNX^Wen9!lC6qtM$P@*$?m2_Vpkg*&++70MYkulwd*&bOZ8Y-;E}fctGB*# zXGuq!BJM%0^hj;-6_cug?6B|~H@>;p=6=r4KNC@AG5QevNQot>U;UEWwNfBsxS z^8WI-cMQRt4=UKMtp3iA&*GMeKd{^mw$-17#WZNm**X_T-Ukp zXmE066U(tfsydvShB4G0y!WZ^*;BiIrR(W+z1B|TT*|?*4c#@y(ENhD-=b3|Kf6HY zX4^KO{hDIu&+eRbit`i zvTr6=HO8j;O7sF=Gvxv`f7{-EypBHWbXhQ-<8jsNET1wp_elC3pQ#Ubn928%k+ig& zl$6|>=2A8^Ov)+L2)&Ma`lcT*Q%lP`I1cOoe8(Yw^)#hkghk)GdvJ~*J!E3y?cm_$ zB^hdgY4t_cp_8x=G2LPI_DOEMr(v~cwei>NExj)aVC@3rlJv2SnA_coxd(`1) z`e&q^2jlOxeM(L3E|b95Z#DWu;EtH^kC5nSVW!&?3-2Hkw(S4>gh7a`jnZ*Q@@HjQ z*eV}`Iul9%(p}D*5MDI3&sWkpNzG03ODKne!j8v#ekE|}{MELwm%)4{4--xJ!n)jU zmIu-Av~(r{c(I2DsN0=8eElEs6Pnf{4-Qy4GpfJAKxa!^$X~xBigl_*KN}D026twP z?S#Mk`>`$^%*ok7`uC4`L8}J$n;5kI=g~nnDe=F*U{vF_Q-x3T-w!1#PO@NiYPCxY z#DD*ai0GObd-SLTYja8s<9~liv_4Np9#Sjyuir3li?)jG{r3foUMr#b*AwhmsU83I zx*3P56OAu1Y`d0Tc&5Si2lqeE%;?7mu2jDd4av3=sh=EhL;3OvqT2uWP%4GfuG7=G ztWI~vFbq2U{kW#ix%ZYi>zTiQ#=n_~qZ|9k{`Ct9((T#dYFXpLJO1^{dO6l+dkN+T z75{Uq{{LHf_l{PPph zHFafklHdPx`?4Fj2+vR&}8R@F5%jkZWk zPalHPU(Vy7(BuUz(vlXLGCXMrzJ@Y^N=glgnNW^~KpkrLR|*@Denu|9JXIO2?hS=4 zl>7;=m$Aj0-ah0Lg`3(%b@PQ+Pc=La1<$cSPnkXuHvl6^YR zg&^PT@$FW341(xxUP#?R-c)z1aMpO6Qkpjyl+fE|68F7wWhV)VIpb@a()LuDko}Nx zPm&9ZMP!7CWXE?XsEd3+5KmYL3k~h@egii^C@xereX*C=Mx*Q-s7bvUU}a{iS0{H! zI{yJ<-99qM*NV>2R;Pk9N7z^!$J|(5e^?UzEW~Ku z3-?s5+z8DHk17n=fy)fhI)~LWyGXV_)rT`ecTC5k($fZg94Pq7a^ZxDjz^Y`z-f3K z-n(a8Vh}$;uaMq8>;~jMhn^EIdSzw$)43$duuuXSw#;?NblOe0d_y=>ClPwiC05`^ zH_ff8_wRYT{Cbt{4kuYj{hs)gltQG#nVGtSM^2gOEJ?YZg549OL9q;=<|4-)vq}Ty zq7UDW1O$X&jP&l^yFk{N8=qk4;8k;T!UE#I*NfmCI6?5A;tv)-ehg0TnX6YfQJzBg zRY|KjFAw}7R3mrBxM4EJO4&`? zBjSkef$QAD3cNdYb#&Z3JRBZ!@M>JRkPNspU>t|8jL#3&d+%`Bh26xpm@Sg{y~7I< zVleCg8!)?bGbz1k+T7Y&f1;Oo%**?_umaAEzk;H?{07(t zlZH25n8R0(l3C(rs-mRizO;K_G40pB54%bwlX@J71f9f%#uTDeFQ^kcBb{6?%gDIa zKB3nxwP*|jS9k&BV{9Kue^H=?Lrq$2z~|f3$Az6+gWr9F5nxJUqOY$n=5q-CyK8Gc zvjcbDeS_dXU0O9xTtZdIo&uYY?Zm_wi!s&LADf%2$Ie>yXA4U}UxjNi8=LG|O%1;M z%t}-Pq@?0nC!q!KA1(kXUI1e!K5lMy7$wC_B!U90(DGSC`S~XhTOe_YrPtJS40dMg z^}9%d!C(fBJTZ|`DkaK-F`}s&Ccjm#-CMCO?d=@$eiAYPnCQpbOnP~G{ziKbm0;Kx z=nWg_>cS*E2A;2lmmHxMjYa&fxj7eTG>n19e#4_h_?|`@ENx@ev*Du`E?a_f0ZOj8 zd)y%m4=|X57d->;p*|3PU0(FNu0b+2T(ttjRI0`>h8}mIS4(Kb&T5bm8UW zr5~F<+?^MpHtzN;t;T}Ud;ZcRpZUrz6PuHrNUn;E4p=h2*4M9=w>c%gk@1@UI5o-j zL84_-e}yfWRA2{b+&$F)%$-LDegj@upOewM8=qil;G38D#&tJ7UhLvqMOy(7N1gga zAwo#^q922Hgi(eMHy%8lwkAFOk zxaD$_jg?iJRm;v!VDa!FsINdDHh?Fh33iU0s=K^|4F;uL{Rl+U@m7TLqUXBHtCIOZ z3$XwuNugPJ^4@lce+@9zNAh^&J z3c7wQxHHz21xr=iw$!AfK98fL+g{$-!*||PSJwjNX_KX{b{k>=>~$WrNx6@_T75MD zBQs3v8O%$lP``qMa7QO%_(RN#(9(*#eQi+OT(*K3Y5m5M5%7+@JG+5RA=-`|zKY>W zXul?;&->Ow)D*S}vJt;ZX-M2xe@{V>IHMAx&Mq!4W0&;3y(O)+Hi-(02B-9yQFs0F&suMuyPR6&xmRKH%32IQEl4iP&0hp#ff*VZ z9hH&MrBzG^)B#s$3|~y2feIV0W2nM8B{WnW)VRfLgvy^k-C(W3+Pvcb+@P%&Cw6|g z&^$L48^ zbxO(>umch3+mgg{ad*+2weNk*=G8wZZ_O_6_sU!pJV2N_)gbx7*ab45rv{H;q=BbG zCLS$gfY=iqHf)fV82)2YW{IUr|IvlVvBzWzb_HQkZQk7hW2K%1fyYse9 z`l0KHGqIwybkw6Nf?fzewu44kX4i>}MfLgV^XC=+O)RI*oTwM(kn;|JyWYFILPxKz z`=aCOb#~G^B{mi(bc1CHZwS0m<}%NFw1) zLJ-WIyx@iG0R3O&FZbm&A*??2%qmQ`(q-~RadovHOij^4nqORW#snW`9pSLEhPU!$ zU}RvJn4E-NCmsjrcEkM;(&Oth{o)89x5>t5X4YXi;IK>@9v%G!X>{<8J&$p*@D!{;HaXvpzr>;o+IC!YU9jndaR_G7 z)1=+A@P#vm%E-(zOS^IU`O%S)z?Tr`XS&v9V|`s+1XV=GN3fjoql1+%RF;K=C|aR} zSNEmvN_I0T8UR>DfGES|3%dz>rb+DT+H57R6%58~n}UpsA3lCO7IWKQ!Ax7*6{`Z| zAp{^oIss97czHF=AHdlRS#YdMUl{64w>Usn1bXgphJojT-D|5Lh$7o#d_BIauC_MM zRW~@i?{-g-(Vy?VbMO>~3ndIq@D;>#7fXA*Ds?>1b32jn*sKK^T4g=*vJS=CwQ~E zrdUDWTwjSF5|E-{3qYm?XT{f|(Bi*jO<#q{1f;5P=#qhwIO1D)5&G`q$PjSuA9KYS zu<>j3_`II*y`v7@dB}Qiraga!d;$rCqiMHwgc3HEIc!gA^D99Rfmn^MjLy!-c$ zjeFqS)zVr=?S+{u|A2rRukWD`A=$!;v=|l-_==SDdj8Tq@QXWf{l{;dQ1HZpojM|E zj7@`ks@#inXV1c8T>sqeXT~lUFP_DogHRCwtwrsJSy|jxdx$tgTdW@&RYv=-jkmG{ z1_nCh-Gf@-_;^-`4+N|Pm|EN0U7=PU@2Gm9ZtNoASRg0mA)xhiS;kdo51w}!DBJbT z%H?zQ9rbLu`DG+1Vbf!=5HdDXQ{nRACfuaMou|}|msVz)H1lL$D9umMm7;p_gaiay={XrY<-hq{i(a@@| z*cA-I#*9#PDK=9?+159{_(hM|5ZeR=IVFy3EIj6@3V7I}0)oz=qn%xa=mai%}P=^{ORaYv<N*9)v_8+W+l~EpQMtp|f zQs{U3Yc9fh5{V3~0yGmvxgh9%nH`SkD2gf8&MZx)U7Jvel!KaoM-#;brq5%ft}%Y!nTvSST>A zP`1C*)>hNn+DfSFo%+T4_jptp!_^I2{g7!b<2QSDn56)5z_}t8&-U*E?K{#Jh)zIMha7?b^9B z43~Rmt_BC^8#7r{hlG2V$UMC_&M6=%KHg*Qi+nfvEPDK4u0gzGa-h5rrzb@AvyMgR zlv5Lo-wA6y!9l3ehfPfTJ_+((4P|8{%UDif$m-?TLr$YExsxjsS5}rj}!$ z!|{z8!ltypJ76#0D_yFVN7g9Hw(sMj{OV1zm(E`+&+%01C2(IVl?(O15-VZfMcMcy)~U_+96xFSFR^Dy zsZUDhIAfHDrim~y{6uU<_Ch5Oa$lp~IP%RsTx#4haVNS}1g~yeJ}Z6nD9BY8<{OmL zwBx7*rK2(ac}Y4I_gU5`Ze0$ExWLYWHUkz1F{lEkB`5nqdb7lUU_y>TANT z`UIP)!@i`Z()Zr+{dtXI0$;|> z9sBc`c0+;#fXqA&A$(lD1(XH8e z!zFD$u{AkqNQ-s6Emz5XdCW{8^wb1yU7c2=>ws=LWId72&}iI7QSZ0>lPlB$L^a4^ zQ0>{1eRTj6cc`%t+rXKOQDlclgVKC{Nb~5)Rx&$!MD=cSKH1Azj;q+Ox}!S31>$*kae7#-{D;t}tiV9r#!ls#XaL6E3#GkZ2 z0iaBYQz?A?&K>aCbWBX@kgs5&0X#u56(MODO#@m7wwx#Sjx5!0-Cb$BUwRQ2nl z5;A0E+kV#J`#9(&TivXwtg<(a<^sy0?%O2fH~1z?7=2iX}^L{N|> z&~SJbgIaeV8GltxjRxg+Rp(XxvuC}2e5yjp2`<`v(l#%{TNzV09#PU`i4Mr_UF1Wj z$Zoy(lksEdHNMrb6iWiecD(Xy-WiQ)D)@t|8xD=pb-$nuOR%uE<{f3ZC?YV*^8HYO z!Ion@Rw;oN8Xr%|p+HE+Js9FkG9sP7L?UZMnoX>~+0f7crGoCV*5>B0Pt0dBS^Uas zvfk@lSI;hY9;9E4kC64pDbkMWt1}mZBS?SHUlm+A-}N86DSUN!bxan)n-7>cf(A28 zz_W8zI%Z(@F*Y+(>-GKI?J7V(<5*eWz5!ekR!CR$7<&Uew%D+gRI=a<+%GD(_>X%a z=3(2#L9>vI43?NcUG0W^SLSoN$E}2nK^=viMc-NC&qa_F1uuLZa2&4cczd}Z}kzq7BkZ$2s z>65wlhQrkN+Fo&ZY6#Sn@1NdsL5>YghIpZ;y60x_D!9bL%+A-CSAcaiJ_&gD@LtXI z=Dl_M?pjTP=3nepx5lczS)nHI}S)l(9J69qjHOPL`cpMhL1;{vUUkg0#P0noGfo! zLgasDcvyLfT4?Pp4u8~Z!S}Br@}YzCre`mSa>nDkuve3pr}FvU!zP)?c|}AyBq5eK zBx5WD(pbpfAi9WGMz(a()>b1lWs9m+Y*OsB(GfwB!Edvb!*}K$?%!ei5Dm}g*Nn~` zFj!k1yf)t)8u{|@q2k)wklnld-de7ti`SZQ$^%My#@1FAg3&_}d#FQOIFg!_%vxy!>K`f%WJ!41<(# zSptvfw7D22pu&wM;)w?YOV}yk2?VYg0QeK z>{IZW0p=h8s}}Vt!}$?=Fhck(D9#F=ld*7@##aO{b-omXEFI>{w@dn+N>oWdZ^mtG z#2Fl-m+g7}Cw8hMvk_4T<;*PZ(vKfnbzkFgWb!#yRC`Ox25!nt+sCnbhwk3Eh2B!h z6mXYr8Y$|QNtXV@d{OE+D)%M79Q_5v;H3JutF-~L%Yh5)E0^?>*l20>pt_E>qJ+r| z^K}NMx|vN@;j>me+eEuh@)6UT%7jCTFYU7WPVuJedyt2 zxqQ7)?f{tTIS!pg1PD2;E!Xn1`VNJ8`Z_DxMc0%B9#QW;OipV-!nHTEmn_JFt-IT> zU`X)#?QdhNm0e;#6*NwyzFKeAcI_&-_*qbbHCpu%MVUdpsaH@%#S&TqIOXciUu^dj z`S$he*YR=n06%+Xd<(ogGcg3ej0>5bN%)4^W->a}$$A373caJM<^@!8_{ibx3%u|< zdjXFpSU8Opu~+ws2nlf&{^^Xhu%rsg3li*?r|{EP?|pYHfJtOuerN z+>SoZmPr0t;mjqH2Uo4+8E$uf&5Flftj1~pP>b&nW=cJVGTMAqttmT{q4R!ujF$5h zM}sKAyclR%PBhy|lT)13vCg))A|lo|HaO&bk^iv3fiZpuGpjrT>xbUnnDFqbfVB#6 zktFQxSL`GPKICZVz7OWSiQA4s^C_6=IJJ_1G=O<$CoL>Rmz|J6n{@IpOfj+VhiWgE zCfzdIiIwkR3(QJo7tdr-tcT*Qw%1~>&*2n(0$+hk6O^*MvU4|ZjMWEj-ESO*FXZXif$bKRC_t$Jy{p9z{?a5up#Lqj!~4IG@(9LY;0nz zGs@%aOh9Q74h{m>fuqZzz@j!ev;JPqen-bw8b*3qopxAUs&$#-N$-I?CU5`@*W1aF zarJgmQj28?y|A%BPwn|LAPOj zq5uz1Hb?;&TZTF@5<+akOs&a|#{&Bg9&|)H0YfR(ojphd0R(eNImkHV^CtzIGc-IP zwZzSQUD@fBwKZBJHt;FKODdUe=(1N^E74x_d;9*q+LRR*j2t_aOH=_C5?Ou0U9HeR46rZ%3+_qyRlP@xlofL@)}DsXsH9M(zCdGvwfHK5&DY=c8XbH{s#lTX{( z<-vLFEj7@HBmhkjO(P2{z-=+UhfmQjBZE{P2N!38!05=xn8ZgiA!k z)yWBMNE!*2;xY*s6@L1}n$~4-=<_PU`SB-gFva{x5@0%(y^RS60x>Z$Oj#n?KRY`) zVKfw9H9GgcV1&}|wSz|}3R6H_weF*&l9ZO_=D<(brqwZH*KLwlu>)OeQS$FfRfn<; zu~UX&Zu#59#PL?O!cxIwOI~`f2blkS=(!EJbRSBw56^%EXtI@57ZmJHYvI#OhL`Ju zlbF4FdcN(ON2SZ>x5IAe>&2*e96a~v#Oc$S6909y@g5z^`UHHkg5Q7D=yvz#RVRo#;&eQ@Fs>j zBwCbX*QoSCnh_9q)6{gM$?|jUWj!Qsi_?k|?~{1wT~zB;jvbRTZJAie*S(^PuFg8I{!ZV)~{(Em(|{KW0bJ8H^E>a>RM==YFqjf`hho|^Y~ z&76GZ-qS$Z;4Pn{613qN6bFc+Q11H>cnfHxRckyhD5%DSGJ0Ssn_L8!y+@a*`*mD9 z*xA{~+~n-WmKz$BN(@*E&Lhn=E488M4GeJQ?un~tR0P-Ci4bcDP9@Rs#hJ+4Oq3uD}ILV$K-B&Zgo!*9S5aB{ z=fTMYM)FW(lc0W8I`yl_r|t@d5T9KNFdsPawq2p1Tnf0Cz^}c!Mlgj98aT$*@Awh* zXr~#vv3%Uz!OWEf&dQz@+NV#esh-f2)sR$U3ko>@%i8P+0zkI$K;B?|MQ_yO2M@;Z z7S>M^lT(I7K|k=~7=5A4?0skHGCzGFUH+m)JU|%`*>Sm48r%$az6#Pq<;@T&vAM)~iGm?)i=0Dh7^nLmV|8k9K*O+nw?Emvitx zpMtL*op}P*!W4FDXMHM8n#|N5P*y0zRPW!ivb21&KNY5_1QJ(nE`YXYP%3V0R9zbAXxbxr@kRzqs70}d^pS*mIqNOQ--$`u+dCq9-WGI< zKkzB3?DZTI`0Sc{@pdod{RchCf@68o$+y`lzw&7@vtoc!;X5oxR9*;87xeVtx!!0~ z+H$3to*Ug)Y3W4Si)iyApfHB&9zU&j?p(!_CqTAeqeAAhx&cwGM1bh!MLk$s=N=n} zJB+q0q5)z8_fA#J4Z5tS=w-i=^2@jW)bIfC(gKPc`J` z$AvPi(Ogs~%^$20z+b>uQWV&?FRFyqt(7=U^QZ8{t>0xM8ueS$O| zJ;h@S35$cgii-6}ia$zSJ1Kq!DBpncUO;}~PJT-)59zb#~#hW3QyV3@Cs&| z!UC8as^20+kKfmC$@&r8e|7mK#WH5s0hN%ZtEzFwrKfZ3PR_{n)9*$tgB8BHxi)9L zDx^#e5FV;GzyJK0F|4DxPgOkL;@Oy%nVLH3+*D=Rw7Z0TNJ2t_(C^{oVGg@(Oyy7n?T_98 zzb8d{fI-I)m(Y5_t;RBs&=J6JnZ?D6#{!};RSex#LFQtn8^ilClm(}3BsU)HV0U0R z0mxoLkQ902nfQd9ibmhf^(yNDbASKZKI+}5HoPufQY}Umz-RO#yC;9q$$lHrhwS52 z`YUhEMIizhM!^#LFoN$%?{WC(!piklIRw)rfj7v?l14=-d95$Id&cwEFo0uY=hTIo zE55uIBF?HO^FeaF%c{1#)$wv7=F`q7Y1!q$#&^H858Kiz_&+f)m(R1Ixz%A@j3a}HCqp#yB!)SVq?FeOkh`Fk{>5;6X0kYH9w;P8uZ26HDfEzLI zv5$izM)*9keolr|v5UL?hft<0l5M^fH-+<59-DhoUZXx*W>rR{W7jO2Y^*eOAA2~TgB3z z0ewP5bpU3c0j1z9#jYO&|Uh1pv6%!6HQzL)Y|#wzk=G|Elot7W_ICvrL7B5hG-0O0$&2pH@_uR(a$A zCF$;{Z8 z4j};*D}at{W2*;Gp16Tx0VffzQs3z=PYq;VxK_Z9ges8Og%X71r-56W(1g`)Zxm3v zc9jf=6^zxdEIo>fYR%KlMvkg`Nh9-TI_2CA6|VAoJ|&bh%xtG_fBog#UC8zJnX(r# zxU41R<;Oxkf(ja&sm-(}OYd;>D7r&xU8sakJ$E5u>eb9={`Mg>`Qe#L#nqaODejS6 z&ul%=P~eMzTNuYxgc?zo_<6TmqQ2b=$Z~iV1IGQ0qNv18+gXUbLtXV~ZFe#wQ@_8b zEc^(k0yh@Vn<(m5gI%S=w$I^W`^3VZ*+GVV`;Psv0vxN784H)+qjP{m&f-9kW~Fop zIo)I}*7)+JrRmyLXz~!$n|>!VX96_>3j5`q2s#`3EGn=&xVTfVt88n?U*Ju%s8f!6x?$+ntDz}rtw#3?@-Dnu zVaNA7)8ojI2)s(rQ~%V1YBG=HV|CNt*l1D($AhkVJ9vnrLMSz*Z_Mb z17ut<+D1b7<{~WOVc#5u(5fFoTG2}EZ1@dbw- z@Y!A@OtezO%en|>Q1wQ>M52u0P!H);9HuhT(u^!DJ9jF^l|Vqt9h4T@yJheOn8{o7 zcFxMqMrS$l0LD5`rE$s0ed_CDlq|>rAzf5TN=rjy?MpD`r{G?V{%*GvVdM@;319A| zDr0;O9E2d>KpnlWukVpjWm{h#p+h+)hn6~l*ic}ilxvu&b5kMfxq7-(4Z33J;E*1A zGx`g(geBNwnmyTZZYmW`CSIGB1Q%AoMXGcXhY=S1y*GfYCfYK;r^0oqh?hXgMe zVLi&g@AFk(-?Iu6K6vc*^+`)go}JIee(4B5tfDbt!elQ}hh!T5ihS1Hl>~R+hz+Vd8I&Oz?dE=sq8VBR~7cylE_hV zJ;qH_{RUrTmWYZic|LNZ?lGgBW6~U3!#~4(ENNo`Bc*;V#LL!iE{aoioq5yyf+>HD zJHXYftHw{TbY?c+X8S7|W~dsvMTVM7w>b^Q zRxlD>vx&pEfq7Z=Z?rsgyFom7WAhHx>EXjPA72B7-_Og7ZRk1ESAx^}>4M`a4Go-) z$f;TTDasWBH;qkA70~zXK702BuLIhY1Ya1|bOwn!AQK!M>DU7C`39Yxogi$2u>>+z zUU0CA^B#yy60&{EkniQEK~_c%y=d8B!N4BR|6xi}5xMwTTV+gMmxaT$>1afAx1!>KeSPLPOc-W?~}nm|}W zi|);%I_#A^m@9=^p@o3dLpu7;^hb@Ej^_xoph~^D8~O3$N9ZTok(gq$LlD;U2>=g* z)Yg~U%a*gXWWh>8&QqR#Aiv^vu02@gdPK|#Ou<&qmWcJ$Mq5uk&m5bDbK zfyq0sCKhznBfA}2!SwJ~7#bX8p~$-B=YYYwyeOr9CvUhQ+`W5lW9MOhd90(x#!)p0U$^-6GLka?6x$Cb)u;uQ&O6E^QiG=-{FWu#Yg!XAV4W6To3frSZjAdVYnYIKa zb4v_t)>KqgTdcxbZ7Q7y@5*JtDzne20=@9*WYWebeSHQPF!FGPJ3JZ-V+$(+U$yox z;Kpw*4Z0#j93CF-{z9cw_e_a1<6MwAV^1b=;Wma(TLl6xT25R(Rl3?OG0}mMNqxHQ zN<|n(+^M*JF|*gujPU;tuY4kN(VDB6Jzaj)MO(y6D&9^>@6X=**dJtCja)sX7gD=_ zpT@kZ1am0<7rjaK*I*qHQO2>8r%(S}dOrO1>(B3$PcNfRKw7I)1F^MhTf7*-+w!qz zN+QJac@E~?b;HtMDF5#-3G-RY?3&DujqX!ydBh!&ZW7cl6}QY8wF3S-$=0#=uSvCz z`HZ#!QPyVnnZf$Nu)+V1c3lu;X3JG(In(hWLz?B3t7?!`e_Hl`|GbQZJM$)~E_uhu zdfLY&pFWBkgPbG(jL}`YOVRu-xrU7BX7&cH^;!+z0X4MY|L@T;Ps6CmyI0cZ0MRmo z#JB*(R_JMmMhiERzn?%(>Ma3lBnM9THw`Mq`K zF&)zs18oE8=KaPyZ-mp5G{X1?uJRV0R_m0L@k?0 zOBQQB@{wZFm473;UGa`VgiTBMP|lq!51u=F{`sSNcG)yxo*wnwE|!INH|v|#W8XW5 zj7fGF^ZL0tx@rA)0C00?SX!8$|Gb_a&k9t_jCm@#n#jN=Qk$jPQ*A>mv~%WPSK{s| zo2H(<>xK%lo=|~|PCHN84SqFtH@TxnSHpse!Pe7;XHBL( zSt(v!Q}NR@6(o8vdS}n8hp`qmUZkGS|GQpn&b=JfNQ)kQ#;QzhNi3}NiK2|8bk6Fv|dJ0GUJ`64J?%~F))=F@*BAHUw?J-=nhwSD_Pw}42=nJn%mWYUuVzwuAE zRCU_#kN$goP-`V3QC7ERl=7v^%ja(^Ye(kgzV`a z0zNt(`uh6#Y3oDQrkl_u+7@-{>$nZl(MNnvHU!eYg6J*bVMj;lYF0zVC^jEvWi9el zY-Z?rbfRoc+}j=3Jb_)-8{d8qo|$(}V^B?KnyOdqNSu>&bQcCfud;(O^4BYL7e1t_?^yqBiIqB#H-&(M&AYZ@^0yg;Fgg$b6LEf~}*S-&J zRAO&|2cZjNn0^QTRS(^bynD_G8k3Yr>C~kAdA1l_);L=5*EKg#2?_~qtgncBL5=6l zOutu5$N_3H92z*8z@vCjRDtRKS|D)c@(M^%Z~@t!uU=Z?v_MILclQcy$A?D08}m56 zAV^MoBJ~1YU-q3@AQIo49JId{M+*z~hP$yQG_!Dt8CZue{;K-Z`^>rPA2U>qkS{_l~ma^UOs!WwD)iP?+_)9x$pOrN5kOHDL&K_Hr_LUvQC50Habl;W&0nkVYi@kNy%W^0(A_4K++U)h zzYDqvmf<8U*9y|oOjK01zm@HGusw2dTE9e+Zr{G7loWics53#?XVL^DhlwThBh_3{ zn}U!xFz~bmV=Bn>ZJ{1zW23HxUoyuurvS#MTK9DC@9)QG1s49nM|A5e)BS_2(Y2}m zSs&v$4ELqCV`31pqROsY>kI8zKpQ7coq+rT<~f0)hFaj8+)2g0{_Bx?Feku5^mt^z zZACi1b)p}uQaddRL`Ymd$05@M$exaXaR`D6su*4JLS(k_rFeGBz(BQy8InXT%CJ&g zE{E`pBH>d{ec+p>L%EKkHnSIL^^YGvfFVx)^ueSjNZH+sKUe$#8=!)~_+fmuEi&sb z&jSXL`FLDK4*-K5u(^Nk`zu&>16T;)K~w-3ZpzZ)*FGALWOVG?z6a`A0M5}$LV{`S z65?kemIRtI#5vjI{XBu$Sy*I(e!qGX6ZD*zLBq>nybW%0gieO8ZqU4S?;}G3;B)8T z`K%;t%$8Ynk`_mIIC4ojubN%4W$!K9*!uNAsPdYY63N&(YB~$SHkKn{vcEnB-K;$Q zf;Lf)j;%2$_!hUc4jmQsL`!O~=cEobKOGD?FStY~MR49OG*21gNS_la%n`~d>Ztm& zFie~>mf-W{WS7+CZvp<5AdbC|0Y?bFfPno~c=j#6&(ILizZJj4B?P~4j7DM_MwA@w z;&*886d!vEY77YE0LW`KO0hLm+7FU2iJD*Q_c|6tY_q~!rWXM!0Glw#!2oZ_M zFROd%xmm?Lv$8YCe~W|AUN8InjM({G*Ief>W7d;ZX^SNGL-32x>HXp>q9yGw-qS{AZI=w9!N~fkeg< zY(cj*8`6DnGXhTG$F+@hAG*fV0C<*fEkI+AZw9$(vaIt3Om%`t1Ux8c>2}aH)|i4K zOG4W%Y^M+~h$}$DXXM@QCMDkFkW}eZfk}gxzoUAW9fSf{SLlC0R|V)991;TJC+&Mu zrXlmrohUh=!G$OUGpQ=bi;Z)Jw*WdkdT+pFb$bnMu!;yPAanvP8wet*bl;o(8O~h4 z&BOCi^|;szdnhjv53Q`NLH2s{>J?cSj}Q!86t0RBE*`4-Mn*O0p^4Y8&QF)rf18*9 zbt63_MBU0tPNI0*gWT3x)xYCaOi2Av6Ya=cRdd9}#frsb0 zKgfcY$#?aO$@%k7u_wP9tiF!U)K}StL|{2r4X`P7|FxBJrBpT{shC4qbO4M`U5~#u*1)5iOrRyLM^R z>e(vHO7brO*u^I;IhkCwro4M+3d$uIfY}l3`Wu`Y9Q|8kvZ>l%oMfR9 z0d1Ben9Sw}HRU~B>t14IMt^I5=*uW|kayxbLSyZPxFK?y`^g9Fun*3RzIM>73h zLBSiFzqYm(3@?PqPcF4!G@^Z7C97K1TBfXA7)XSieAhhk1=qVOaLR#T*JUEdmttgm>**IX=^Z5n1t=bw!!z)&+k$d3P?FwBf^ER27U#t5 z^d)a!Py=FPdoYXv6m{Zjq%Ds=l&1Us)-W?0>FYDBDl+FFOI?9WVL}1It%$6wQR%U} zuSy5MMk%cuja#4xIPN4%))$hYbS988OiRs`zw8t|di4F%r`(E)sA~@L@_u|NUY~+H z4KH)^=FM`}Q5QHCAsC@+1pL%#iOX15HyXyWNo1k8fdPxxxznfJkw>(WP^;S_(qlu} zZX$bV+;RUA9U2UiQs31YOhjlbEqU@Aaygo#_l}vRAP#D2Xw>?y)qM~FtOn;%RW$Yz zeU#AautrSaN=HMFXVV%IvjzhnHbSQbax>D~06)Gz=wT0TDIyW~sQbfoj+MoxbbxFGI3=A7H)gzss z!J}|mS#(%n6(lBx`7udFoDDZ+Vmb6rl)lce8Oci=lRIdG&<9D$(>0a?M)Yu#w92KR z;F2WI2=MUW{}gJ8q-Z?oXs`ug6BQM$7SuRGLLsK0^6igfZu;cnw>sAXjU-N%cBf$X z3&3if{Mab@`OJx2*+oS^5Ep@8>>xptpMbWYO$1U%pl69tPOdJeAvR)e1m#u^O}~fy ziHU57GqYQ43kVsZ@-n(~ZaegRw z5XmDu=G^fG4+Yj1GuZf>v-K#H9jFAfDWXbQAJuo$?m>6|$}0#K}e z{rB`(loH$9X|zQKhlaL0+Cee|kSOoDF9HN2e3yuda~UtrHX;b_&7R&~eu1lO5lt5! zV1MV(+28h#BaiG$Nde6F4%64i$8*3#`0^#>V-OSF*1rLXkIU$TK4WC`24C<@k#WK6V>k8{0N%|K+l5g0nENdw2l4ya z*o8HRaX9{R6sPnU0t@0j^}HfAR^`QlI30sJ@(+I z?zO#x(#7A3`#xP2STX$<2nbh~DEW&&z?tl;g?=5>1@}H&hiL>zOKcHnvduxN>9@Z8 zWjqVqy@QEbt{VqxSm7`g9UTq6h+wR(l@(H$W^;)r8|&9HiC1dhfp!MAE1+XA-~o@5 zwODx9(z``r>4AU^1uofsBRcK$fV)CgiS*HPh8Kj6(QZ8r$&8x1V-8}+XFS<;G?S`*Ieq!4nV6#I{=HxQk zP{=+Z*ZEeQ3d*>F$o8hT7-JJ3pS|-Kw2FSy?J0)jcC{aa_EmK1*-xJAJF&<^WAv^R zEo%VDIthma^+Itl5Uae=69l;1ou|iaE|myg1XOP}P!D4SA#XC+=v|I= zh{ICCz{XRr=^5x1we|FoF2X6}%Pe>!u zD;Y^iRNS>6R#7fxXrf$ti_42&UP3DI75cYZsi^RtD=R9f9o~b421^?V?ri4EI~1o) zy~EYAVI70;bM5NYKu}G-7r-Qevqg{Bgh*^hG6auBoW_gOr|)Ad(#4BS<`R*}>*#m$ z-w4{5T~qT0U0&S(^CTk_E_=eLxcpWhm_H2r_Uw}j7{$3d>0&%$WhThuz+~V*aNt#I z>wOH?0+SIf|Hn{TdAw}RtkWYD6D5c`XR0qd%-FGH^K)3;BauRSj}EG=!q^DCbpC32 zu>6;h38Aegq+^uD4Nx6%3EHLV6qP+~p@n&Qp;1w24(0Ytq8kizobB}V4t926zN@fy zBhZ1hC14rYYKd5-jd2{v)$#hJcNc*^iWLApjthbdoi?zc(vun*)U0BWNU>*Gh}T!z?5gG0$9r zgKlfmGE5R!S>ur+#x_D#eorHNX9&`NxN{(XgT4sC)ZpjaoJU%MIUK^N_i;I*ij|j> zyWOBZUwQ#lul=W4*x5nT@>%;ifF2W5zKeiBg4j90J7_w^j|eX7CB-bD z9nL2whXwjgK0C#0uaOaQ`7XgF1r#w2PfPxHgZK0Ek)*@aB|GvxNZX}Gg%cAK_&dsI zKbV+2EH2h|QiyHkk0^{Gn-e^}z6m)qG+yd%L$JHiwJb2Fd4z|{E>iGG3VCLuT8F71 zdlF3yG2sa9D2YTZ#q^Q)g-)%%>hWX%$q>T8Z^dB;Ve=wDMx+eE7XP@|I=riy0}~p& zrjvy^x&)O^8=!)soKVxwdZp=UI^|h6t>fc!bouObs!xOD;7T+GR|W?LM&~QE%^4g( z{Rj^u00}SJ+ufX;upvtyJmBdY#6<)u!b1Dc7B0WIgoGZO3M@W)Q``~~j5n7*Mu>0m ztH4bK&J^!Af5`1N9IUNFp6nbN8q!M*Kyl_Qdvjthh`6|fQB?0279K7;F-$#m{>vJT z#q6y8>8cKeKf96JADbq&1gfr>!s~)~H{?b=7V%I_f~U8lAKK*zX()_wp2zQ^pn>Su zRR~DUm(fvY@XI3-#A@4<454rUWmZT?h(#%=c&fzovGIAm2f$~bNi!&>KKBEbAMl5B z02`&j-1Wb{FK&4i*@gBq%=e@~rI!jCBfqyodkPjYf(g@jO{8q+qM$j>>n88C=Pmjl=6D7;1kFC*fRPX8QX~lMqPW6x&^<7Xv zAhUZtZ}20ul}9+W0c)UERIVeEdQd0fD#y^}lrNXyuf(9V`T@E)Kv8gKm@Eq0qmikL zvZ^3=vBTOAs0g9C6CF+PK0(kN=M_tMdEo&lQ1T|gA_LMjS;Shb%BlD+V~=-+>!ghv znV4+<`M1E86VPQkIyi`4%|Df>7CzqtwiaCcqG|6xHDYt=!X1nS*~89mZDmF7>4HhM z;J;hpsuQ9&s{Zs2A?mFA_cihkZf#p-O@VTXk=>vF=BdX4aVlGO-$_USiA5r2w(Qv& zvy4e|O%+xLEWfhz87ZULAD2ESU&e@EPT+F5q;O5aN1#DJ6#5HoB606QD=ic|2`^6& zPod-k1r^|?csH3f0R|9NGFLXkc~8$;a902Y>r$J*1-#pbYENax%K*fs7kJ}qRvQBo z{&$|HMl#N>Zu_(MRtxSb`p9|d>Fbwa5D9Wqc+4<1mfD>uQBzkB2?~PNU-{V-jtG)* zTtck36_}Zs;X*+!_;Z&KvocS)io}PDO@h=DW^lwHpYvjRW+*G@l+BzxhlrN>WU~aC zfRjX`oBO`^q$qb-P2C#9c_52~QAIruYIbGkY$+-%G<)m?!sgmCOizl~Y& z5%33}#6_xMM@mnB6E}R8Mow%S+Yf&!zh!SluZY&EAwa+%U23I_;BABQZ*8ie1bW`C z6ucfjn&XFmd2NMwL-?;;aw4t45{v3;e;M16^p2=ox8NOwxn@}@LvqMX5HBA+dKAdN z#}lASvXY;#fk7|2{9WWkEiNkTA3Yh^6VQ!^3F?tRhM#`zuG_ST5RJyOj^Z+=NzQ}N z3dJL=ddNNo$ULZG-BE)!^`%`xG6l*TMAlI)v7(Zu0G407b1`#MKkMvt0^I=*2Gzza zYFZ74KyGSeBG?wLGDWag(lopc1&bweNitva3n8B?!sVH{1fUgEK{tmr6YH&WlvDJT z&1)i^9nd-^E2T8))Of5usD}kz6Fz@7)Q6F0IJPp5?`*8CWv(qAa6R&?wqSpDo(&=u{F6)`x);5$`wH$? z)~N+l3*eGTU%}OhbO8j&Bk;Q{DVfDBheT+7X)Fo~QUF!~+_)-XlW=S|Y7_Y4lLf0V z7DB7OJFOZMT1rab=LeHQfHfd(;k?S3S;Mpf+xHwc6>bK+I2B6jfU8J)AvY_-24auX4E-ua>um+C315A&=|3*T$ z0(2d59up}2E$%|}ZlCZA&Ljb`I6g9m4xKn#EX9F)2##yAXxI4rHvoP?SpvNd$dCBk zd*#6ec}762v$HdF1ry`yj(|nrwDbk_2%5#UeDEM3P=Jh-a$*Jyn1bL?=_YlVE?t7lk)(FA3G=KBR9LJV9VV%5ILK4^EoaJ8>@t0OPX%;r^a;5KOE*H zAz#6mD46(=wZ6#;o@EIM3CYPOYRx_k)S69og3T6BzCLEcO-g3hTqxK3_V|Uxd(lga z7{Gwt(JM;{YSia%-}W&vktwY#?GeYC-s)z!0oQ0w3($`*Uq+N$Md&d{ll@Q7O;08cA*E|MkJZ^y$eV7}M|ydN19IzORVw&;#^ z7PWwp2@)?vwrF}FOV7zK7o-BLa(nw_i}&{j)F$<}#M$J7`i{WL5lquh>gn%S6L?^0 zj^Y(&9=MkN`vAtWa>xo=x6~uas;;aI3JJ+>1veXqu8r+ouL*VQlP$`sYHA2+ zZ(;B1J4VKBE*Mlp^}Bb*uEIdvOw0G=8(KCCND{`%`jtd8PfwRjeAj66x|^bSC{fI1-I^7A_? zU05Ciu4LIYx@REgR9y;dYeg3gb_lR}i+cWC6}nx(r%`*Z)bD?h7v0^6qjw;UgrFR}Eq!`;eEyo7OO&v9h6rd`I`Zz_t4}T$UpS$+h|wBY zJa!UERb8E(jqMwZYS7Vur-vV;4QTx79oVz`7^#OG5>b)SE^+M0`xP17Ky?NN$w^6X?C*rNa>vXSp<06$>f>+> zA}Y`XFI@820UaG3I?xZdwtC{?HS=2widzGy!m(g z5-_oNLA!$-3l(*0>fTgs_4I1Minyt^|Ni>%6QJ!;!~RmLrnmwI0y z*C9LWA@LYr!WHrUiH}KXhOZ9y2`d}%yHh&|hjovL58(f6Zx@a0f9xlGZl4wse^Jo~ zAwX4h1DGf&Z{YQ3i3uQ@koS$NtE-C(@6N3V5eRX5TR-9tVWxF5sk^iDU7g#0kr%gk z2VR_zy#?bUd>~%R2&Q!53Kokl4Fhf#J$H0sdIG5|%BB+qfx*F9d5vUx7=Z;E7QE)x zdOd+vmXqIG`uh*#UPMMupx1NC!A67VBXZY4)VX$-FPD5zWwwrrYJD3bEd&z`O~EkS zGz$Vl&9{6=+7_U(0zg3Q|AJP}mh1BkiV2eW$nzmknJk;c~P=JCt%>Fs1Q=#4h5fDsq&QM^bqByvd2pbfDP zx`8GRMAFA&LV(c%l>#b&T7gUlmjVyZ&)~yfr5ZoBU0+p6?ksg|s-$_9u+URt&vot3 zg-bCZ$#PYaZlLcNLk2%-7Yc zh`T@;8cv|H!x{mvf%cKPcLfbE6#5A-XMjXZOmJuSIdPUhc)(iADwnEF>amqhLaBt2 zcptJrgUCp71kegY9%!hbGX*Aq@MS|0+ltX_QXp*e%(X?@OC_chyyUAp$CqD@CiEYC_-=mZFYoL>7e?IORTSI5$ROt<&A-Ce{`udx@nPh}xE=d(^_!{Y zHU~(lD}C|)gMRO?tY|9xZn|!RG!ig*onz?=S1#0kRaGEmS5)uy+;-1vEba#*&vRP` z7Fun}O!{b|P3-xLu^P^Ns)Dxz-2Ww~ty&Krad}nsb(_8RjTHBVN;kjV%V8{(n~sV7 zhRu^zIglo=A*N60Dg=F9$WtKn?IERvQ1hHW)etWo!lKJvR>>Z7)+Y9IWgwAp%Utnc zE$S-XzId_!4gsL}u-5g|maRN6V{X~I_b%lUcMS1V{1Xio55vP}r)y{?9Q0*r{(V;R zFW=W0NgBFNwDf^h40pesHko!Q5@VGf-25)b{d%-o{{N>P;;&vU2^H}UvfyYIqBf3_ zr(9s170)7i#S^{4h&4vZcZX~B1+)G`o1=K7^t_Epc8Bp$NSOU&^@jo#8f*h~27~;c zvS{-f1D{tO`#-Y~zA%y0bcJQnpdt(|a;bWCX=)OQW$y$BjY+Isej7^l`-PMp|MPV+ z)S38nze_}(HrvQGtAFx%l%>;-aM~tT4FK|9nE)-GnQ@lq|7ZBabZ9IeB9=|zZZ;c- z4Pi^hw{rV0oSSlFj&v=0$n<A(!cHBwIaWqW8&QUry86e6;%eEK0W91ozd#0{e2IX>hxD2%kF{$u9slDOf^OTwBux6gyedOaU-4Bv%2a8iLgvxt zVfJ~Fq5B!L*upY5(iIhU<2}2#duEgWoqs%JWl{nQb1q}KBm zALlX06^e;F#r&S7mYGE_^&M{yFtc3d6!NrVPADm!jG|L?*uG&;;KSqE@CadxyXKfLj> zue&=-zwfuc1h43tV*0QCi}PD@%#JBeZVQ%HcDOwh=6=d%TK{6Kirr4ymQ^#&`$;17 z{Qsl=Qc!rY>x1FUNzzYK@HH^oFjhYUXZWD-`?dvo(gwYTv#W_QzDhnz5<>{ux5M`+ zGlD0flDV)bMsf5e%V0)U(;@%*3x-;t52)`gev zEliW6-TDy68n|)O6PkAA#?Fz~iacA`{e6~awrkVAo#iA2e|BBm)+n(rN~nVO_kK&- zpOh#3G&VHiZ<@wbi072h?4G>(QfNs5NipM=pnx$JTI1_uck^lKC+kzA7*t-A zNw<)GoQzqdIF5BK!*;x;Jt#|kBWehn`sZ*Gkf+N#AW<^lf?1g5iH literal 0 HcmV?d00001 diff --git "a/docs/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256/\345\276\256\344\277\241\345\233\276\347\211\207_20261020163407_533_5.png" "b/docs/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256/\345\276\256\344\277\241\345\233\276\347\211\207_20261020163407_533_5.png" new file mode 100644 index 0000000000000000000000000000000000000000..d544fe10feb499f0eb420f5fce5e9bb44f312fd8 GIT binary patch literal 200506 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

~t$68#i*NFffJ! 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

fvc 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 literal 0 HcmV?d00001 diff --git "a/docs/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206/\345\276\256\344\277\241\345\233\276\347\211\207_20261020143712_527_5.png" "b/docs/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206/\345\276\256\344\277\241\345\233\276\347\211\207_20261020143712_527_5.png" new file mode 100644 index 0000000000000000000000000000000000000000..fe351f714b084e53ac419d2f7519daf304f351b6 GIT binary patch literal 434059 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 l!-%o;JvLPN^Et9Iit^h>0}( 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

byCmHSTy6oIY;Y*2D?yGJtm|Xe% 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 z%0DBN$HPBNAG(VbA*dp@z>}>0j=r*T15oq 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 z u9({@jMHv}hRi7R|{qm)pknSj_w$Z_7pjt!qZ(w-KnnuVu-?nCWh|P6)>7txm zx+n3I;~bhFP4m)Uyr@p?%+tsO zsk?Wh@5i!S4{L$|XHE5|M`%&`knt8U0_Vy{4J$t6wBLX>G7hT&Q`)Bg^X@zvi(e#aXG=$?32VRT}m!{E4#=FK_gfwkxt)s*95;hEWf@ zN4-lpBTW)NK*#M>nG-L=WmBhnYfFa2Cp{A4-%ArDrZZ%-b4eAmH`{T&2;uM7YvMd| z&2!|$@Pvtx!SK+HBJ0K5rt5@|Fq`CH;4~aetubC*RdpohYiV6FZns7C?6S>o+nAHi zeW6iZ^vMBl0|RakGh$+X3Ub%H-t!&zoF%p_ye!yJ=>DV*1wKeK9zqMWO3e$a+7e)^uq?RsiLwl5WR}8)u?0_o~5G~K4;!j?65QSCP8WeT=s?L zmLj{5hJES^sLI{6%KFej7A~_`pJ+W9rXBBjd->bz*Hk~Y@7>oGC7508F6kbz1M%GN zy7*-#cL-ov<8(qpnaeeV#@(O;#)RaLuPi-{2=$BRXbrA0E|-o&6om?{6|F-D&FfRb z*?J?dk;uj`#nn#Ty0gkX4Rl=H?cC-fj8xa=2=VlvaWwonb;_rynR6L0eY7iCJGcK^@{>L0@$_3Q1K~}1=azYrY5jt< zLOG&qQ8W$vU>thn@34#6+CN4p&L#hgUHSN%NELp&YS#Na1Q zA7d8YT+89^E89S$EhBwtn(t2E)63mAIJ#;l$V8D(94d5uU3vbbLjgNmcT-|WvT@G6 zL)$iQ=7u9xev)RA0|E+SJ4Q{ zpT9N(6y$>)BNO4QA}CnoAee$Gqb7^vK6k>On0I#A$ zNV8v%G^kr3#8dLz|GsZcJ<(=kW!rYV%c1xN`R61BHML9nGw-&&qt4sU__9tHA4fh= zTVLNdECX|GcwhhEsU|ym^eD~o_@ww>$UyfQsuR!|Hf(9BsTt|s?@F?zvRx!TqDTwW zG?dx%XoPTz7`1%x`zU@Ne!iYC78s|bgn9?Cay*1L3IK3a?2+A20=)8##LHc~SpNiU zeP{}pfS9yw7*Ei|1x#3(&wp&)-OgS!?5vg{7K~IcsuB!V_l%5;wkAxxFs&3 zzAz<{tKd9b%6jtI>Miw9jzhAqD+Tk8|7b!BpY(%1`SEWf&(D56S)O`-v9)Kr(ZX@5 zv931LD{{jkFaN^>+v}g+-!seJH^ek{v!-Evc~M4;*ZXGtaL2W4u2P&%?O6)j50=Cp zS~fikX%ZUO7%w~M*DEI_U0X+^+ctXD#Omx@0f8ji4cXiDL0z)0ZE@zSycZPat*vEZ z%~-tXzKY7c35yEXkmz&dXQT|vq)KA%EPrZdyO2M~rw|;Jney%{ZRdfr<#Y5E@st`I zhrH`pmB?})Ce@Gf)9r1t{$TqbZrLNb5h%G-`cN+}gy{^m41r94PV>_A*qFLGoyvc> zRUas7g*?-#{~xVNUV&G#!7qXQf7Gkrdr|_2q~eCeC;l=ah!F>T{GZpOBuQ)CJ8S=s z75C`?iNN2KtN*-ZWfz-)!v{*D96#qD$Mr4$WwbpSFQ1_hZ;1YJSGwTY%>RB~iEU8G z^OvUsEe9vB|Nm%bGg5Tc2U~B}u2T_2{mY`eBKzs9^#^YXQ3?-_nv>#n|9<$pFc>q_ zJpE1I%6({_?_PaHL+@6eMH$F-~71aPDEUA|`D_VP<(s@6!=ZBbLGw}{QXHECgIrwpAJ(; z&?Dv5as4JOVbJFqS_U;+ivQ)!?fA15`VG=SlJ)_XI-qA z;IPjzYTA9F@vmLNc2J!Vq)8XBzAOW$N%1odR7rtXM`CN@#U-naKkyajCEm^t92s;W z3h{xG*f!xeg1@gL${YSf65f<;3jj;K%cLij;-$aeH?vRhuc(HQgMrqgq_qN`Ci7`m zn0<k%lO5#27N98N5K`J9H8SFNoOAYy_#oucaquv{doBRVXQ*peUD%T$of5rXA$$teEht|}GpGMv-M_695)xEf#K4zjMH!s5I7T6X|LPA6 zSKqi$dB|oZc5sd$NOR>DI|s+nf5@dPVWHvS?F%HhPejn(8Fv=&QmS?em(yUBU~xL} zZjFnZfG#NMr1bOL+=l^-vPm|NcJ9!wT{9gIAbQ!_nlg3^U)F#SE9JGuyz%gHfdQ@6 zf{|TqF-Z?a@oiuj_@5Q0GnKT2ZL(+Z$lnhcZ+aAlc{q0B(3e1iYaB|r!gm5SRdA)NyZ!=YpnR>Kk?|=Vl*Q0dAtf0gHpRMc6Ktc2L|I?2j35xF=&i{Q0 zf09;@RANmXyZhh2!GBxN(5mtt`_1+>^S&(c|K2Pd#J%Sc`%$H3wOzur|8jzzr#7@p zG)ZhOUW-t#a((rz&{X5$sL8!s*9O)ipoI2Z{5`gF7|r#`xTGW#eSO$RDYxudy^`uU zU^2p}@ajL`@uWxMnBT6}W!>P!*~k(Mm6D0bu*nVH&rd^X8QGm6ab z`a(yAQ1VG{#r9lTrR1LcUj<&r^z082XNc}PBK+UTtMf+RQesGK#NGGYz2jB=uaYR5 zMiM@LoS7$bR1tw?g$8%UZI!<-f|YnKvRrlltmVJAfcXSlknR+}^RV+D^1ym(jQz#>{tlZ|oI*zm#=s_r%HjJBqE#9y6{w0p!qB8S){9%KiuGdl z?an2&eFIhX_2-zG<>Sd*TsD)ujYD`@r7SZZ8L01V?5mlPDm!A*X?OVV@9sQ=aXSMQ zPb~z~oR-tX&)gc$P=F<95YgV<`%E1KBQ74aY%(!2g6DRd{Wv6> zZaf48rS7uL=(}2iAc+2i1~H@oM>JH#dQY9DCTYe#h&X_yEIM%Kf4zOHh;l`G|11oL zR3W-?6;j7)}V*c}m#$VY--lqvnM|cF>pda@Z<#1H=`pXhIj6q&f^mjf>eMO`*HA4lOXV7LsyfD3xy?Fv>m#3`R%iXvU z!DmKE=e{L_Viek;&{0Yw(zy;PL#R7otyZwF*9`3HZLD zKLk^1LkOq=C-L%tE&Tcj$#q= z@KATRA+O0~ule@%D|AoqKoSySX=vC&SSLnR)p>h!nL{J{4YvW2SwuiUxfv})E31i9 zf>&_iU^|>E`dV4ZbmojMzKa!yj7x&>KI4qo;Cdn0q6k7k8c1mZIxHU|0)lsWGDBly z{Fs@d=nn2Qv$D#3V*L^D6=z|chf?O|Q;?h$5)v{qGaK{A01in#gq8}stPSMfCein3 znO*(gw*3NS94u}l6F{)Z&o%l52TKrikU&B4 z?vHo( zow)^j8GR;|@X)rn$qU@vXv}rSN(Dee4~jnaPEL?aTnf=~W$A!w)fe2etjx?mlat`1 zuGesu;l@E{1U*MPh+d;D7)}Ql-v*uR5Lp< zMMae9?CeCKq2;%7m#BrfP}-#-*(+Ce*;Q3REDb_v97O;%FLrswL&A-9bjO#eYmCxM z+#gI?;BC;hNmL5+^}W)d9~K&l3=zS^^v=-Dg{$OAg9-Zre*AqZ{GJzmDv3?tEF4wi zb5+r>k-^}ZBVu+xrjm$2P$@#Smlupu1VKYh9bb%BM4P~4$)+qgA%(f|rf$S}V_eqKl(%0M=glIH%+a7Tv} z);&W*GW3n99TC?Mh)G0856r^f21ii|EF1oaL6&c+lGY8s)U#$c02!5#dzK65n`{u- zj!oEGdLBnhf$c;vq{K?XqoU+`t0C3ncl{U%$!R;Ot3k|c@k~L|d#$9N2L}520U5r> zKm$gbY_vk#7-3TK7~Qe3XyEX>PLHmgjiu#|y-i*tIt$<{K;6g2jr3&&RQqrxqF7Wo z-`SiDDlzTG?3unuHqWdRod&9c5!stJhtsb0Yo9(ryF4><+CAlV-W1*yFP8^dJ`5s{ ziVogPxw@GA;8Wom?LU=;yaQV3_d7Bf=;+zlWF#d$YkZELJZT9zOgM@%zoCb@lM6aA z*4Fb_|3R9!5zg>#+!^%twawe`$n)(FdN+kAr#-->p|$}7kd?I>;T@_DFy3wd@uSpj zyA&O>r%%Ny&fJPXtu{K!Oh%?SR4)6XAS`UVr!+k0^;E&in`%QZ6<35S%t$|f{@m4# zcd}Ns-qqJ192m&5olqer_CXHB9C=@w<@GaqdU|Lb$*>=X%-65t zg{rktBE6NBlCuYra`$Bfo@#&&q;XFIQVtwP&1)HOXlSGLFrbKE7$+Gr`H|9rWtZ$I z8220siZ3hBC&IE|aEF=E&e)3TVtr3f&!=_BMd;kg9iZ>(?u%~%^-DO;Jm8WZ@~Th=aTt?IWo7RGlFUy9tToqJ3!;Uk zF}6otCM{i!eSj=+$5>mMIDkYHhj$ZLXSm-`+*7^Bec3w_a+n}Y4K0{McpIuC*Uq-% z4*}lxSbKO+<+p0jVJOw?-n|>zl;hLWAaOS%Z@}`0k|LIhX0g3rJe?JOo0ZjgZ<)y9 zO(=qA!FeX`H4{I-1LjHCoUwYobys5_L3ocfuh%w{fOfp~`_yDrDa+Z%GjJamK5;l&*TX$8+!X2L6m6{zLT`aJdD*l+i z;}yg57>fNektaBS#e~g|Wz`VGqK^^m#VIvh@6nXGAB$>1(=#SUFG#a?w{qNWS_%f* zClCH?ZXohM~lbKkt~sg@@n97Xw>FO-&78yNdBD!e;^?XrVcZX9^Vz^3khwD#N|i z3dPaF)h|zQ8u`gZ%U{A0M3d#c19I|i5K3D{ z9S1D|osG#l1ojn_ln@Rs3B2v9ub--adl@WV&CqXAu4{=nfxC+P@Nebc$680?q(Tjh#EFS%0XJs|%`|@nzAzV7-nGYc+wws@yC!`zr=92LKy(odD zwRJd;vHzTq#m|qpzN?m@c__)zTxFuC2lrZyxs|2k4)k1IUAKkZww}}+B0GLO*xw%x z(MQq_M>XgmjeQmnN^$ZeJY>DTz+ERI z)s|jTk`~+jZKfmC&1Pk4dJuw)s=HB6Y4E6 zGp3}hF6QCKLdA>$Kd+FupqI6W

)TXN6!&?Yg-J6GKGNFjP1&_J!&+0iB5naHT*D zt*))@6iO|`f;4hVIW2|z;G)!}71j{TEtcd;dpUAp!DdP{fYO0uw{Y1Lhla8CUf=}^ z0>yZ;q}zU#x)k~OHDQILM~(1|^{CN)CYyb`xoaS+gK+1@WU@sOx86-_W9x)0> zGFk2Ft#SLdlY>K$#@9zA$RuOnpw`mpaQ7}<>X!`YPiI_uSt_QG*jiCxfG6D4bW2-% zK6ztNv_QDYd0uXLHrY()pYG<2dP_@7fNU%5+`c^v#;8F(cxX2-Zdf(#QQT-WL%S;~ zo-qc#yXBWPQeRO~iPK{_J5Grej^c~R%8022^o@evQizu6Y0jbL>cw)pngJ zT9xT_=AGQ4(8d^b*NzXMk9I=%l}>Ya3qc4fu#Ba%qq?C*!XXcDKuQj-a2 za^_e}B2uOAJt*wN)U(`!PAYiI&pCPVIUyfGgHhTgB-vRrjx#8ey!gie4n4%jzPTyY zIZ~fYY~YkzF%sw>U_Iba?8Je^c3a_2bs!6t9h8|t9hSR#^)s@p(NBm-Fih7p@x(az z2oaG#sC@^qX9xQG(F6x6=vqQ369Yrj1%7Y~2E&>H-b0)b5XacEGx9YoTkOhJOsTmp zfBjupd zk$|%lqfw+8T_Jr}D(KW0TS0VS2I^cCY3u9j*t9JSh@C-R(pXqKUiCYjmV;kZB#^yB z>J|vt{_%0?5J9JMq06{=$9@QM(F;?!K0??5@-eJO=Nv*B1NCX(|KK!Z4?R%q z)3Nb!?feIKVO^N1EQZodOXbK2wj74aIMcJfFd3u4j~Cm_6@!oLknG%f7CXK6L`6eH zXU*&YgASbTsBb-|b}Z1!vU;tMO9&CN~Y=MhNscU(Tu3m6?sfv=js zsy<}%Rq07l%`(G9QJYCrF|NiDxlMZ6Rw23x(ee~0PDr-Qtgoy9*Sgu7sEAn!Jt-`G z-$n|?jy!o6ye@7zFBf7={dWWEiUR^0SnRmK9nlNm7loE0xb(iEDm`$RG8@{pLr+^5 zGN5UXs5!X95J9GO7r7iFTJ~o^b&UtwCql zda$>TPZS(UQ1PSJg`T)NLuWM5c^JW9n+(you-Vyfxkg$&D^l3W@==dqAwxNT3HJwf ze~@0Kb#rsGu$U&2q{5IOFK_u`ue0J~mNo;SSX6z;(a{}PTOEnw;NZZ0v-|Ut>s~Dd zjZbCfALLH2X@tfnCxr~E15q~UxNWwfy#|l!)*hqLbc34U%)k0^hga6tB9VTgP+8!S zLVFCQ1+mD+>CT?H z^=fHURPRn%JMx`VXUD;06{%$Z{Ho>)>(>Z1U0T+} zNJIhx_P;aaKEPRo1TtM-1fx(w$OvK+nuEONUG`s}ALVH{0hZ5kGO`Osjqk9F@TFPp zd+-ojsBj^ag7zQrMC6%ws&>dIzPtK#G^x0_m{a(q+oZ}qel_81UlueLdH&5-{{vNcrIn+n>F2i||@ zAz|x^$BMQOvWRF@DhqTr%1DKAZty^_9qrJa9)mKMgqBwKoZc*Lz;eW`uEOH9z%F@d z2s;Sa&%DdayC{3t;{N?&cph7nuEy`yy0yKoV<7V;*aV9i-zQASiLYd$ukUmBkEW&$ zR0I}fn|$y#4jaCURF%Pr`P{i4TsUGYZulj$Up&H5au@O%6)$7)hN94iefdsJ#Ke?< zT7Xn^&_4ky8qAr_Y~NuV_&09cfE8}OY5NqaQrod+g9RfW{Do_CRn1U!T^+(kT}=%T zJAQDeKwaLR-h1cHoknop9d*UnkH4(w2lQai9KK6gmFb4sSJ(;=UuO83f|+S_$9 zboafzqrFNRhHZ^Y)y?<`J#TY1a66j?rO{2&q_GY=$il}LOu}t7czrGP7zG9M@t5ez z8XFiuYAFXGy*10Xf)3o9?+K3_sjsT?E6G2LS`6nf)+ZU!vjz20b#@5TV>cY86^^Ls z^~V&;?y9>Qd%VvRx%3SRTk@OyoXM6!Hvi0?-v-fbkvU1c!0k@lKs(B8~$3bdV#k3 z19)5_D|5vqAP5+}KN!}bsiWhDF9n?q5&N0nh!bGB-^Ul#*B4)}s;irwp`eDoDZoKE z*)fu6TCp0bPs0W!I&`QDOAlaa?8)Nn3SVp%mhPDCu>rUPaOKux&XFP7> zt9Q_+#9c=o1KXAY8h6n?T;kAih^Z=H-3o3nopLZT8b!3P(6vP8HrJxJKIj@X#ze5! z21=yotE!{UXG(1P;7ZA|mPDlp8G|bWn=nfude|Lh-(&06Nb}pti z5X`nZ!PVrGjY-s>#PH|O39PNHsi?w{j~>{+zxlVTyu3VY8}uu?Fm*sP(4~Xl!{o7a zMI9UMw8WOp-$&C21~e$(VAT=p731M?PrcF~!>G)UA5nxSZ9T0+2>=d`1a#pI)bY{C zbdW)z;T23Jg`0?AnDYL;LaLr#@>G5iQPCO&LR(mvLFqtSOqU@n%bJ@_MXmjCkyCEfoaf=?d)mVF4`hPr%LfF;0VKnb|9c-wPtv&m9e8S+J zf`dH7g}wW%@k0)cQ;%*hqQgS?Y{%>*Qp8P|hlhum`FZ5$-;}V^uKf0`Ie8BN(qt4A zMzA@=!G#3B(H!Pp1axq}erM>ywz2yvA-SMa{9xWw%`BfVC+&=czt{7wMKbI3z+BA}4BwY4=+ z#i3weXo$ubnk$e$;S7>SjRq$s>@mFZPYxXt`}0SdzfCG27MqM{?~|Q2+U3hNX@vkW z*LhE&uLb4K6!i=E{_yGs0RGo$I3BUERKb*}w)PL%{YU8<@t2mnWmky<(`H;4e3K5F zsESc&t$zOJ3tUmo5b389w$e457@r53%UDlOu_fliq)DGjl9^bN*+8GZ!AMYHMCCwR z^C-V_9d|{60UUKk9h41JSS}j9KK~Em=aXUG;zpG+N2e_hZ@RvSoi@`sqP6Mx^+s08 z_7Qqv;hx4udF)KDT%AN*T5GEkjFZA>z8^4Yqf)(m@u;%OLIj=omHA|)b>Cgb64lb? zeOUe9{WfRN8zp%1&xE~|hD{?|ox(UMVO`>QVZpgW8?G+wgAu}fH9 zTs%C$b$m}&w$PmH5?4}>1_D@*WccYXM#TulAKHm*>{*{|o{5cAcd5)ATiN^fV#ob( zF6FM^k2_~ba8zmi&aPv3J6^cQ1V!!N%CNLHn2T0*_5J6b0UUI;n&=mo;C^7yt_ky* zIu!V@US!nHkQybYSe4}cA0lA}+HLs>0@J&W)N?#ph^w@BI65 zAMP1cOwhBl`AWy3r~b=x@62Npg12l(LPDr6sxm&4A+J~XyOo2Vc!U_2|GH8S`W)pW zqE*KRr6sD!4>Xc5$Ll*W=={^?$NO5}C_J@2RGyh~`mB(cr0M}Kn-4=t=WWt%bQSBa zHvSi|gg34f2hybLbu26#x@z^}!)xugloZDvH4F3EXmfwlGN;$N%A|ZkNV4l+1k1aJ zEWGi6yg=J(UsDEHaNZ`mO1q>JAB<`OIU}9Rx%z=vl_iZk=?=rcU$FI+!hkmxJ5uLk zdk=_=wbZ0CO6;2ne$UcbeeJCXpKjVE^DQpkYX%{zqx=2^xLm1Y(VOBYBb~ePVE*;C zb3+)#v0ZdS62U`1SkASoKW;4W|G(*a_s%%oyY!H&i}ruS%9j-q)25!%O_Xrcn)-{K zg(L6B-O4W(J7xw4i}1C+3UBQ^B-(!Nv~TCSAAw8H8HUecmURVQ2?+&R_S;+pCZ*oo zaN&9RU-?g!QdYngO|osU%zhIq8-Aljvs=ewf;>v znJm6-?gcD~qYR2hEi)U}|vJAazL=*Umz-1|lKP7w=Y@n05yo}T{Mn@$pGr>DJL z?jNthb2$8}S%O64{kgv{JyHUmf+Ep&P^Dx=WJ$fQ=>Z*STYY8CN>``nPmi6Q+COi) zl#KfYq-cD!=sC~f4M8uj<=WX*^L_fcib<|t|LJQZ8@a=qw!gdg|9xG;d9oDalJ7N+84Nq_4 zadtZFWV?6I`MJB*9tehMP2AA3exc$NuP$H(*sxq^@`s}9@9f{*`Kxbe7^rjZA$LVk zC-C~m;C1ddD|UrwEfw|fX1lrb=eA9&ckKS6sJi&`3;Dm|V&eOE9~W{0UU1)kK0RXE z@#m{Ker#FlqO|aD@!eBre=jpn`8}O`7I3-UwA56sSC?9K6nRc>0uI4m2{GUbIx=Nf z?UOjE>yy6u|DRet-w9#fQNEJkrF5RIelF{r5}E+HnfLGj literal 0 HcmV?d00001 diff --git "a/docs/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206/\345\276\256\344\277\241\345\233\276\347\211\207_20261020143340_525_5.png" "b/docs/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206/\345\276\256\344\277\241\345\233\276\347\211\207_20261020143340_525_5.png" new file mode 100644 index 0000000000000000000000000000000000000000..059b7d9531ac97312bcb7902c1e556e22e81d216 GIT binary patch literal 370403 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%0Djolgm_Vnh4d;Tp%1=9ey+PMogajYB6K%&kTpM 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

;nU)U>PK?Q+ zlypfiYD^R@Vc@(HTbj$)PPo74&9#+qfEoT6i;YtoheR@UFuQq)Ja>KUO;|uiW{59& z4WTCcdPAklD5nfgqk&-k!>ar8S`?IM=I!+;dqF%e^g$7ml0}hIX%QVt7}de9@{&?A zOeS+8CkLf5(X6gGhra!$7A}bX1~C?hWk{9OA6`e`qwHo15e3OYcTFUz2{twru>FOA zoSw^*;-Yt2`zEL5g&X2hE3CT4DKt`{HNBEeixz`a%!iHc-uV-gqqerFCJRD_jSVk+ zIx%VKsj0XOnT?IZ_M*Gvcl_B7Trt%)sumM#CTXJPt$SU_nECl@fq_nLf&zj26pY;% z8?VBqBq1vLb8L)|kTCn=Za!}*cJD4+jq`&EbLHDm8w4Pm?|WbtLpE<`V{`Vh7cpG~ zO6y}awcP5FE?cwi1qEgxJpk5VT^(c}q~jHveUQyU2@&`Trza<@7>Zh2 zKZ^5KxLpxjJn9Xubx~27tXSgU6(=g{xgtYfUCmoiQ07a=&mZqg#9^dl$_GV(GA(V* z()W61X08A^ghJKrDo1bxfpV4SHGfnfD}$dAoZ~75^0)TDNu;K#3e5|$97;;P$2WkW z7MYv73e|w=Ub7E!UhuYtMczeeeh68GkQWYren3n?Bn+>goS!e?urm)}O|bly-p{oH zLKXx$VCV&@IW`&^p0Y$>mSeA-n3$K5BW3zvW-qjq=u>|+uP1}M0@^gtEZmgRc zF=P9^M!#(zoNN4Q&tNZgHQP_rhKnu8mzla%V~O}~e@EI8m6WBt*D;!x1An`hL3;0= zp@`@^tK*13rDWmy>Uq^+*!bet2j@>e-Hv&ukSzL)QD>@j|4_BJ=T=P)O(aQ3NaDuC zkdA7S@BNftNP!dH(dzg_t?Y!V3iDg5Q+FNQ@@iC(xj8zfD080#KYk8x5U6*f__aJ% z;1WS&6e!tb8Ai~1nW=zFxl_!*L1VCx6Rs#gOpQ-aCRkfzm?S)oqpe9=x0@|BkwT7o zE)cdd;QJN#5!z_|eM!-S>JxQ^?3LdMvSfk1z4A;v<7MR7%aZiEWnQW{PcT?35Va^! zIT5{@8h|AVMz3J{3JVjsj|boK1n5_!Kw}SMUM8@>OiibN&!>k`Ar(&h1fv9!7Nb+D zTW<&<+7Ea)25AZ`J~1&duu20@C6g9kY_L#)RdSSbZa%-V@>~>FH|gV7AAng4793So z$55q*B4<7j4uClc2{a(rel|D1Zv1d9h=?Qhv7SeH`2o~LeY0TTTa?;n_sU}jTmZI+ z5J!wB-FT?;uGgETZD z=oehEQ&PZXbp;S^cLlI8F!&w!vY=iEw=r150~Ygh)fF5QJOk;pB9yd4p$qwmQat%( zo<>;?^ur+Nhqnw}6>!`G!@D`BX@e~tPzQ@6t-gP1IyktKvz(6*q{MnNh*Rb4b~Z@* zMDA@~^YfWL_1?y==IX-K)q?0OvO7Id1K~AeJm&}Vg4nH5_ZA|$G&FwRQ~6v&NMvq4 zrQY)R_BZot!nl0n5!&=L?yAxwaq*Rt?@opTVcChpGZ#lzQ>0o21{s?{u9Bi3R~&Q1 zzZsmeXKT2fr~20Q1H)%Cnu;Ng+;&(>RTsI`6Sa#zO*_x4I=t2>JKTX};&c8ro1#HU zseoHo%}|okri%SJ_+hKSqaWeEqI{Zm%+62nUbs5&U=wB;*P6vOQd-|`<0%Gy>{BXwYWH}RdE!@!X@WIUna~n&`2Pwdb=}r>TweN=*5r%UWC1b}RGysR8 zJa9UNcoiz87eD2eN<6xTw+h){e~XVdI9Gwy0GMxbetU|yJy&*vuuFbIdp>4<4)F~9 zVL*gWc)a8>06_d3822dxDd=knsBt=g&8elW4MhS-#{T;2F96A^JeqSbGRg@JeFA7F zjE;^DumvO|BLf^NNxPW1IK4p-AzKD86X0I#Tsj6ku_rcZn!+-GpLlEssq@Za&1vv&d`SEX)^8 zOU-UALU-Z7G{@Z~GJDE%%Gjx;Chto3K;L>Vb>4#?$$^R5zTs>oRFw9FcbcgEa4e+ajeM~L84{R$SNP}`aRGK(BImy>1 z0=1aIj^q;B+R(m)2I+W;SODtn5zr4_Zn|BrLZ1;1N}%@wg$1s4{l$r~9(IwM7N{UV zTLI>R!6ZCT6y1h-zfKZTNUgAMm_EB$bt{Ag2kZwi9@)HrfG%%L;D9_9h#K`{9f8Ry_5e8>}IIKK>bp*m4Bj3@hiAe-0R}IPVXuc2>c~1S+he zEIt7NjJiR$T^HaO@F*iKQZoU46D&Q(U)*M}55k@u4CSO-x8C=sw8A0miIv*d*9Y66 zWcmQy0m`)J`z;+wSC5cDo05d5}t(&D3jGy7vwbbI{UeK&Gg!t`2|z`Fse4 z)b}|_^@d*33>W-{7#lic3JHA1Ba91kAQc7^ZdN+FCD>V^z$!WfuthXnT2K#w3>ccc zd|*Dp&0Ps89>fojIT94OG&c-E-YRrJi{M|(G5U7@Tw z)gwO3$>GFk_|TDT^^f6~0=?8!_r0+%{O@B^;RrY}vT{8+`>A>O z2x2-YbAx|#HJHVNCMUvj3##=KV`FG2^>9fzjE1sk4fE6^gZvy;e*dPoVY-g^&9Iz){@QB@LTcv)8Wa zc&Rco)TfZ(`T~ta=t2Q&IIszUAW!^cdKS_0x=4wB3YH^m{Y-mezW$D{&CHTxxu3uu z$HzAT8yMJ#sNcuN4oyhN=biO@N*}QSHxAIKn@Qbs>=Rj-n9ehq`iF)-OHu<{m<94U z#mwN666>;uy+B?Cmy*5p34%-&#NAu+!ikGsH%ZzpN!p7U+byBQex!Q8?=*rtvEftf z(DcXyMh2?n%C8AQF6DVY)Sj^wWNjJgKbLbF9jAZxj_kSbgA|?M0_6#HBJ7c=>9KD= zOIPx84qC+xpSPIit?v&F9kz;>m6fv%J3d&T=&~dj3=87maFIz0l1@@mC}m-Wyz%4S zKp3TinD)0$JKjWD*`@c3N1efKWA7BQQ>?}#bMAYuA@`;6c3OU?#PU@))$}(C)yZr^HL2y`I5Xn&SQigkbcP? zpr?*scl%ek#}d!K0HBSh2X|5z5#&E^1|_lu-b%Lq8yHlX65H=n73zPVd5YH_dh40@ z-%z1huWz~Z{)1oo&+pP{!97Vt`R`9CHCrz^-TCkTZ~Y}sEwV@Q-zUDXf@JUHC-^%2 z=dp#RcVA@bZ{IVEO48-W9enyT?r*;dzDtzyt=WH{!D*9dq4PuZZh6W-`_Ho%n)F`R zu^(K|y73IhTTk#n1yL&jlNsbrxc~lj({*;(JAWJ!?GqOr66d&o=|;X&kHjMbe0goS z463~pj*f9U~*xIQ3xWvu~)9S-j}a1|ASr5X_lo=q@dgbRsn4&G;jk8n23&!<7X4Hvt;80Lc3 z2C4!i84HGH!)O9rYHQ7>>N)h9MNyu8vpb3Z*Dsiz(HlFTC^)&{LTda-x?I4;nOVTl z$89kuVpL-K&3z)xQR(O(j0{XUbgKDZG7I#w(-prdn+Ll_Q-ybLnS7F0(V#4(TjY)r zk#Dl(DK0K84*NV1W)fraE;W)Dha>qS)njtGqL9>lW;dbbnL}b!F?zO^PR^&OV*eC> ze-d@|$QQB7Dv`>}>x(aQtGvpJcLtlCDKBX9$Cg%%1K^Nan8?lK)8NCfMdo&>bf}3n z1=M`@-5ch}F(uKFZF!6nJO_(a=%zg;e{TsR1HNB0{U&L4DLc=jjvlb_uE}_KQek&! zcC;jatuU_~>Z{de8$c7$thVVOcQYv~E?yZ(4>$6H6ZOx+f~J}VFkigAywUr2OZWDm zoa*JNl_cbr48{pY=Sx{xCuIA0jKu5)2M{B+vLt%(L@xLd%nhA2t)K475ktv=NwfYP z-#Z0EBaNJZ66xVl9&8#K+ND+E`$U9w-%$O07r7(nsKa^>-tEe5-i}mG%QT3Fmxl9R z<JENdDY}9*?n57 zGm_d}BAQ)ViqcIDpO|afV^DC4VuPz4L-PWC_c+StNggt>u=Nc*NNFw}xHMjx=`F&? zPM4Byo2gB!UJI{X+sfN}CRLQ3g;k1iHB(#u(9=h zsT>-IvzFZGCITDWB3g^zN!Ql7Ow&3Ryrhd{*0$PebftvgfSAwB8AMDG*5*^_r-sLJ zMMZLc-y4*3K}1AE99O&G;o<9BW9^}znVy~?U{^no*V`ym$I{u=HCmv4zL@0XB{eJn zc~BbsZ7N2Y>brl0E^Bpo)M+Cw%70a8^VwmLvD(P~*@xYX0wE(%mC?SyOpjvH{RK|{d6j%7p`M{ao7T9kHFvMfc|>J?S2F!(H8 zbZEHE-IsT~roi-UO|`M5uS-GY;zr!IbvWl6bFJuw{x2Sj_)|4ygAL)L)Ee8fJ!N@& zRntMa+G+U_-3voUg+>lPmzQuttWwxV`63ScuC_7zuo4VO-+B3q1ZJI9I={K2Yo{{6m&3@0>exMQ&aS9$?2 zv?}J?s^jv-RDis$c3n}nmr>WmYf7ry)_&iozQ1+XElHxH62)LEC?LQ_mX>d3T9H3> zb#O>3xtVZT?|7}y=U*wxOI-fmBz}k2vVA7n0(G4w+#Bs;0nm7^+`7acKHLHj(zt&F z15o@9(^F z=@NVsHIOV#H@_4VxNglpAf2ihKZYSTUi(*D=lzU>;Vx zEFQ3|x`F2cxAW0fThOJoh0WSz)opA6KWBc!ftG2PlY`ZX3XuY}I-`Y(kM;GoOrm9O zj?T{UcX?G!cLhk#qvAFG5tz*4wr`*P$?|bYSl!}pcuZL;%3Nq*L^zodXOsZft|x;A z7rVHuB+MXuPGeplJz_U4y*q#1sCdOVCi|Ld?-sv==th$!Gm`kr zY^B%XH&^rN3t^oi*FSSZ2DNFS;Lv#P@seP%W+T3>mR{nHv}OKDYHtq=M$NRoRe2uXe}SarcTOJ6Ya1lCH;; zeroya9j}r_7h9R5wM|HQ5)auq0~naq4rgu6`Jxixi+{)djld%UsV{pz;i(ncU7xeUOCA^s;$JJz`*>5`X*pHSpj;a$#j_ z*?-DMN4P)l7hF&PWA}Q(!XhFbaWzgxM$cc1q(F6(HAY-VXCsy)w}Eiabf*5?cgcrb z-*dXQ$Y`PS^8A7idwsmDB(2gO>Wlc1ks?D*vVCjrC=Gkt`z~{+eoH{7fJ&FcuS+Hz zwv;j962X-VCl;NZdRTrh8!k{h6aK8N@u$CEX-3@zLHm#H?y1#b#e9dAc`3>h+pW9r zIzTB{TUX0vKFxY%j~fg1hfLYTc)@cYsElLxM?ZN3v(jLJNGaOenw$aH8k%F<{F^BT(W+g~%Id2k zZDn{*M>rm9Or>?3%VXVMD2^e+WN!`Y*bp?PqEZWvQ|c|0)G6BTK%pd>Uu)a0_0?Nh zB7P!Dz@`}RZeRqT6GxmH+m8e@h-#> zk91_NSsoV|RDY)ckA!b3S86t&ffuL4lb9e@V+_A*!Hs?Mi`L`Zyizq!f^zfVcFAW4A<&BYE(NeS z-dX?|D(K*$Daz4n87R3263siLyy#C}QX0+Fe}kL#$rCvjXO|g!VM@3ycpTR}L16-F zK%t{Mr*~n+f>ZAm*X|(fQ=_P=T<_0HE9M;#ra=W0d70v4LHOeF!}n%T2+Pq&Gq|rm zquYd!AK8fm`jxT= z#-3{vHabxoN)C75+8`Oeac&&>XSlnXRlN#HNz&5W6%EK1DJt;MZ14=v{1=|~Uzl96*CF(iM}GGNft$X8f!pDfuEuKd|D=U*m^GP`z1Ho?jrOv0M%R#v=2rPN{_*DH98!}Tb z!xS0*v6`HJ>pOD3#P#D6bWx*?I<&E<`{bG(5j5znZPPgc?-5u5F{e0gcCm$bvKEKVQvzQbWVnz;(Y~A#LcE{vIb}@| zh&ZI{35{}^rO#IW8tt{>mlLHiLB;M+Q;DWdX<|lteW-t>a)e}ITdnqP6WyzPT(RKS zfZmxlSW6U=wZ1(O?sTp-`DdzLMRVr+L`(caRYXX$-%`7i-aEX7AVyP8PL6Qp??%hS zDqk6~1(?5E_YD`mQs*eT-%+C*B`FGpfvr2BFl%oUL|SH-nV#Nx^CrB+peIkWqOGNc zn&Q=v&)A2(y;5kx*sKhx%c-(F5ESI$@$&_|B@&r9Mfp}wkNK?#fnO_#DuNtw(hCk& zoNQtC3m!YS(LFn$dFX_V4Xg&MLe<{U_R+q+uVBnAAjD=L0xiNfuU&HAUExo;Om~CvR zGV9{sxCL+gouQ$8wrotP&H6Y8$_-aT1J%?$w;X5^WymH>W-Bf}N9oY1w76JA8vA{p zn8;Ml9qHVe;XKQLomq`WDDX>dEs4ZsNoZ6QLwE$k!ypXIveGi?=g*bomU+j=Z?KSY zd^o&WD0MV6%Yl|eR7?9yk+Uzm2TBYrXOlt``>%Wz(rDySdCc5Q3zWK>zRxS2>WCl| zcs{?swDxf`oNf|PTs5)Mg5+1^L3=TNtOm!6BoR@Vw&#n<)|N%BxgWF8@s5j?;j-@& zePW0((LKwOq6qExjys2B^;8A%yyHa`4e9yrkv)ZS8?Y|q6l;Pa&Q~|w%?z;UE8=~5 z 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

GEj5=FF{6cb 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 |$s?r(Y-kcVvQn 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 F{$JhSffE1# literal 0 HcmV?d00001 diff --git "a/docs/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206/\345\276\256\344\277\241\345\233\276\347\211\207_20261020143641_526_5.png" "b/docs/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206/\345\276\256\344\277\241\345\233\276\347\211\207_20261020143641_526_5.png" new file mode 100644 index 0000000000000000000000000000000000000000..c6f77f27154870f9c15b5ee50fe6347b67e57d40 GIT binary patch literal 264890 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 H9=Y&8d&$Ad literal 0 HcmV?d00001 diff --git a/fixtures/index.ts b/fixtures/index.ts index cfb0cfb..f4683d4 100644 --- a/fixtures/index.ts +++ b/fixtures/index.ts @@ -6,6 +6,7 @@ import { FLAKY_TAG, resolveFlakyRetries } from '../core/flaky'; import { annotateFailureClass, classifyFromTestInfo } from '../core/failure-classify'; import { applyQuarantine } from '../core/quarantine'; import { attachFailureDiagnostics } from '../core/diagnostics'; +import { annotateFailureFrequencyForTest } from '../core/failure-tracker'; import { attachRuntimeHealthGuard, getRuntimeHealthGuard, @@ -15,6 +16,10 @@ import { buildPageFixtures, type AppPageFixtures } from './page-registry'; type AppFixtures = AppPageFixtures; +/** + * 业务 fixture:依赖 chromium.project 的 storageState(auth/user.json)。 + * 每条用例由 Playwright 新建独立 context + page,再注入登录态;此处不持有跨用例 session。 + */ export const test = base.extend({ page: async ({ page }, use) => { attachRuntimeHealthGuard(page); @@ -33,17 +38,27 @@ test.beforeEach(({}, testInfo) => { test.afterEach(async ({ page }, testInfo) => { const failed = testInfo.status !== testInfo.expectedStatus; - if (failed) { - if (page && !page.isClosed()) { - await attachFailureDiagnostics(page, testInfo, getRuntimeHealthGuard(page)); + try { + if (failed) { + if (page && !page.isClosed()) { + await attachFailureDiagnostics(page, testInfo, getRuntimeHealthGuard(page)); + } else { + annotateFailureClass(testInfo, classifyFromTestInfo(testInfo)); + } return; } - annotateFailureClass(testInfo, classifyFromTestInfo(testInfo)); - return; - } - const guard = page && !page.isClosed() ? getRuntimeHealthGuard(page) : undefined; - if (guard) { - await guard.assertClean(`用例收尾:${testInfo.title}`); + const guard = page && !page.isClosed() ? getRuntimeHealthGuard(page) : undefined; + if (guard) { + await guard.assertClean(`用例收尾:${testInfo.title}`); + } + } finally { + // 跨构建失败频次:reporter 负责累计落盘,这里基于最新统计打 annotation(只读), + // 保证诊断/健康检查异常也不影响打标;无编号用例自动跳过。 + try { + annotateFailureFrequencyForTest(testInfo); + } catch { + // 统计不影响用例结果 + } } }); diff --git a/fixtures/page-registry.ts b/fixtures/page-registry.ts index 724ee8b..0250e51 100644 --- a/fixtures/page-registry.ts +++ b/fixtures/page-registry.ts @@ -3,7 +3,37 @@ import type { Page } from '@playwright/test'; import { LoginPage } from '../pages/auth/login.page'; import { AnaChgPage } from '../pages/spd/ana-chg.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 { 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 { CenterReportLmmPsiPage } from '../pages/spd/center-report-lmm-psi.page'; import { CenterReportLmmSheetPage } from '../pages/spd/center-report-lmm-sheet.page'; @@ -120,6 +150,36 @@ export const PAGE_CTORS = { anaChgPage: AnaChgPage, anaDeptPage: AnaDeptPage, 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, reconDeptAccountPage: ReconDeptAccountPage, reconDeptNetInPage: ReconDeptNetInPage, diff --git a/locators/spd/admit-material-list.locator.yaml b/locators/spd/admit-material-list.locator.yaml new file mode 100644 index 0000000..b5458b9 --- /dev/null +++ b/locators/spd/admit-material-list.locator.yaml @@ -0,0 +1,469 @@ +# 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 条/页')" diff --git a/locators/spd/admit-material.locator.yaml b/locators/spd/admit-material.locator.yaml new file mode 100644 index 0000000..41c8a27 --- /dev/null +++ b/locators/spd/admit-material.locator.yaml @@ -0,0 +1,1280 @@ +# Auto-generated from admit-material-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productApply/ZRlist +# Scanned: 2026-10-20T08:45:26.257Z +# Page: 基础设置 > 准入管理 > 物料准入管理 +# Note: filter dropdown was opened during scan +# Note: detail view was opened during scan +# Note: log view was opened during scan + +page: admit-material +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productApply/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: '准入任务分配' })" + + admitMaterial: + description: "物料准入管理" + element_type: tab + section: "main::main" + strategy: text + value: "物料准入管理" + playwright: "page.getByText('物料准入管理')" + + tab_admitMaterial: + description: "物料准入管理" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/dataSync/dataSync/productApply/ZRlist" + playwright: "page.locator('#rc-tabs-0-tab-/basic/dataSync/dataSync/productApply/ZRlist')" + + createDateDateRange: + 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')" + + createDateInput: + 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: '展开' })" + + el_e585b3e88194e4baa7e59381Btn: + description: "关联产品" + element_type: button + section: "main::main" + strategy: role + role: button + name: "关联产品" + playwright: "page.getByRole('button', { name: '关联产品' })" + + el_e7949fe68890e794b3e8afb7e58d95Btn: + description: "生成申请单" + element_type: button + section: "main::main" + strategy: role + role: button + name: "生成申请单" + playwright: "page.getByRole('button', { name: '生成申请单' })" + + el_e4b880e994aee7949fe68890Btn: + description: "一键生成" + element_type: button + section: "main::main" + strategy: role + role: button + name: "一键生成" + playwright: "page.getByRole('button', { name: '一键生成' })" + + el_e4bd9ce5ba9fBtn: + 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: '详情' })" + + logBtn: + 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_el_e585b135e69da1313530e69da12fe9a1b5: + description: "共 5 条150 条/页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 5 条150 条/页" + playwright: "page.getByText('共 5 条150 条/页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + # --- main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院 --- + productName: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_goodsName" + playwright: "page.locator('#form_item_goodsName')" + + registrantSelect: + description: "湖南海琅生物科技有限公司" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "器械注册人" + playwright: "page.getByText('器械注册人', { exact: true })" + + manufacturer: + description: "器械注册人" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_goodsMfrsId" + playwright: "page.locator('#form_item_goodsMfrsId')" + + specModel: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_goodsSpec" + playwright: "page.locator('#form_item_goodsSpec')" + + el_e6b3a8e5868ce8a784e6a0bcInput: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_regCertSpec" + playwright: "page.locator('#form_item_regCertSpec')" + + el_e6b3a8e5868ce59e8be58fb7Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_regCertModel" + playwright: "page.locator('#form_item_regCertModel')" + + el_e58d95e4bd8dInput: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_unit" + playwright: "page.locator('#form_item_unit')" + + el_e5a283e58685e5a283e5a496Select: + description: "境内" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "境内境外" + playwright: "page.getByText('境内境外', { exact: true })" + + el_e5a283e58685e5a283e5a496Input: + description: "境内境外" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_abroadFlag" + playwright: "page.locator('#form_item_abroadFlag')" + + el_3638e58886e7b1bbSelect: + description: "医用高分子材料及制品" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "68分类" + playwright: "page.getByText('68分类', { exact: true })" + + el_3638e58886e7b1bbInput: + description: "68分类" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_kindSixtyeightCode" + playwright: "page.locator('#form_item_kindSixtyeightCode')" + + el_3138e58886e7b1bbSelect: + description: "临床检验器械" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "18分类" + playwright: "page.getByText('18分类', { exact: true })" + + el_3138e58886e7b1bbInput: + description: "18分类" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_kind18Code" + playwright: "page.locator('#form_item_kind18Code')" + + el_e696b0e7aea1e79086e7b1bbe588abSelect: + description: "Ⅱ类" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "新管理类别" + playwright: "page.getByText('新管理类别', { exact: true })" + + el_e696b0e7aea1e79086e7b1bbe588abInput: + description: "新管理类别" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_riskLevel" + playwright: "page.locator('#form_item_riskLevel')" + + el_e697a7e7aea1e79086e7b1bbe588abSelect: + description: "Ⅱ类" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "旧管理类别" + playwright: "page.getByText('旧管理类别', { exact: true })" + + el_e697a7e7aea1e79086e7b1bbe588abInput: + description: "旧管理类别" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_consumableLevel" + playwright: "page.locator('#form_item_consumableLevel')" + + el_e7bb93e69e84e7bb84e68890Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_goodsDesc" + playwright: "page.locator('#form_item_goodsDesc')" + + el_e582a8e5ad98e69da1e4bbb6Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_storageConditions" + playwright: "page.locator('#form_item_storageConditions')" + + el_e58c85e8a385e8a784e6a0bcInput: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_invUsage" + playwright: "page.locator('#form_item_invUsage')" + + el_e58f97e68998e7949fe4baa7e59586Select: + description: "湖南海琅生物科技有限公司" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "受托生产商" + playwright: "page.getByText('受托生产商', { exact: true })" + + el_e58f97e68998e7949fe4baa7e59586Input: + description: "受托生产商" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_trustMrfsName" + playwright: "page.locator('#form_item_trustMrfsName')" + + el_e59381e7898cInput: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_brand" + playwright: "page.locator('#form_item_brand')" + + el_e698afe590a6e58cbbe79697e599a8e6a2b0Select: + description: "诊断药品" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否医疗器械" + playwright: "page.getByText('是否医疗器械', { exact: true })" + + el_e698afe590a6e58cbbe79697e599a8e6a2b0Input: + description: "是否医疗器械" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_instrFlag" + playwright: "page.locator('#form_item_instrFlag')" + + el_e9878de9878fInput: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + materialCode: + description: "填了则按用户填写的,未填写则系统自动编码" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_goodsCode" + playwright: "page.locator('#form_item_goodsCode')" + + registrationNo: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_goodsRegCert" + playwright: "page.locator('#form_item_goodsRegCert')" + + el_e69c9be6b5b7e6b3a8e5868ce8af81e7949fe69588e697a5e69c9fDateRange: + description: "望海注册证生效日期" + element_type: datepicker + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "望海注册证生效日期" + playwright: "page.getByText('望海注册证生效日期', { exact: true })" + + el_e69c9be6b5b7e6b3a8e5868ce8af81e7949fe69588e697a5e69c9fInput: + description: "请选择" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_whCertStartDate" + playwright: "page.locator('#form_item_whCertStartDate')" + + el_e69c9be6b5b7e6b3a8e5868ce8af81e69c89e69588e69c9fe887b3DateRange: + description: "望海注册证有效期至" + element_type: datepicker + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "望海注册证有效期至" + playwright: "page.getByText('望海注册证有效期至', { exact: true })" + + el_e69c9be6b5b7e6b3a8e5868ce8af81e69c89e69588e69c9fe887b3Input: + description: "请选择" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_whCertEndDate" + playwright: "page.locator('#form_item_whCertEndDate')" + + el_e69c9be6b5b7e7949fe4baa7e4bc81e4b89ae7bc96e7a081Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_manufactCode" + playwright: "page.locator('#form_item_manufactCode')" + + el_e69c9be6b5b7e5819ce794a8e697b6e997b4DateRange: + description: "望海停用时间" + element_type: datepicker + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "望海停用时间" + playwright: "page.getByText('望海停用时间', { exact: true })" + + el_e69c9be6b5b7e5819ce794a8e697b6e997b4Input: + description: "请选择" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_enDate" + playwright: "page.locator('#form_item_enDate')" + + el_e69c9be6b5b7e69bb4e696b0e697b6e997b4DateRange: + description: "望海更新时间" + element_type: datepicker + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "望海更新时间" + playwright: "page.getByText('望海更新时间', { exact: true })" + + el_e69c9be6b5b7e69bb4e696b0e697b6e997b4Input: + description: "请选择" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_updateDate" + playwright: "page.locator('#form_item_updateDate')" + + el_e58aa9e8aeb0e7a081Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_inputCode" + playwright: "page.locator('#form_item_inputCode')" + + el_e696b0e697a7e585b3e88194e4baa7e59381Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_link_hos_goods_name" + playwright: "page.locator('#form_item_link_hos_goods_name')" + + el_e98082e794a8e88c83e59bb4Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_purpose" + playwright: "page.locator('#form_item_purpose')" + + el_e58cbbe4bf9de7bc96e7a081efbc883237e4bd8defbc89Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_miTwentysevenCode" + playwright: "page.locator('#form_item_miTwentysevenCode')" + + el_e58cbbe4bf9de5908de7a7b0Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_miDjmc" + playwright: "page.locator('#form_item_miDjmc')" + + el_e58cbbe4bf9de8a784e6a0bcInput: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_miGoodsGg" + playwright: "page.locator('#form_item_miGoodsGg')" + + el_e58cbbe4bf9de59e8be58fb7Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_miGoodsXh" + playwright: "page.locator('#form_item_miGoodsXh')" + + el_e4baa7e59381e4bf97e7a7b0Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_goodsGeneralName" + playwright: "page.locator('#form_item_goodsGeneralName')" + + remark: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_remark" + playwright: "page.locator('#form_item_remark')" + + el_e98787e8b4ade590abe9878fInput: + description: "采购的最小包装数量,默认1" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: placeholder + value: "采购的最小包装数量,默认1" + playwright: "page.getByPlaceholder('采购的最小包装数量,默认1')" + + el_e98787e8b4ade58d95e4bd8dInput: + description: "采购的包装单位名称" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_purchaseUnit" + playwright: "page.locator('#form_item_purchaseUnit')" + + el_e8afb7e9a286e590abe9878fInput: + description: "请领的最小包装含量,默认1" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: placeholder + value: "请领的最小包装含量,默认1" + playwright: "page.getByPlaceholder('请领的最小包装含量,默认1')" + + el_e8afb7e9a286e58d95e4bd8dInput: + description: "请领的最小包装单位名称" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_buyUnit" + playwright: "page.locator('#form_item_buyUnit')" + + el_e4babae4bbbde8bdace68da2e6af94Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + el_e698afe590a6e4b8b4e98787Select: + description: "否" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否临采" + playwright: "page.getByText('是否临采', { exact: true })" + + el_e698afe590a6e4b8b4e98787Input: + description: "是否临采" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_tempFlag" + playwright: "page.locator('#form_item_tempFlag')" + + el_e698afe590a6e58fafe98787e8b4adSelect: + description: "是" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否可采购" + playwright: "page.getByText('是否可采购', { exact: true })" + + el_e698afe590a6e58fafe98787e8b4adInput: + description: "是否可采购" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_purchaseFlag" + playwright: "page.locator('#form_item_purchaseFlag')" + + statusSelect: + description: "启用" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "状态" + playwright: "page.getByText('状态', { exact: true })" + + statusInput: + description: "状态" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_goodsStatus" + playwright: "page.locator('#form_item_goodsStatus')" + + consumableCategorySelect: + description: "低值易耗品 / 低值易耗品-其他材料 / 基础卫生材料-其他材料-其他材料" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "耗材分类" + playwright: "page.getByText('耗材分类', { exact: true })" + + consumableType: + description: "耗材分类" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_goodsKind" + playwright: "page.locator('#form_item_goodsKind')" + + el_e88097e69d90e7b1bbe588abSelect: + description: "低值" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "耗材类别" + playwright: "page.getByText('耗材类别', { exact: true })" + + el_e88097e69d90e7b1bbe588abInput: + description: "耗材类别" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_purMode" + playwright: "page.locator('#form_item_purMode')" + + el_e8b4a2e58aa1e58886e7b1bbSelect: + description: "卫生材料 / 卫材-低值易耗" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "财务分类" + playwright: "page.getByText('财务分类', { exact: true })" + + el_e8b4a2e58aa1e58886e7b1bbInput: + description: "财务分类" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_fcatKind" + playwright: "page.locator('#form_item_fcatKind')" + + el_e5b8a6e9878fe58886e7b1bbSelect: + description: "非带量 / 自行采购" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "带量分类" + playwright: "page.getByText('带量分类', { exact: true })" + + el_e5b8a6e9878fe58886e7b1bbInput: + description: "带量分类" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_vbpKind" + playwright: "page.locator('#form_item_vbpKind')" + + el_e698afe590a6e9ab98e580bcSelect: + description: "请选择" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否高值" + playwright: "page.getByText('是否高值', { exact: true })" + + el_e698afe590a6e9ab98e580bcInput: + description: "是否高值" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_higHvalue" + playwright: "page.locator('#form_item_higHvalue')" + + el_e698afe590a6e68d90e8b5a0Select: + description: "否" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否捐赠" + playwright: "page.getByText('是否捐赠', { exact: true })" + + el_e698afe590a6e68d90e8b5a0Input: + description: "是否捐赠" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_ecoBat" + playwright: "page.locator('#form_item_ecoBat')" + + el_e6b4bbe58aa8e68f8fe8bfb0Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_ecoBatRemark" + playwright: "page.locator('#form_item_ecoBatRemark')" + + el_e698afe590a6e99990e588b6e7a791e5aea4e98787e8b4adSelect: + description: "请选择" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否限制科室采购" + playwright: "page.getByText('是否限制科室采购', { exact: true })" + + el_e698afe590a6e99990e588b6e7a791e5aea4e98787e8b4adInput: + description: "是否限制科室采购" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_deptFlag" + playwright: "page.locator('#form_item_deptFlag')" + + el_e98787e8b4ade695b0e68daee69da5e6ba90Select: + description: "请选择" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "采购数据来源" + playwright: "page.getByText('采购数据来源', { exact: true })" + + el_e98787e8b4ade695b0e68daee69da5e6ba90Input: + description: "采购数据来源" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_dataSources" + playwright: "page.locator('#form_item_dataSources')" + + el_e789a9e6b581e4b8ade5bf83Select: + description: "请选择" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "物流中心" + playwright: "page.getByText('物流中心', { exact: true })" + + el_e789a9e6b581e4b8ade5bf83Input: + description: "物流中心" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_logisticsCenter" + playwright: "page.locator('#form_item_logisticsCenter')" + + el_e698afe590a6e5ad98e59ca8e887aae8b58be7a081Select: + description: "请选择" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否存在自赋码" + playwright: "page.getByText('是否存在自赋码', { exact: true })" + + el_e698afe590a6e5ad98e59ca8e887aae8b58be7a081Input: + description: "是否存在自赋码" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_uniqueIdFlag" + playwright: "page.locator('#form_item_uniqueIdFlag')" + + el_e698afe590a6e998b2e796abe789a9e8b584Select: + description: "请选择" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否防疫物资" + playwright: "page.getByText('是否防疫物资', { exact: true })" + + el_e698afe590a6e998b2e796abe789a9e8b584Input: + description: "是否防疫物资" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_plagueFlag" + playwright: "page.locator('#form_item_plagueFlag')" + + el_e698afe590a6e5a487e8b4a7e5ba93e587bae5ba93Select: + description: "请选择" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否备货库出库" + playwright: "page.getByText('是否备货库出库', { exact: true })" + + el_e698afe590a6e5a487e8b4a7e5ba93e587bae5ba93Input: + description: "是否备货库出库" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_stockUpFlag" + playwright: "page.locator('#form_item_stockUpFlag')" + + el_e698afe590a6e69c89554449Select: + description: "医疗器械GS1-UDI" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否有UDI" + playwright: "page.getByText('是否有UDI', { exact: true })" + + el_e698afe590a6e69c89554449Input: + description: "是否有UDI" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_UDIType" + playwright: "page.locator('#form_item_UDIType')" + + el_e98787e8b4ade68890e69cace5bd92e5b19ee7a791e5aea4Select: + description: "请选择" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "采购成本归属科室" + playwright: "page.getByText('采购成本归属科室', { exact: true })" + + el_e98787e8b4ade68890e69cace5bd92e5b19ee7a791e5aea4Input: + description: "采购成本归属科室" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_affiliationDept" + playwright: "page.locator('#form_item_affiliationDept')" + + el_e789a9e8b584e7aea1e79086e58886e7b1bbSelect: + description: "请选择" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "物资管理分类" + playwright: "page.getByText('物资管理分类', { exact: true })" + + el_e789a9e8b584e7aea1e79086e58886e7b1bbInput: + description: "物资管理分类" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_materialClassification" + playwright: "page.locator('#form_item_materialClassification')" + + el_e7a88ee78e87Select: + description: "13%" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "税率" + playwright: "page.getByText('税率', { exact: true })" + + el_e7a88ee78e87Input: + description: "税率" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_taxRate" + playwright: "page.locator('#form_item_taxRate')" + + el_e698afe590a652464944e7aea1e79086Select: + description: "否" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否RFID管理" + playwright: "page.getByText('是否RFID管理', { exact: true })" + + el_e698afe590a652464944e7aea1e79086Input: + description: "是否RFID管理" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_rfidFlag" + playwright: "page.locator('#form_item_rfidFlag')" + + el_e698afe590a6e586b7e993bee8bf90e8be93Select: + description: "请选择" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否冷链运输" + playwright: "page.getByText('是否冷链运输', { exact: true })" + + el_e698afe590a6e586b7e993bee8bf90e8be93Input: + description: "是否冷链运输" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_isColdTransport" + playwright: "page.locator('#form_item_isColdTransport')" + + el_e99480e594aee59586Select: + description: "浙江高仰医疗科技有限公司" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "销售商" + playwright: "page.getByText('销售商', { exact: true })" + + supplierId: + description: "销售商" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_provId" + playwright: "page.locator('#form_item_provId')" + + el_e98787e8b4ade58d95e4bbb7Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + el_e4b89ae58aa1e6a8a1e5bc8fSelect: + description: "直销" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "业务模式" + playwright: "page.getByText('业务模式', { exact: true })" + + el_e4b89ae58aa1e6a8a1e5bc8fInput: + description: "业务模式" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_bizMode" + playwright: "page.locator('#form_item_bizMode')" + + dimensionSupplierSelect: + description: "浙江高仰医疗科技有限公司" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "供应商" + playwright: "page.getByText('供应商', { exact: true })" + + subSupplierId: + description: "供应商" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_subProvId" + playwright: "page.locator('#form_item_subProvId')" + + el_e4ba8ce7baa7e4be9be5ba94e59586e7bc96e7a081Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_supplierCode" + playwright: "page.locator('#form_item_supplierCode')" + + stockoutDays: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_daysOfStockout" + playwright: "page.locator('#form_item_daysOfStockout')" + + twoHourArrivalSelect: + description: "请选择" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否2小时到货" + playwright: "page.getByText('是否2小时到货', { exact: true })" + + twoHourArrival: + description: "是否2小时到货" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_twoHourFlag" + playwright: "page.locator('#form_item_twoHourFlag')" + + chargeableSelect: + description: "是" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: label_text + value: "是否收费" + playwright: "page.getByText('是否收费', { exact: true })" + + chargeable: + description: "是否收费" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_chargeFlag" + playwright: "page.locator('#form_item_chargeFlag')" + + el_e694b6e8b4b9e8aea1e9878fe58d95e4bd8dInput: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: id + value: "form_item_unitName" + playwright: "page.locator('#form_item_unitName')" + + el_e694b6e8b4b9e58d95e4bbb7Input: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + backBtn: + description: "返 回" + element_type: button + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)-新品入院" + strategy: role + role: button + name: "返 回" + playwright: "page.getByRole('button', { name: '返 回' })" + + # --- main::modal:操作日志 --- \ No newline at end of file diff --git a/locators/spd/admit-product-apply.locator.yaml b/locators/spd/admit-product-apply.locator.yaml new file mode 100644 index 0000000..ac2d2b3 --- /dev/null +++ b/locators/spd/admit-product-apply.locator.yaml @@ -0,0 +1,1985 @@ +# Auto-generated from admit-product-apply-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productApply/list +# Scanned: 2026-10-20T08:45:49.043Z +# Page: 基础设置 > 准入管理 > 产品准入申请 +# Note: filter dropdown was opened during scan +# Note: detail view was opened during scan +# Note: log view was opened during scan + +page: admit-product-apply +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productApply/list + +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: '准入任务分配' })" + + admitProductApply: + description: "产品准入申请" + element_type: tab + section: "main::main" + strategy: text + value: "产品准入申请" + playwright: "page.getByText('产品准入申请')" + + tab_admitProductApply: + description: "产品准入申请" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/dataSync/dataSync/productApply/list" + playwright: "page.locator('#rc-tabs-0-tab-/basic/dataSync/dataSync/productApply/list')" + + createDateDateRange: + 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')" + + createDateInput: + 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: '展开' })" + + el_457863656ce5afbce585a5Btn: + description: "Excel导入" + element_type: button + section: "main::main" + strategy: role + role: button + name: "Excel导入" + playwright: "page.getByRole('button', { name: 'Excel导入' })" + + el_e7949fe68890e794b3e8afb7e58d95Btn: + description: "生成申请单" + element_type: button + section: "main::main" + strategy: role + role: button + name: "生成申请单" + playwright: "page.getByRole('button', { name: '生成申请单' })" + + el_e4b880e994aee7949fe68890Btn: + description: "一键生成" + element_type: button + section: "main::main" + strategy: role + role: button + name: "一键生成" + playwright: "page.getByRole('button', { name: '一键生成' })" + + el_e696b0e5a29ee4baa7e59381Btn: + description: "新增产品" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新增产品" + playwright: "page.getByRole('button', { name: '新增产品' })" + + el_e4bd9ce5ba9fBtn: + description: "作 废" + element_type: button + section: "main::main" + strategy: role + role: button + name: "作 废" + playwright: "page.getByRole('button', { name: '作 废' })" + + cpzr202609240001Btn: + description: "CPZR202609240001" + element_type: button + section: "main::main" + strategy: role + role: button + name: "CPZR202609240001" + playwright: "page.getByRole('button', { name: 'CPZR202609240001' })" + + detailBtn: + description: "详情" + element_type: button + section: "main::main" + strategy: role + role: button + name: "详情" + playwright: "page.getByRole('button', { name: '详情' })" + + logBtn: + description: "日志" + element_type: button + section: "main::main" + strategy: role + role: button + name: "日志" + playwright: "page.getByRole('button', { name: '日志' })" + + cpzr202609220002Btn: + description: "CPZR202609220002" + element_type: button + section: "main::main" + strategy: role + role: button + name: "CPZR202609220002" + playwright: "page.getByRole('button', { name: 'CPZR202609220002' })" + + cpzr202609210002Btn: + description: "CPZR202609210002" + element_type: button + section: "main::main" + strategy: role + role: button + name: "CPZR202609210002" + playwright: "page.getByRole('button', { name: 'CPZR202609210002' })" + + cpzr202609210001Btn: + description: "CPZR202609210001" + element_type: button + section: "main::main" + strategy: role + role: button + name: "CPZR202609210001" + playwright: "page.getByRole('button', { name: 'CPZR202609210001' })" + + cpzr202610150002Btn: + description: "CPZR202610150002" + element_type: button + section: "main::main" + strategy: role + role: button + name: "CPZR202610150002" + playwright: "page.getByRole('button', { name: 'CPZR202610150002' })" + + cpzr202610160001Btn: + description: "CPZR202610160001" + element_type: button + section: "main::main" + strategy: role + role: button + name: "CPZR202610160001" + playwright: "page.getByRole('button', { name: 'CPZR202610160001' })" + + 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_el_e585b137e69da1313530e69da12fe9a1b5: + description: "共 7 条150 条/页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 7 条150 条/页" + playwright: "page.getByText('共 7 条150 条/页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + # --- main::detail:返回新增产品 --- + productName: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_goodsName" + playwright: "page.locator('#form_item_goodsName')" + + registrantSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "器械注册人" + playwright: "page.getByText('器械注册人', { exact: true })" + + manufacturer: + description: "器械注册人" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_goodsMfrsId" + playwright: "page.locator('#form_item_goodsMfrsId')" + + specModel: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_goodsSpec" + playwright: "page.locator('#form_item_goodsSpec')" + + el_e6b3a8e5868ce8a784e6a0bcInput: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_regCertSpec" + playwright: "page.locator('#form_item_regCertSpec')" + + el_e6b3a8e5868ce59e8be58fb7Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_regCertModel" + playwright: "page.locator('#form_item_regCertModel')" + + el_e58d95e4bd8dInput: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_unit" + playwright: "page.locator('#form_item_unit')" + + el_e5a283e58685e5a283e5a496Select: + description: "境内" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "境内境外" + playwright: "page.getByText('境内境外', { exact: true })" + + el_e5a283e58685e5a283e5a496Input: + description: "境内境外" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_abroadFlag" + playwright: "page.locator('#form_item_abroadFlag')" + + el_3638e58886e7b1bbSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "68分类" + playwright: "page.getByText('68分类', { exact: true })" + + el_3638e58886e7b1bbInput: + description: "68分类" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_kindSixtyeightCode" + playwright: "page.locator('#form_item_kindSixtyeightCode')" + + el_3138e58886e7b1bbSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "18分类" + playwright: "page.getByText('18分类', { exact: true })" + + el_3138e58886e7b1bbInput: + description: "18分类" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_kind18Code" + playwright: "page.locator('#form_item_kind18Code')" + + el_e696b0e7aea1e79086e7b1bbe588abSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "新管理类别" + playwright: "page.getByText('新管理类别', { exact: true })" + + el_e696b0e7aea1e79086e7b1bbe588abInput: + description: "新管理类别" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_riskLevel" + playwright: "page.locator('#form_item_riskLevel')" + + el_e697a7e7aea1e79086e7b1bbe588abSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "旧管理类别" + playwright: "page.getByText('旧管理类别', { exact: true })" + + el_e697a7e7aea1e79086e7b1bbe588abInput: + description: "旧管理类别" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_consumableLevel" + playwright: "page.locator('#form_item_consumableLevel')" + + registrationNo: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_goodsRegCert" + playwright: "page.locator('#form_item_goodsRegCert')" + + el_e7bb93e69e84e7bb84e68890Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_goodsDesc" + playwright: "page.locator('#form_item_goodsDesc')" + + el_e582a8e5ad98e69da1e4bbb6Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_storageConditions" + playwright: "page.locator('#form_item_storageConditions')" + + el_e8bf90e8be93e69da1e4bbb6Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_transportConditions" + playwright: "page.locator('#form_item_transportConditions')" + + el_e58f97e68998e7949fe4baa7e59586Select: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "受托生产商" + playwright: "page.getByText('受托生产商', { exact: true })" + + el_e58f97e68998e7949fe4baa7e59586Input: + description: "受托生产商" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_trustMrfsName" + playwright: "page.locator('#form_item_trustMrfsName')" + + el_e59381e7898cInput: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_brand" + playwright: "page.locator('#form_item_brand')" + + el_4449e7a081Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_goodsDI" + playwright: "page.locator('#form_item_goodsDI')" + + el_e698afe590a6e6a48de585a5Select: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "是否植入" + playwright: "page.getByText('是否植入', { exact: true })" + + el_e698afe590a6e6a48de585a5Input: + description: "是否植入" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_implantsFlag" + playwright: "page.locator('#form_item_implantsFlag')" + + el_e698afe590a6e58cbbe79697e599a8e6a2b0Select: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "是否医疗器械" + playwright: "page.getByText('是否医疗器械', { exact: true })" + + el_e698afe590a6e58cbbe79697e599a8e6a2b0Input: + description: "是否医疗器械" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_instrFlag" + playwright: "page.locator('#form_item_instrFlag')" + + el_e698afe590a6e58db1e58c96e59381Select: + description: "否" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "是否危化品" + playwright: "page.getByText('是否危化品', { exact: true })" + + el_e698afe590a6e58db1e58c96e59381Input: + description: "是否危化品" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_dangerFlag" + playwright: "page.locator('#form_item_dangerFlag')" + + el_e9878de9878fInput: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + el_e58786e585a5e59088e5908ce58fb7Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_contractNo" + playwright: "page.locator('#form_item_contractNo')" + + el_e58cbbe4bf9de7bc96e7a081Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_miCode" + playwright: "page.locator('#form_item_miCode')" + + el_e58cbbe4bf9de7bc96e7a081efbc883230e4bd8defbc89Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_miTwentyCode" + playwright: "page.locator('#form_item_miTwentyCode')" + + el_e58cbbe4bf9de7bc96e7a081efbc883237e4bd8defbc89Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_miTwentysevenCode" + playwright: "page.locator('#form_item_miTwentysevenCode')" + + el_e58cbbe4bf9de5908de7a7b0Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_miDjmc" + playwright: "page.locator('#form_item_miDjmc')" + + el_e58cbbe4bf9de8a784e6a0bcInput: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_miGoodsGg" + playwright: "page.locator('#form_item_miGoodsGg')" + + el_e58cbbe4bf9de59e8be58fb7Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_miGoodsXh" + playwright: "page.locator('#form_item_miGoodsXh')" + + el_e58cbbe4bf9de7babfe4b88ae694afe4bb98Select: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "医保线上支付" + playwright: "page.getByText('医保线上支付', { exact: true })" + + el_e58cbbe4bf9de7babfe4b88ae694afe4bb98Input: + description: "医保线上支付" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_miOnlinePay" + playwright: "page.locator('#form_item_miOnlinePay')" + + el_e4baa7e59381e4bf97e7a7b0Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_goodsGeneralName" + playwright: "page.locator('#form_item_goodsGeneralName')" + + remark: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_remark" + playwright: "page.locator('#form_item_remark')" + + el_e98787e8b4ade590abe9878fInput: + description: "采购的最小包装数量,默认1" + element_type: text + section: "main::detail:返回新增产品" + strategy: placeholder + value: "采购的最小包装数量,默认1" + playwright: "page.getByPlaceholder('采购的最小包装数量,默认1')" + + el_e98787e8b4ade58d95e4bd8dInput: + description: "采购的包装单位名称" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_purchaseUnit" + playwright: "page.locator('#form_item_purchaseUnit')" + + el_e8afb7e9a286e590abe9878fInput: + description: "请领的最小包装含量,默认1" + element_type: text + section: "main::detail:返回新增产品" + strategy: placeholder + value: "请领的最小包装含量,默认1" + playwright: "page.getByPlaceholder('请领的最小包装含量,默认1')" + + el_e8afb7e9a286e58d95e4bd8dInput: + description: "请领的最小包装单位名称" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_buyUnit" + playwright: "page.locator('#form_item_buyUnit')" + + el_e4babae4bbbde8bdace68da2e6af94Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + chargeableSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "是否收费" + playwright: "page.getByText('是否收费', { exact: true })" + + chargeable: + description: "是否收费" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_chargeFlag" + playwright: "page.locator('#form_item_chargeFlag')" + + el_e698afe590a6e4b8b4e98787Select: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "是否临采" + playwright: "page.getByText('是否临采', { exact: true })" + + el_e698afe590a6e4b8b4e98787Input: + description: "是否临采" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_tempFlag" + playwright: "page.locator('#form_item_tempFlag')" + + el_e698afe590a6e58fafe98787e8b4adSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "是否可采购" + playwright: "page.getByText('是否可采购', { exact: true })" + + el_e698afe590a6e58fafe98787e8b4adInput: + description: "是否可采购" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_purchaseFlag" + playwright: "page.locator('#form_item_purchaseFlag')" + + statusSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "状态" + playwright: "page.getByText('状态', { exact: true })" + + statusInput: + description: "状态" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_goodsStatus" + playwright: "page.locator('#form_item_goodsStatus')" + + consumableCategorySelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "耗材分类" + playwright: "page.getByText('耗材分类', { exact: true })" + + consumableType: + description: "耗材分类" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_goodsKind" + playwright: "page.locator('#form_item_goodsKind')" + + el_e88097e69d90e7b1bbe588abSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "耗材类别" + playwright: "page.getByText('耗材类别', { exact: true })" + + el_e88097e69d90e7b1bbe588abInput: + description: "耗材类别" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_purMode" + playwright: "page.locator('#form_item_purMode')" + + el_e698afe590a6e99c80e8a681e6b497e6b688Select: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "是否需要洗消" + playwright: "page.getByText('是否需要洗消', { exact: true })" + + el_e698afe590a6e99c80e8a681e6b497e6b688Input: + description: "是否需要洗消" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_sterilizeFlag" + playwright: "page.locator('#form_item_sterilizeFlag')" + + el_e8b4a2e58aa1e58886e7b1bbSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "财务分类" + playwright: "page.getByText('财务分类', { exact: true })" + + el_e8b4a2e58aa1e58886e7b1bbInput: + description: "财务分类" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_fcatKind" + playwright: "page.locator('#form_item_fcatKind')" + + el_e5b8a6e9878fe58886e7b1bbSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "带量分类" + playwright: "page.getByText('带量分类', { exact: true })" + + el_e5b8a6e9878fe58886e7b1bbInput: + description: "带量分类" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_vbpKind" + playwright: "page.locator('#form_item_vbpKind')" + + materialCode: + description: "填了则按用户填写的,未填写则系统自动编码" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_goodsCode" + playwright: "page.locator('#form_item_goodsCode')" + + el_e698afe590a652464944e7aea1e79086Select: + description: "否" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "是否RFID管理" + playwright: "page.getByText('是否RFID管理', { exact: true })" + + el_e698afe590a652464944e7aea1e79086Input: + description: "是否RFID管理" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_rfidFlag" + playwright: "page.locator('#form_item_rfidFlag')" + + el_e698afe590a6e68d90e8b5a0Select: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "是否捐赠" + playwright: "page.getByText('是否捐赠', { exact: true })" + + el_e698afe590a6e68d90e8b5a0Input: + description: "是否捐赠" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_ecoBat" + playwright: "page.locator('#form_item_ecoBat')" + + el_e698afe590a6e69c89554449Select: + description: "无码" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "是否有UDI" + playwright: "page.getByText('是否有UDI', { exact: true })" + + el_e698afe590a6e69c89554449Input: + description: "是否有UDI" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_UDIType" + playwright: "page.locator('#form_item_UDIType')" + + el_e7a88ee78e87Select: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "税率" + playwright: "page.getByText('税率', { exact: true })" + + el_e7a88ee78e87Input: + description: "税率" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_taxRate" + playwright: "page.locator('#form_item_taxRate')" + + el_e698afe590a6e99990e9878fe98787e8b4adSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "是否限量采购" + playwright: "page.getByText('是否限量采购', { exact: true })" + + el_e698afe590a6e99990e9878fe98787e8b4adInput: + description: "是否限量采购" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_limitPurchase" + playwright: "page.locator('#form_item_limitPurchase')" + + el_e68b9be6a087e69687e58fb7Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_bidCode" + playwright: "page.locator('#form_item_bidCode')" + + el_e99480e594aee59586Select: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "销售商" + playwright: "page.getByText('销售商', { exact: true })" + + supplierId: + description: "销售商" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_provId" + playwright: "page.locator('#form_item_provId')" + + el_e98787e8b4ade58d95e4bbb7Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + el_e4b89ae58aa1e6a8a1e5bc8fSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "业务模式" + playwright: "page.getByText('业务模式', { exact: true })" + + el_e4b89ae58aa1e6a8a1e5bc8fInput: + description: "业务模式" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_bizMode" + playwright: "page.locator('#form_item_bizMode')" + + dimensionSupplierSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "供应商" + playwright: "page.getByText('供应商', { exact: true })" + + subSupplierId: + description: "供应商" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_subProvId" + playwright: "page.locator('#form_item_subProvId')" + + el_e998b3e98787e7bc96e7a081Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_hitCode" + playwright: "page.locator('#form_item_hitCode')" + + el_e998b3e98787e4bbb7e6a0bcInput: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + el_e998b3e987874944Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_hitId" + playwright: "page.locator('#form_item_hitId')" + + el_485250e7bc96e7a081Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_hrp" + playwright: "page.locator('#form_item_hrp')" + + el_484953e7bc96e7a081Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_herpCode" + playwright: "page.locator('#form_item_herpCode')" + + el_e7babfe4b88ae98787e8b4ade7b1bbe588abSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增产品" + strategy: label_text + value: "线上采购类别" + playwright: "page.getByText('线上采购类别', { exact: true })" + + el_e7babfe4b88ae98787e8b4ade7b1bbe588abInput: + description: "线上采购类别" + element_type: search + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_onlineKind" + playwright: "page.locator('#form_item_onlineKind')" + + el_e4be9be5ba94e59586455250e7bc96e7a081Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: id + value: "form_item_erpCode" + playwright: "page.locator('#form_item_erpCode')" + + el_e4be9be5ba94e59586e4baa7e59381e8bdace68da2e6af94Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + el_e4be9be5ba94e59586e98787e8b4ade4bbb7Input: + description: "请输入" + element_type: text + section: "main::detail:返回新增产品" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + backBtn: + description: "返 回" + element_type: button + section: "main::detail:返回新增产品" + strategy: role + role: button + name: "返 回" + playwright: "page.getByRole('button', { name: '返 回' })" + + submitBtn: + description: "提 交" + element_type: button + section: "main::detail:返回新增产品" + strategy: role + role: button + name: "提 交" + playwright: "page.getByRole('button', { name: '提 交' })" + + # --- main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法) --- + productName_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_goodsName" + playwright: "page.locator('#form_item_goodsName')" + + registrantSelect_2: + description: "湖南海琅生物科技有限公司" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "器械注册人" + playwright: "page.getByText('器械注册人', { exact: true })" + + manufacturer_2: + description: "器械注册人" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_goodsMfrsId" + playwright: "page.locator('#form_item_goodsMfrsId')" + + specModel_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_goodsSpec" + playwright: "page.locator('#form_item_goodsSpec')" + + el_e6b3a8e5868ce8a784e6a0bcInput_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_regCertSpec" + playwright: "page.locator('#form_item_regCertSpec')" + + el_e6b3a8e5868ce59e8be58fb7Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_regCertModel" + playwright: "page.locator('#form_item_regCertModel')" + + el_e58d95e4bd8dInput_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_unit" + playwright: "page.locator('#form_item_unit')" + + el_e5a283e58685e5a283e5a496Select_2: + description: "境内" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "境内境外" + playwright: "page.getByText('境内境外', { exact: true })" + + el_e5a283e58685e5a283e5a496Input_2: + description: "境内境外" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_abroadFlag" + playwright: "page.locator('#form_item_abroadFlag')" + + el_3638e58886e7b1bbSelect_2: + description: "医用高分子材料及制品" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "68分类" + playwright: "page.getByText('68分类', { exact: true })" + + el_3638e58886e7b1bbInput_2: + description: "68分类" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_kindSixtyeightCode" + playwright: "page.locator('#form_item_kindSixtyeightCode')" + + el_3138e58886e7b1bbSelect_2: + description: "临床检验器械" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "18分类" + playwright: "page.getByText('18分类', { exact: true })" + + el_3138e58886e7b1bbInput_2: + description: "18分类" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_kind18Code" + playwright: "page.locator('#form_item_kind18Code')" + + el_e696b0e7aea1e79086e7b1bbe588abSelect_2: + description: "Ⅱ类" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "新管理类别" + playwright: "page.getByText('新管理类别', { exact: true })" + + el_e696b0e7aea1e79086e7b1bbe588abInput_2: + description: "新管理类别" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_riskLevel" + playwright: "page.locator('#form_item_riskLevel')" + + el_e697a7e7aea1e79086e7b1bbe588abSelect_2: + description: "Ⅱ类" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "旧管理类别" + playwright: "page.getByText('旧管理类别', { exact: true })" + + el_e697a7e7aea1e79086e7b1bbe588abInput_2: + description: "旧管理类别" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_consumableLevel" + playwright: "page.locator('#form_item_consumableLevel')" + + registrationNo_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_goodsRegCert" + playwright: "page.locator('#form_item_goodsRegCert')" + + el_e7bb93e69e84e7bb84e68890Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_goodsDesc" + playwright: "page.locator('#form_item_goodsDesc')" + + el_e582a8e5ad98e69da1e4bbb6Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_storageConditions" + playwright: "page.locator('#form_item_storageConditions')" + + el_e8bf90e8be93e69da1e4bbb6Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_transportConditions" + playwright: "page.locator('#form_item_transportConditions')" + + el_e58f97e68998e7949fe4baa7e59586Select_2: + description: "湖南海琅生物科技有限公司" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "受托生产商" + playwright: "page.getByText('受托生产商', { exact: true })" + + el_e58f97e68998e7949fe4baa7e59586Input_2: + description: "受托生产商" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_trustMrfsName" + playwright: "page.locator('#form_item_trustMrfsName')" + + el_e59381e7898cInput_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_brand" + playwright: "page.locator('#form_item_brand')" + + el_4449e7a081Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_goodsDI" + playwright: "page.locator('#form_item_goodsDI')" + + el_e698afe590a6e6a48de585a5Select_2: + description: "否" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "是否植入" + playwright: "page.getByText('是否植入', { exact: true })" + + el_e698afe590a6e6a48de585a5Input_2: + description: "是否植入" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_implantsFlag" + playwright: "page.locator('#form_item_implantsFlag')" + + el_e698afe590a6e58cbbe79697e599a8e6a2b0Select_2: + description: "诊断药品" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "是否医疗器械" + playwright: "page.getByText('是否医疗器械', { exact: true })" + + el_e698afe590a6e58cbbe79697e599a8e6a2b0Input_2: + description: "是否医疗器械" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_instrFlag" + playwright: "page.locator('#form_item_instrFlag')" + + el_e698afe590a6e58db1e58c96e59381Select_2: + description: "否" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "是否危化品" + playwright: "page.getByText('是否危化品', { exact: true })" + + el_e698afe590a6e58db1e58c96e59381Input_2: + description: "是否危化品" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_dangerFlag" + playwright: "page.locator('#form_item_dangerFlag')" + + el_e9878de9878fInput_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + el_e58786e585a5e59088e5908ce58fb7Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_contractNo" + playwright: "page.locator('#form_item_contractNo')" + + el_e58cbbe4bf9de7bc96e7a081Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_miCode" + playwright: "page.locator('#form_item_miCode')" + + el_e58cbbe4bf9de7bc96e7a081efbc883230e4bd8defbc89Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_miTwentyCode" + playwright: "page.locator('#form_item_miTwentyCode')" + + el_e58cbbe4bf9de7bc96e7a081efbc883237e4bd8defbc89Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_miTwentysevenCode" + playwright: "page.locator('#form_item_miTwentysevenCode')" + + el_e58cbbe4bf9de5908de7a7b0Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_miDjmc" + playwright: "page.locator('#form_item_miDjmc')" + + el_e58cbbe4bf9de8a784e6a0bcInput_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_miGoodsGg" + playwright: "page.locator('#form_item_miGoodsGg')" + + el_e58cbbe4bf9de59e8be58fb7Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_miGoodsXh" + playwright: "page.locator('#form_item_miGoodsXh')" + + el_e58cbbe4bf9de7babfe4b88ae694afe4bb98Select_2: + description: "否" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "医保线上支付" + playwright: "page.getByText('医保线上支付', { exact: true })" + + el_e58cbbe4bf9de7babfe4b88ae694afe4bb98Input_2: + description: "医保线上支付" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_miOnlinePay" + playwright: "page.locator('#form_item_miOnlinePay')" + + el_e4baa7e59381e4bf97e7a7b0Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_goodsGeneralName" + playwright: "page.locator('#form_item_goodsGeneralName')" + + remark_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_remark" + playwright: "page.locator('#form_item_remark')" + + el_e98787e8b4ade590abe9878fInput_2: + description: "采购的最小包装数量,默认1" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: placeholder + value: "采购的最小包装数量,默认1" + playwright: "page.getByPlaceholder('采购的最小包装数量,默认1')" + + el_e98787e8b4ade58d95e4bd8dInput_2: + description: "采购的包装单位名称" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_purchaseUnit" + playwright: "page.locator('#form_item_purchaseUnit')" + + el_e8afb7e9a286e590abe9878fInput_2: + description: "请领的最小包装含量,默认1" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: placeholder + value: "请领的最小包装含量,默认1" + playwright: "page.getByPlaceholder('请领的最小包装含量,默认1')" + + el_e8afb7e9a286e58d95e4bd8dInput_2: + description: "请领的最小包装单位名称" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_buyUnit" + playwright: "page.locator('#form_item_buyUnit')" + + el_e4babae4bbbde8bdace68da2e6af94Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + chargeableSelect_2: + description: "是" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "是否收费" + playwright: "page.getByText('是否收费', { exact: true })" + + chargeable_2: + description: "是否收费" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_chargeFlag" + playwright: "page.locator('#form_item_chargeFlag')" + + el_e698afe590a6e4b8b4e98787Select_2: + description: "否" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "是否临采" + playwright: "page.getByText('是否临采', { exact: true })" + + el_e698afe590a6e4b8b4e98787Input_2: + description: "是否临采" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_tempFlag" + playwright: "page.locator('#form_item_tempFlag')" + + el_e698afe590a6e58fafe98787e8b4adSelect_2: + description: "是" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "是否可采购" + playwright: "page.getByText('是否可采购', { exact: true })" + + el_e698afe590a6e58fafe98787e8b4adInput_2: + description: "是否可采购" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_purchaseFlag" + playwright: "page.locator('#form_item_purchaseFlag')" + + statusSelect_2: + description: "启用" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "状态" + playwright: "page.getByText('状态', { exact: true })" + + statusInput_2: + description: "状态" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_goodsStatus" + playwright: "page.locator('#form_item_goodsStatus')" + + consumableCategorySelect_2: + description: "低值易耗品 / 低值易耗品-其他材料 / 基础卫生材料-其他材料-其他材料" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "耗材分类" + playwright: "page.getByText('耗材分类', { exact: true })" + + consumableType_2: + description: "耗材分类" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_goodsKind" + playwright: "page.locator('#form_item_goodsKind')" + + el_e88097e69d90e7b1bbe588abSelect_2: + description: "低值" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "耗材类别" + playwright: "page.getByText('耗材类别', { exact: true })" + + el_e88097e69d90e7b1bbe588abInput_2: + description: "耗材类别" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_purMode" + playwright: "page.locator('#form_item_purMode')" + + el_e698afe590a6e99c80e8a681e6b497e6b688Select_2: + description: "否" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "是否需要洗消" + playwright: "page.getByText('是否需要洗消', { exact: true })" + + el_e698afe590a6e99c80e8a681e6b497e6b688Input_2: + description: "是否需要洗消" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_sterilizeFlag" + playwright: "page.locator('#form_item_sterilizeFlag')" + + el_e8b4a2e58aa1e58886e7b1bbSelect_2: + description: "卫生材料 / 卫材-低值易耗" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "财务分类" + playwright: "page.getByText('财务分类', { exact: true })" + + el_e8b4a2e58aa1e58886e7b1bbInput_2: + description: "财务分类" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_fcatKind" + playwright: "page.locator('#form_item_fcatKind')" + + el_e5b8a6e9878fe58886e7b1bbSelect_2: + description: "非带量 / 自行采购" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "带量分类" + playwright: "page.getByText('带量分类', { exact: true })" + + el_e5b8a6e9878fe58886e7b1bbInput_2: + description: "带量分类" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_vbpKind" + playwright: "page.locator('#form_item_vbpKind')" + + materialCode_2: + description: "填了则按用户填写的,未填写则系统自动编码" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_goodsCode" + playwright: "page.locator('#form_item_goodsCode')" + + el_e698afe590a652464944e7aea1e79086Select_2: + description: "否" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "是否RFID管理" + playwright: "page.getByText('是否RFID管理', { exact: true })" + + el_e698afe590a652464944e7aea1e79086Input_2: + description: "是否RFID管理" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_rfidFlag" + playwright: "page.locator('#form_item_rfidFlag')" + + el_e698afe590a6e68d90e8b5a0Select_2: + description: "否" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "是否捐赠" + playwright: "page.getByText('是否捐赠', { exact: true })" + + el_e698afe590a6e68d90e8b5a0Input_2: + description: "是否捐赠" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_ecoBat" + playwright: "page.locator('#form_item_ecoBat')" + + el_e698afe590a6e69c89554449Select_2: + description: "医疗器械GS1-UDI" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "是否有UDI" + playwright: "page.getByText('是否有UDI', { exact: true })" + + el_e698afe590a6e69c89554449Input_2: + description: "是否有UDI" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_UDIType" + playwright: "page.locator('#form_item_UDIType')" + + el_e7a88ee78e87Select_2: + description: "13%" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "税率" + playwright: "page.getByText('税率', { exact: true })" + + el_e7a88ee78e87Input_2: + description: "税率" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_taxRate" + playwright: "page.locator('#form_item_taxRate')" + + el_e698afe590a6e99990e9878fe98787e8b4adSelect_2: + description: "否" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "是否限量采购" + playwright: "page.getByText('是否限量采购', { exact: true })" + + el_e698afe590a6e99990e9878fe98787e8b4adInput_2: + description: "是否限量采购" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_limitPurchase" + playwright: "page.locator('#form_item_limitPurchase')" + + el_e68b9be6a087e69687e58fb7Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_bidCode" + playwright: "page.locator('#form_item_bidCode')" + + el_e99480e594aee59586Select_2: + description: "浙江高仰医疗科技有限公司" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "销售商" + playwright: "page.getByText('销售商', { exact: true })" + + supplierId_2: + description: "销售商" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_provId" + playwright: "page.locator('#form_item_provId')" + + el_e98787e8b4ade58d95e4bbb7Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + el_e4b89ae58aa1e6a8a1e5bc8fSelect_2: + description: "直销" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "业务模式" + playwright: "page.getByText('业务模式', { exact: true })" + + el_e4b89ae58aa1e6a8a1e5bc8fInput_2: + description: "业务模式" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_bizMode" + playwright: "page.locator('#form_item_bizMode')" + + el_e998b3e98787e7bc96e7a081Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_hitCode" + playwright: "page.locator('#form_item_hitCode')" + + el_e998b3e98787e4bbb7e6a0bcInput_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + el_e998b3e987874944Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_hitId" + playwright: "page.locator('#form_item_hitId')" + + el_485250e7bc96e7a081Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_hrp" + playwright: "page.locator('#form_item_hrp')" + + el_484953e7bc96e7a081Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_herpCode" + playwright: "page.locator('#form_item_herpCode')" + + el_e7babfe4b88ae98787e8b4ade7b1bbe588abSelect_2: + description: "备案采购" + element_type: select + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: label_text + value: "线上采购类别" + playwright: "page.getByText('线上采购类别', { exact: true })" + + el_e7babfe4b88ae98787e8b4ade7b1bbe588abInput_2: + description: "线上采购类别" + element_type: search + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_onlineKind" + playwright: "page.locator('#form_item_onlineKind')" + + el_e4be9be5ba94e59586455250e7bc96e7a081Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: id + value: "form_item_erpCode" + playwright: "page.locator('#form_item_erpCode')" + + el_e4be9be5ba94e59586e4baa7e59381e8bdace68da2e6af94Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + el_e4be9be5ba94e59586e98787e8b4ade4bbb7Input_2: + description: "请输入" + element_type: text + section: "main::detail:返回丙氨酸氨基转移酶测定试剂盒(IFCC法)" + strategy: placeholder + value: "请输入" + playwright: "page.getByPlaceholder('请输入')" + + # --- main::modal:操作日志 --- \ No newline at end of file diff --git a/locators/spd/admit-product-list.locator.yaml b/locators/spd/admit-product-list.locator.yaml new file mode 100644 index 0000000..fbd0a2a --- /dev/null +++ b/locators/spd/admit-product-list.locator.yaml @@ -0,0 +1,451 @@ +# Auto-generated from admit-product-list-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productOrder/list +# Scanned: 2026-10-20T08:46:15.237Z +# Page: 基础设置 > 准入管理 > 产品准入单列表 +# Note: filter dropdown was opened during scan +# Note: detail view was opened during scan + +page: admit-product-list +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productOrder/list + +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: '准入任务分配' })" + + admitProductList: + description: "产品准入单列表" + element_type: tab + section: "main::main" + strategy: text + value: "产品准入单列表" + playwright: "page.getByText('产品准入单列表')" + + tab_admitProductList: + description: "产品准入单列表" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/dataSync/dataSync/productOrder/list" + playwright: "page.locator('#rc-tabs-0-tab-/basic/dataSync/dataSync/productOrder/list')" + + 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('请输入物料编码、名称')" + + 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_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 条/页')" diff --git a/locators/spd/admit-shared.locator.ts b/locators/spd/admit-shared.locator.ts new file mode 100644 index 0000000..6eefaf2 --- /dev/null +++ b/locators/spd/admit-shared.locator.ts @@ -0,0 +1,44 @@ +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(); + } +} diff --git a/locators/spd/admit-supply.locator.yaml b/locators/spd/admit-supply.locator.yaml new file mode 100644 index 0000000..ca8a54c --- /dev/null +++ b/locators/spd/admit-supply.locator.yaml @@ -0,0 +1,596 @@ +# Auto-generated from admit-supply-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/supplyApply/list +# Scanned: 2026-10-20T08:46:30.524Z +# Page: 基础设置 > 准入管理 > 供货目录申请 +# Note: filter dropdown was opened during scan +# Note: detail view was opened during scan + +page: admit-supply +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/supplyApply/list + +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: '准入任务分配' })" + + admitSupply: + description: "供货目录申请" + element_type: tab + section: "main::main" + strategy: text + value: "供货目录申请" + playwright: "page.getByText('供货目录申请')" + + tab_admitSupply: + description: "供货目录申请" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/dataSync/dataSync/supplyApply/list" + playwright: "page.locator('#rc-tabs-0-tab-/basic/dataSync/dataSync/supplyApply/list')" + + 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('结束日期')" + + el_e99480e594aee59586Select: + description: "请选择" + element_type: select + section: "main::main" + strategy: label_text + value: "销售商" + playwright: "page.getByText('销售商', { exact: true })" + + supplierId: + description: "销售商" + element_type: search + section: "main::main" + strategy: id + value: "table_search_form_provId" + playwright: "page.locator('#table_search_form_provId')" + + dimensionSupplierSelect: + description: "请选择" + element_type: select + section: "main::main" + strategy: label_text + value: "供应商" + playwright: "page.getByText('供应商', { exact: true })" + + dimensionSupplierInput: + description: "供应商" + element_type: search + section: "main::main" + strategy: id + value: "table_search_form_subProvId" + playwright: "page.locator('#table_search_form_subProvId')" + + 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: '展开' })" + + el_e689b9e9878fe68f90e4baa4283029Btn: + description: "批量提交(0)" + element_type: button + section: "main::main" + strategy: role + role: button + name: "批量提交(0)" + playwright: "page.getByRole('button', { name: '批量提交(0)' })" + + addBtn: + 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: '编辑' })" + + # --- main::pagination --- + pagination_el_e585b13138e69da1313530e69da12fe9a1b5: + description: "共 18 条150 条/页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 18 条150 条/页" + playwright: "page.getByText('共 18 条150 条/页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + # --- main::detail:返回新增供货目录 --- + el_e99480e594aee59586Select_2: + description: "请选择" + element_type: select + section: "main::detail:返回新增供货目录" + strategy: label_text + value: "销售商" + playwright: "page.getByText('销售商', { exact: true })" + + supplierId_2: + description: "销售商" + element_type: search + section: "main::detail:返回新增供货目录" + strategy: id + value: "form_item_provId" + playwright: "page.locator('#form_item_provId')" + + dimensionSupplierSelect_2: + description: "请选择" + element_type: select + section: "main::detail:返回新增供货目录" + strategy: label_text + value: "供应商" + playwright: "page.getByText('供应商', { exact: true })" + + subSupplierId: + description: "供应商" + element_type: search + section: "main::detail:返回新增供货目录" + strategy: id + value: "form_item_subProvId" + playwright: "page.locator('#form_item_subProvId')" + + el_e4b89ae58aa1e6a8a1e5bc8fSelect: + description: "请选择" + element_type: select + section: "main::detail:返回新增供货目录" + strategy: label_text + value: "业务模式" + playwright: "page.getByText('业务模式', { exact: true })" + + el_e698afe590a6e9bb98e8aea4Select: + description: "请选择" + element_type: select + section: "main::detail:返回新增供货目录" + strategy: label_text + value: "是否默认" + playwright: "page.getByText('是否默认', { exact: true })" + + el_e8aea1e58892e7949fe69588e697b6e997b4DateRange: + description: "计划生效时间" + element_type: datepicker + section: "main::detail:返回新增供货目录" + strategy: label_text + value: "计划生效时间" + playwright: "page.getByText('计划生效时间', { exact: true })" + + el_e8aea1e58892e7949fe69588e697b6e997b4Input: + description: "2026-10-20 16:46:32" + element_type: text + section: "main::detail:返回新增供货目录" + strategy: id + value: "form_item_planEffectDate" + playwright: "page.locator('#form_item_planEffectDate')" + + el_e585b3e88194e68e88e69d83e58f98e69bb4e794b3e8afb7Input: + description: "请选择授权变更申请" + element_type: text + section: "main::detail:返回新增供货目录" + strategy: id + value: "form_item_relOrderNo" + playwright: "page.locator('#form_item_relOrderNo')" + + remark: + description: "备注" + element_type: text + section: "main::detail:返回新增供货目录" + strategy: id + value: "form_item_remark" + playwright: "page.locator('#form_item_remark')" + + el_30e4b8aa: + description: "上传附件" + element_type: span + section: "main::detail:返回新增供货目录" + strategy: role + role: button + name: "上传附件" + playwright: "page.getByRole('button', { name: '上传附件' })" + + el_e4b88ae4bca0e99984e4bbb6Btn: + description: "上传附件" + element_type: button + section: "main::detail:返回新增供货目录" + strategy: role + role: button + name: "上传附件" + playwright: "page.getByRole('button', { name: '上传附件' })" + + addProductBtn: + description: "添加产品" + element_type: button + section: "main::detail:返回新增供货目录" + strategy: role + role: button + name: "添加产品" + playwright: "page.getByRole('button', { name: '添加产品' })" + + el_e5afbce585a5657863656cBtn: + description: "导入excel" + element_type: button + section: "main::detail:返回新增供货目录" + strategy: role + role: button + name: "导入excel" + playwright: "page.getByRole('button', { name: '导入excel' })" + + tableDensity: + description: "column-height" + element_type: span + section: "main::detail:返回新增供货目录" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + submitBtn: + description: "提 交" + element_type: button + section: "main::detail:返回新增供货目录" + strategy: role + role: button + name: "提 交" + playwright: "page.getByRole('button', { name: '提 交' })" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::detail:返回新增供货目录" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + # --- 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: '返 回' })" + + tableDensity_2: + description: "column-height" + element_type: span + section: "main::detail:供货目录详情" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + 0Input: + description: "40" + element_type: text + section: "main::detail:供货目录详情" + strategy: role + role: spinbutton + name: "40" + playwright: "page.getByRole('spinbutton', { name: '40' })" + + 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 条/页')" diff --git a/locators/spd/admit-task.locator.yaml b/locators/spd/admit-task.locator.yaml new file mode 100644 index 0000000..b2ffbb3 --- /dev/null +++ b/locators/spd/admit-task.locator.yaml @@ -0,0 +1,348 @@ +# Auto-generated from admit-task-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/accessKind/list +# Scanned: 2026-10-20T08:46:44.025Z +# Page: 基础设置 > 准入管理 > 准入任务分配 + +page: admit-task +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/accessKind/list + +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: '准入任务分配' })" + + admitTask: + description: "准入任务分配" + element_type: tab + section: "main::main" + strategy: text + value: "准入任务分配" + playwright: "page.getByText('准入任务分配')" + + tab_admitTask: + description: "准入任务分配" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/dataSync/dataSync/accessKind/list" + playwright: "page.locator('#rc-tabs-0-tab-/basic/dataSync/dataSync/accessKind/list')" + + createDateDateRange: + 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')" + + createDateInput: + description: "结束日期" + element_type: text + section: "main::main" + strategy: placeholder + value: "结束日期" + playwright: "page.getByPlaceholder('结束日期')" + + el_e6938de4bd9ce697a5e69c9fDateRange: + description: "操作日期" + element_type: datepicker + section: "main::main" + strategy: label_text + value: "操作日期" + playwright: "page.getByText('操作日期', { exact: true })" + + el_e6938de4bd9ce697a5e69c9fInput: + description: "开始日期" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_modifyValue" + playwright: "page.locator('#table_search_form_modifyValue')" + + el_e6938de4bd9ce697a5e69c9fInput_2: + 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')" + + 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: '展开' })" + + el_e689b9e9878fe58886e9858dBtn: + description: "批量分配" + element_type: button + section: "main::main" + strategy: role + role: button + name: "批量分配" + playwright: "page.getByRole('button', { name: '批量分配' })" + + el_e58886e9858de7b1bbe59e8bBtn: + description: "分配类型" + element_type: button + section: "main::main" + strategy: role + role: button + name: "分配类型" + playwright: "page.getByRole('button', { name: '分配类型' })" + + # --- main::pagination --- + pagination_el_e585b132e69da1313530e69da12fe9a1b5: + description: "共 2 条150 条/页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 2 条150 条/页" + playwright: "page.getByText('共 2 条150 条/页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" diff --git a/locators/spd/basic-branch.locator.ts b/locators/spd/basic-branch.locator.ts new file mode 100644 index 0000000..fb5b3bf --- /dev/null +++ b/locators/spd/basic-branch.locator.ts @@ -0,0 +1,25 @@ +import type { Locator, Page } from '@playwright/test'; + +import { BasicSharedLocators } from './basic-shared.locator'; + +/** 院区管理。选择器来自 locators/spd/basic-branch.locator.yaml */ +export class BasicBranchLocators extends BasicSharedLocators { + readonly addBtn: Locator; + readonly campusNameInput: Locator; + readonly addName: Locator; + readonly addAddress: Locator; + readonly editId: Locator; + readonly editName: Locator; + readonly editAddress: Locator; + + constructor(page: Page) { + super(page); + this.addBtn = page.getByRole('button', { name: /新\s*增/ }); + this.campusNameInput = page.getByPlaceholder('院区名称'); + this.addName = page.locator('#basic_name'); + this.addAddress = page.locator('#basic_address'); + this.editId = page.locator('#form_item_id'); + this.editName = page.locator('#form_item_name'); + this.editAddress = page.locator('#form_item_address'); + } +} diff --git a/locators/spd/basic-branch.locator.yaml b/locators/spd/basic-branch.locator.yaml new file mode 100644 index 0000000..7d0e3a8 --- /dev/null +++ b/locators/spd/basic-branch.locator.yaml @@ -0,0 +1,405 @@ +# Auto-generated from basic-branch-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/hospital/paltform/area +# Scanned: 2026-10-20T08:39:31.067Z +# Page: 基础设置 > 医院资料 > 院区管理 +# Note: dialog/modal was opened during scan +# Note: edit dialog was opened during scan + +page: basic-branch +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/hospital/paltform/area + +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_hospitalInfoRegister: + description: "医院信息登记" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "医院信息登记" + playwright: "page.getByRole('menuitem', { name: '医院信息登记' })" + + menu_branchManage: + description: "院区管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "院区管理" + playwright: "page.getByRole('menuitem', { name: '院区管理' })" + + menu_noticeManage: + description: "公告管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "公告管理" + playwright: "page.getByRole('menuitem', { name: '公告管理' })" + + menu_financeMonthSetting: + description: "财务月份设置" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "财务月份设置" + playwright: "page.getByRole('menuitem', { name: '财务月份设置' })" + + branchManage: + description: "院区管理" + element_type: tab + section: "main::main" + strategy: text + value: "院区管理" + playwright: "page.getByText('院区管理')" + + tab_branchManage: + description: "院区管理" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/hospital/paltform/area" + playwright: "page.locator('#rc-tabs-0-tab-/basic/hospital/paltform/area')" + + addBtn: + description: "新 增" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新 增" + playwright: "page.getByRole('button', { name: '新 增' })" + + addInput: + description: "院区名称" + element_type: text + section: "main::main" + strategy: placeholder + value: "院区名称" + playwright: "page.getByPlaceholder('院区名称')" + + tableDensity: + description: "column-height" + element_type: span + section: "main::main" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + editBtn: + description: "编辑" + element_type: button + section: "main::main" + strategy: role + role: button + name: "编辑" + playwright: "page.getByRole('button', { name: '编辑' })" + + # --- main::pagination --- + pagination_el_e585b13138e69da1313530e69da12fe9a1b5: + description: "共 18 条150 条/页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 18 条150 条/页" + playwright: "page.getByText('共 18 条150 条/页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + # --- main::modal:新增院区 --- + campusNameInput: + description: "请输入院区名称" + element_type: text + section: "main::modal:新增院区" + strategy: id + value: "basic_name" + playwright: "page.locator('#basic_name')" + + el_e68980e59ca8e59cb0e58cbaSelect: + description: "请选择" + element_type: select + section: "main::modal:新增院区" + strategy: label_text + value: "所在地区" + playwright: "page.getByText('所在地区', { exact: true })" + + el_e8afa6e7bb86e59cb0e59d80Input: + description: "请输入" + element_type: text + section: "main::modal:新增院区" + strategy: id + value: "basic_address" + playwright: "page.locator('#basic_address')" + + el_e590a6: + description: "否" + element_type: radio + section: "main::modal:新增院区" + strategy: text + value: "否" + playwright: "page.getByText('否')" + + el_e698af: + description: "是" + element_type: radio + section: "main::modal:新增院区" + strategy: text + value: "是" + playwright: "page.getByText('是')" + + disable: + description: "停用" + element_type: radio + section: "main::modal:新增院区" + strategy: text + value: "停用" + playwright: "page.getByText('停用')" + + enable: + description: "启用" + element_type: radio + section: "main::modal:新增院区" + strategy: text + value: "启用" + playwright: "page.getByText('启用')" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:新增院区" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + confirmBtn: + description: "确 定" + element_type: button + section: "main::modal:新增院区" + strategy: role + role: button + name: "确 定" + playwright: "page.getByRole('button', { name: '确 定' })" + + # --- main::modal:编辑院区 --- + el_e999a2e58cbae7bc96e58fb7Input: + description: "请输入" + element_type: text + section: "main::modal:编辑院区" + strategy: id + value: "form_item_id" + playwright: "page.locator('#form_item_id')" + + campusNameInput_2: + description: "请输入" + element_type: text + section: "main::modal:编辑院区" + strategy: id + value: "form_item_name" + playwright: "page.locator('#form_item_name')" + + el_e68980e59ca8e59cb0e58cbaSelect_2: + description: "湖北省 / 武汉市 / 洪山区" + element_type: select + section: "main::modal:编辑院区" + strategy: label_text + value: "所在地区" + playwright: "page.getByText('所在地区', { exact: true })" + + el_e8afa6e7bb86e59cb0e59d80Input_2: + description: "请输入" + element_type: text + section: "main::modal:编辑院区" + strategy: id + value: "form_item_address" + playwright: "page.locator('#form_item_address')" + + el_e590a6_2: + description: "否" + element_type: radio + section: "main::modal:编辑院区" + strategy: text + value: "否" + playwright: "page.getByText('否')" + + el_e698af_2: + description: "是" + element_type: radio + section: "main::modal:编辑院区" + strategy: text + value: "是" + playwright: "page.getByText('是')" + + el_e7a681e794a8: + description: "禁用" + element_type: radio + section: "main::modal:编辑院区" + strategy: text + value: "禁用" + playwright: "page.getByText('禁用')" + + enable_2: + description: "启用" + element_type: radio + section: "main::modal:编辑院区" + strategy: text + value: "启用" + playwright: "page.getByText('启用')" diff --git a/locators/spd/basic-finance-month.locator.ts b/locators/spd/basic-finance-month.locator.ts new file mode 100644 index 0000000..0740509 --- /dev/null +++ b/locators/spd/basic-finance-month.locator.ts @@ -0,0 +1,25 @@ +import type { Locator, Page } from '@playwright/test'; + +import { BasicSharedLocators } from './basic-shared.locator'; + +/** 财务月份设置。选择器来自 locators/spd/basic-finance-month.locator.yaml */ +export class BasicFinanceMonthLocators extends BasicSharedLocators { + readonly settleMonth: Locator; + readonly searchBtn: Locator; + readonly resetBtn: Locator; + readonly modifyBtn: Locator; + readonly autoSettleDateInput: Locator; + readonly saveBtn: Locator; + readonly cancelBtn: Locator; + + constructor(page: Page) { + super(page); + this.settleMonth = page.locator('#form_item_settleMonthDate'); + this.searchBtn = page.getByRole('button', { name: /查\s*询/ }); + this.resetBtn = page.getByRole('button', { name: /重\s*置/ }); + this.modifyBtn = page.getByRole('button', { name: /修\s*改/ }); + this.autoSettleDateInput = page.getByPlaceholder('请选择日期'); + this.saveBtn = page.getByRole('button', { name: /保\s*存/ }); + this.cancelBtn = page.getByRole('button', { name: /取\s*消/ }); + } +} diff --git a/locators/spd/basic-finance-month.locator.yaml b/locators/spd/basic-finance-month.locator.yaml new file mode 100644 index 0000000..f2761d1 --- /dev/null +++ b/locators/spd/basic-finance-month.locator.yaml @@ -0,0 +1,306 @@ +# Auto-generated from basic-finance-month-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/hospital/balance/month +# Scanned: 2026-10-20T08:38:57.574Z +# Page: 基础设置 > 医院资料 > 财务月份设置 + +page: basic-finance-month +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/hospital/balance/month + +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_hospitalInfoRegister: + description: "医院信息登记" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "医院信息登记" + playwright: "page.getByRole('menuitem', { name: '医院信息登记' })" + + menu_branchManage: + description: "院区管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "院区管理" + playwright: "page.getByRole('menuitem', { name: '院区管理' })" + + menu_noticeManage: + description: "公告管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "公告管理" + playwright: "page.getByRole('menuitem', { name: '公告管理' })" + + menu_financeMonthSetting: + description: "财务月份设置" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "财务月份设置" + playwright: "page.getByRole('menuitem', { name: '财务月份设置' })" + + financeMonthSetting: + description: "财务月份设置" + element_type: tab + section: "main::main" + strategy: text + value: "财务月份设置" + playwright: "page.getByText('财务月份设置')" + + tab_financeMonthSetting: + description: "财务月份设置" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/hospital/balance/month" + playwright: "page.locator('#rc-tabs-0-tab-/basic/hospital/balance/month')" + + el_e8b4a2e58aa1e69c88e4bbbdDateRange: + description: "财务月份" + element_type: datepicker + section: "main::main" + strategy: label_text + value: "财务月份" + playwright: "page.getByText('财务月份', { exact: true })" + + el_e8b4a2e58aa1e69c88e4bbbdInput: + description: "请选择财务月份" + element_type: text + section: "main::main" + strategy: id + value: "form_item_settleMonthDate" + playwright: "page.locator('#form_item_settleMonthDate')" + + 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: '重 置' })" + + tableDensity: + description: "column-height" + element_type: span + section: "main::main" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + el_e4bfaee694b9Btn: + description: "修改" + element_type: button + section: "main::main" + strategy: role + role: button + name: "修改" + playwright: "page.getByRole('button', { name: '修改' })" + + el_e8afb7e98089e68ba9e697a5e69c9fInput: + description: "2026-10-20" + element_type: text + section: "main::main" + strategy: placeholder + value: "请选择日期" + playwright: "page.getByPlaceholder('请选择日期')" + + saveBtn: + description: "保存" + element_type: button + section: "main::main" + strategy: role + role: button + name: "保存" + playwright: "page.getByRole('button', { name: '保存' })" + + cancelBtn: + description: "取消" + element_type: button + section: "main::main" + strategy: role + role: button + name: "取消" + playwright: "page.getByRole('button', { name: '取消' })" + + # --- main::pagination --- + pagination_el_e585b13236e69da1313530e69da12fe9a1b5: + description: "共 26 条150 条/页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 26 条150 条/页" + playwright: "page.getByText('共 26 条150 条/页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" diff --git a/locators/spd/basic-hospital-info.locator.ts b/locators/spd/basic-hospital-info.locator.ts new file mode 100644 index 0000000..ac71390 --- /dev/null +++ b/locators/spd/basic-hospital-info.locator.ts @@ -0,0 +1,29 @@ +import type { Locator, Page } from '@playwright/test'; + +import { BasicSharedLocators } from './basic-shared.locator'; + +/** 医院信息登记。选择器来自 locators/spd/basic-hospital-info.locator.yaml */ +export class BasicHospitalInfoLocators extends BasicSharedLocators { + readonly editBtn: Locator; + readonly saveBtn: Locator; + readonly cancelBtn: Locator; + readonly linkman: Locator; + readonly contactWay: Locator; + readonly shortName: Locator; + readonly loginBgUpload: Locator; + readonly logoUpload: Locator; + readonly previewLink: Locator; + + constructor(page: Page) { + super(page); + this.editBtn = page.getByRole('button', { name: /编\s*辑/ }); + this.saveBtn = page.getByRole('button', { name: /保\s*存/ }); + this.cancelBtn = page.getByRole('button', { name: /取\s*消/ }); + this.linkman = page.locator('#form_item_linkman'); + this.contactWay = page.locator('#form_item_contactWay'); + this.shortName = page.locator('#form_item_anotherName'); + this.loginBgUpload = page.locator('input[type="file"]').nth(0); + this.logoUpload = page.locator('input[type="file"]').nth(1); + this.previewLink = page.getByRole('link', { name: '预览文件' }); + } +} diff --git a/locators/spd/basic-hospital-info.locator.yaml b/locators/spd/basic-hospital-info.locator.yaml new file mode 100644 index 0000000..170b8f9 --- /dev/null +++ b/locators/spd/basic-hospital-info.locator.yaml @@ -0,0 +1,271 @@ +# Auto-generated from basic-hospital-info-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/hospital/paltform/info +# Scanned: 2026-10-20T08:38:47.297Z +# Page: 基础设置 > 医院资料 > 医院信息登记 + +page: basic-hospital-info +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/hospital/paltform/info + +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_hospitalInfoRegister: + description: "医院信息登记" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "医院信息登记" + playwright: "page.getByRole('menuitem', { name: '医院信息登记' })" + + menu_branchManage: + description: "院区管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "院区管理" + playwright: "page.getByRole('menuitem', { name: '院区管理' })" + + menu_noticeManage: + description: "公告管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "公告管理" + playwright: "page.getByRole('menuitem', { name: '公告管理' })" + + menu_financeMonthSetting: + description: "财务月份设置" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "财务月份设置" + playwright: "page.getByRole('menuitem', { name: '财务月份设置' })" + + hospitalInfoRegister: + description: "医院信息登记" + element_type: tab + section: "main::main" + strategy: text + value: "医院信息登记" + playwright: "page.getByText('医院信息登记')" + + tab_hospitalInfoRegister: + description: "医院信息登记" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/hospital/paltform/info" + playwright: "page.locator('#rc-tabs-0-tab-/basic/hospital/paltform/info')" + + editBtn: + description: "编 辑" + element_type: button + section: "main::main" + strategy: role + role: button + name: "编 辑" + playwright: "page.getByRole('button', { name: '编 辑' })" + + el_e799bbe5bd95e9a1b5e8838ce699af: + description: "登录页背景" + element_type: a + section: "main::main" + strategy: label_text + value: "登录页背景" + playwright: "page.getByText('登录页背景', { exact: true })" + + el_e799bbe5bd95e9a1b5e8838ce699af_2: + description: "预览文件" + element_type: a + section: "main::main" + strategy: label_text + value: "登录页背景" + playwright: "page.getByText('登录页背景', { exact: true })" + + el_e58cbbe999a26c6f676f: + description: "医院logo" + element_type: a + section: "main::main" + strategy: label_text + value: "医院logo" + playwright: "page.getByText('医院logo', { exact: true })" + + el_e58cbbe999a26c6f676f_2: + description: "预览文件" + element_type: a + section: "main::main" + strategy: label_text + value: "医院logo" + playwright: "page.getByText('医院logo', { exact: true })" + + saveBtn: + description: "保 存" + element_type: button + section: "main::main" + strategy: role + role: button + name: "保 存" + playwright: "page.getByRole('button', { name: '保 存' })" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::main" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + el_e88194e7b3bbe4babaInput: + description: "20260701测试1" + element_type: text + section: "main::main" + strategy: id + value: "form_item_linkman" + playwright: "page.locator('#form_item_linkman')" + + el_e88194e7b3bbe696b9e5bc8fInput: + description: "10000000005" + element_type: text + section: "main::main" + strategy: id + value: "form_item_contactWay" + playwright: "page.locator('#form_item_contactWay')" + + el_e58cbbe999a2e7ae80e7a7b0Input: + description: "邹城市人民医院" + element_type: text + section: "main::main" + strategy: id + value: "form_item_anotherName" + playwright: "page.locator('#form_item_anotherName')" diff --git a/locators/spd/basic-notice.locator.ts b/locators/spd/basic-notice.locator.ts new file mode 100644 index 0000000..53e427e --- /dev/null +++ b/locators/spd/basic-notice.locator.ts @@ -0,0 +1,25 @@ +import type { Locator, Page } from '@playwright/test'; + +import { BasicSharedLocators } from './basic-shared.locator'; + +/** 公告管理。选择器来自 locators/spd/basic-notice.locator.yaml */ +export class BasicNoticeLocators extends BasicSharedLocators { + readonly noticeTitle: Locator; + readonly publishScopeSelect: Locator; + readonly searchBtn: Locator; + readonly resetBtn: Locator; + readonly addBtn: Locator; + readonly richTextEditor: Locator; + readonly modalTitleInput: Locator; + + constructor(page: Page) { + super(page); + this.noticeTitle = page.locator('#form_item_title'); + this.publishScopeSelect = page.locator('.ant-form-item').filter({ hasText: '发布范围' }).locator('.ant-select').first(); + this.searchBtn = page.getByRole('button', { name: /查\s*询/ }); + this.resetBtn = page.getByRole('button', { name: /重\s*置/ }); + this.addBtn = page.getByRole('button', { name: /新\s*增/ }); + this.richTextEditor = page.locator('.ql-editor').first(); + this.modalTitleInput = this.dialog.locator('#form_item_title'); + } +} diff --git a/locators/spd/basic-notice.locator.yaml b/locators/spd/basic-notice.locator.yaml new file mode 100644 index 0000000..d9686b8 --- /dev/null +++ b/locators/spd/basic-notice.locator.yaml @@ -0,0 +1,549 @@ +# Auto-generated from basic-notice-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/hospital/paltform/notice +# Scanned: 2026-10-20T08:38:11.083Z +# Page: 基础设置 > 医院资料 > 公告管理 +# Note: filter dropdown was opened during scan +# Note: dialog/modal was opened during scan +# Note: edit dialog was opened during scan + +page: basic-notice +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/hospital/paltform/notice + +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_hospitalInfoRegister: + description: "医院信息登记" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "医院信息登记" + playwright: "page.getByRole('menuitem', { name: '医院信息登记' })" + + menu_branchManage: + description: "院区管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "院区管理" + playwright: "page.getByRole('menuitem', { name: '院区管理' })" + + menu_noticeManage: + description: "公告管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "公告管理" + playwright: "page.getByRole('menuitem', { name: '公告管理' })" + + menu_financeMonthSetting: + description: "财务月份设置" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "财务月份设置" + playwright: "page.getByRole('menuitem', { name: '财务月份设置' })" + + noticeManage: + description: "公告管理" + element_type: tab + section: "main::main" + strategy: text + value: "公告管理" + playwright: "page.getByText('公告管理')" + + tab_noticeManage: + description: "公告管理" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/hospital/paltform/notice" + playwright: "page.locator('#rc-tabs-0-tab-/basic/hospital/paltform/notice')" + + noticeTitle: + description: "公告标题" + element_type: text + section: "main::main" + strategy: id + value: "form_item_title" + playwright: "page.locator('#form_item_title')" + + publishDateDateRange: + description: "发布日期" + element_type: datepicker + section: "main::main" + strategy: label_text + value: "发布日期" + playwright: "page.getByText('发布日期', { exact: true })" + + publishDateInput: + description: "开始日期" + element_type: text + section: "main::main" + strategy: id + value: "form_item_date" + playwright: "page.locator('#form_item_date')" + + publishDateInput_2: + description: "结束日期" + element_type: text + section: "main::main" + strategy: placeholder + value: "结束日期" + playwright: "page.getByPlaceholder('结束日期')" + + publishScopeSelect: + 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: '重 置' })" + + addBtn: + description: "新 增" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新 增" + playwright: "page.getByRole('button', { name: '新 增' })" + + tableDensity: + description: "column-height" + element_type: span + section: "main::main" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + editBtn: + description: "编辑" + element_type: button + section: "main::main" + strategy: role + role: button + name: "编辑" + playwright: "page.getByRole('button', { name: '编辑' })" + + el_e588a0e999a4Btn: + description: "删除" + element_type: button + section: "main::main" + strategy: role + role: button + name: "删除" + playwright: "page.getByRole('button', { name: '删除' })" + + # --- main::pagination --- + pagination_el_e585b13635e69da131323530e69da12fe9a1b5e8b7b3e887b3e9a1b5: + description: "共 65 条1250 条/页跳至页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 65 条1250 条/页跳至页" + playwright: "page.getByText('共 65 条1250 条/页跳至页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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_page_2: + description: "2" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "2" + playwright: "page.getByText('2')" + + pagination_nextPageBtn: + description: "下一页" + element_type: li + section: "main::pagination" + strategy: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + # --- main::modal:新增公告 --- + noticeTitle_2: + description: "请输入" + element_type: text + section: "main::modal:新增公告" + strategy: id + value: "form_item_title" + playwright: "page.locator('#form_item_title')" + + el_e585a8e983a8: + description: "全部" + element_type: radio + section: "main::modal:新增公告" + strategy: text + value: "全部" + playwright: "page.getByText('全部')" + + el_e585a8e999a2: + description: "全院" + element_type: radio + section: "main::modal:新增公告" + strategy: text + value: "全院" + playwright: "page.getByText('全院')" + + el_e4b8ade5bf83e5ba93: + description: "中心库" + element_type: radio + section: "main::modal:新增公告" + strategy: text + value: "中心库" + playwright: "page.getByText('中心库')" + + el_e99480e594aee59586: + description: "销售商" + element_type: radio + section: "main::modal:新增公告" + strategy: text + value: "销售商" + playwright: "page.getByText('销售商')" + + el_e698af: + description: "是" + element_type: radio + section: "main::modal:新增公告" + strategy: text + value: "是" + playwright: "page.getByText('是')" + + el_e590a6: + description: "否" + element_type: radio + section: "main::modal:新增公告" + strategy: text + value: "否" + playwright: "page.getByText('否')" + + expireDateDateRange: + description: "截止日期" + element_type: datepicker + section: "main::modal:新增公告" + strategy: label_text + value: "截止日期" + playwright: "page.getByText('截止日期', { exact: true })" + + expireDate: + description: "请选择日期" + element_type: text + section: "main::modal:新增公告" + strategy: id + value: "form_item_expireDate" + playwright: "page.locator('#form_item_expireDate')" + + contentBtn: + description: "内容" + element_type: button + section: "main::modal:新增公告" + strategy: label_text + value: "内容" + playwright: "page.getByText('内容', { exact: true })" + + content: + description: "内容" + element_type: span + section: "main::modal:新增公告" + strategy: role + role: button + name: "内容" + playwright: "page.getByRole('button', { name: '内容' })" + + content_2: + description: "内容" + element_type: div + section: "main::modal:新增公告" + strategy: label_text + value: "内容" + playwright: "page.getByText('内容', { exact: true })" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:新增公告" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + el_e58f91e5b883Btn: + description: "发 布" + element_type: button + section: "main::modal:新增公告" + strategy: role + role: button + name: "发 布" + playwright: "page.getByRole('button', { name: '发 布' })" + + # --- main::modal --- + confirmBtn: + description: "确 定" + element_type: button + section: "main::modal" + strategy: role + role: button + name: "确 定" + playwright: "page.getByRole('button', { name: '确 定' })" + + # --- main::modal:编辑公告 --- + noticeTitle_3: + description: "请输入" + element_type: text + section: "main::modal:编辑公告" + strategy: id + value: "form_item_title" + playwright: "page.locator('#form_item_title')" + + el_e585a8e983a8_2: + description: "全部" + element_type: radio + section: "main::modal:编辑公告" + strategy: text + value: "全部" + playwright: "page.getByText('全部')" + + el_e585a8e999a2_2: + description: "全院" + element_type: radio + section: "main::modal:编辑公告" + strategy: text + value: "全院" + playwright: "page.getByText('全院')" + + el_e4b8ade5bf83e5ba93_2: + description: "中心库" + element_type: radio + section: "main::modal:编辑公告" + strategy: text + value: "中心库" + playwright: "page.getByText('中心库')" + + el_e99480e594aee59586_2: + description: "销售商" + element_type: radio + section: "main::modal:编辑公告" + strategy: text + value: "销售商" + playwright: "page.getByText('销售商')" + + el_e698af_2: + description: "是" + element_type: radio + section: "main::modal:编辑公告" + strategy: text + value: "是" + playwright: "page.getByText('是')" + + el_e590a6_2: + description: "否" + element_type: radio + section: "main::modal:编辑公告" + strategy: text + value: "否" + playwright: "page.getByText('否')" + + expireDateDateRange_2: + description: "截止日期" + element_type: datepicker + section: "main::modal:编辑公告" + strategy: label_text + value: "截止日期" + playwright: "page.getByText('截止日期', { exact: true })" + + expireDate_2: + description: "2026-10-20" + element_type: text + section: "main::modal:编辑公告" + strategy: id + value: "form_item_expireDate" + playwright: "page.locator('#form_item_expireDate')" + + content_3: + description: "内容" + element_type: span + section: "main::modal:编辑公告" + strategy: role + role: button + name: "内容" + playwright: "page.getByRole('button', { name: '内容' })" + + content_4: + description: "存在与平台价格不一致的产品,请注意执行调价!" + element_type: div + section: "main::modal:编辑公告" + strategy: label_text + value: "内容" + playwright: "page.getByText('内容', { exact: true })" diff --git a/locators/spd/basic-role.locator.ts b/locators/spd/basic-role.locator.ts new file mode 100644 index 0000000..bd331a4 --- /dev/null +++ b/locators/spd/basic-role.locator.ts @@ -0,0 +1,36 @@ +import type { Locator, Page } from '@playwright/test'; + +import { BasicUserSharedLocators } from './basic-user-shared.locator'; + +/** 角色管理。选择器来自 locators/spd/basic-role.locator.yaml */ +export class BasicRoleLocators extends BasicUserSharedLocators { + readonly saveBtn: Locator; + readonly refreshBtn: Locator; + readonly addRoleBtn: Locator; + readonly roleSearch: Locator; + readonly roleTree: Locator; + + constructor(page: Page) { + super(page); + this.saveBtn = page.getByRole('button', { name: /保\s*存/ }); + this.refreshBtn = page.getByRole('button', { name: /刷\s*新/ }); + this.addRoleBtn = page.getByRole('button', { name: /^角\s*色$/ }); + this.roleSearch = page + .getByPlaceholder(/角色/) + .or(page.locator('.ant-layout-sider input, .ant-layout-sider .ant-input')) + .first(); + this.roleTree = page.locator('.menu-role-tree').first(); + } + + treeNode(title: string): Locator { + return this.roleTree.locator('.ant-tree-title').filter({ hasText: new RegExp(`^\\s*${title}\\s*$`) }); + } + + treeCheckbox(title: string): Locator { + return this.roleTree + .locator('.ant-tree-treenode') + .filter({ has: this.page.locator('.ant-tree-title').filter({ hasText: new RegExp(`^\\s*${title}\\s*$`) }) }) + .locator('.ant-tree-checkbox') + .first(); + } +} diff --git a/locators/spd/basic-role.locator.yaml b/locators/spd/basic-role.locator.yaml new file mode 100644 index 0000000..75680db --- /dev/null +++ b/locators/spd/basic-role.locator.yaml @@ -0,0 +1,5001 @@ +# Auto-generated from basic-role-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/user/platform/role +# Scanned: 2026-10-20T08:31:14.084Z +# Page: 基础设置 > 用户管理 > 角色管理 + +page: basic-role +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/user/platform/role + +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_roleManage: + description: "角色管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "角色管理" + playwright: "page.getByRole('menuitem', { name: '角色管理' })" + + menu_userManage: + description: "用户管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "用户管理" + playwright: "page.getByRole('menuitem', { name: '用户管理' })" + + menu_unionUserManage: + description: "医共体用户管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "医共体用户管理" + playwright: "page.getByRole('menuitem', { name: '医共体用户管理' })" + + roleManage: + description: "角色管理" + element_type: tab + section: "main::main" + strategy: text + value: "角色管理" + playwright: "page.getByText('角色管理')" + + tab_roleManage: + description: "角色管理" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/user/platform/role" + playwright: "page.locator('#rc-tabs-0-tab-/basic/user/platform/role')" + + el_e8a792e889b2Btn: + description: "角色" + element_type: button + section: "main::main" + strategy: role + role: button + name: "角色" + playwright: "page.getByRole('button', { name: '角色' })" + + saveBtn: + description: "保 存" + element_type: button + section: "main::main" + strategy: role + role: button + name: "保 存" + playwright: "page.getByRole('button', { name: '保 存' })" + + el_e588b7e696b0Btn: + description: "刷 新" + element_type: button + section: "main::main" + strategy: role + role: button + name: "刷 新" + playwright: "page.getByRole('button', { name: '刷 新' })" + + el_535044e58685e7bd91e7b3bbe7bb9f5043e7abaf: + description: "SPD内网系统PC端" + element_type: checkbox + section: "main::main" + strategy: text + value: "SPD内网系统PC端" + playwright: "page.getByText('SPD内网系统PC端')" + + tree_node_basicSetting_e59fbae7a180: + description: "基础设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "基础设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*基础设置\\s*$/ })" + + tree_node_hospitalInfo_e58cbbe999a2: + description: "医院资料" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "医院资料" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*医院资料\\s*$/ })" + + tree_node_hospitalInfoRegister_e58cbbe999a2: + description: "医院信息登记" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "医院信息登记" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*医院信息登记\\s*$/ })" + + tree_node_branchManage_e999a2e58cba: + description: "院区管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "院区管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*院区管理\\s*$/ })" + + tree_node_noticeManage_e585ace5918a: + description: "公告管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "公告管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*公告管理\\s*$/ })" + + tree_node_financeMonthSetting_e8b4a2e58aa1: + description: "财务月份设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "财务月份设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*财务月份设置\\s*$/ })" + + tree_node_e8bf90e7bbb4: + description: "运维管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "运维管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*运维管理\\s*$/ })" + + tree_node_e88f9ce58d95: + description: "菜单控制规则设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "菜单控制规则设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*菜单控制规则设置\\s*$/ })" + + tree_node_e58cbbe999a2: + description: "医院配置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "医院配置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*医院配置\\s*$/ })" + + tree_el_e88f9ce58d95e58f97e68ea7e58897e8a1a8_2: + description: "菜单受控列表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "菜单受控列表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*菜单受控列表\\s*$/ })" + + tree_node_e999a2e58cba: + description: "院区配置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "院区配置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*院区配置\\s*$/ })" + + tree_node_e7a791e5aea4: + description: "科室配置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室配置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室配置\\s*$/ })" + + tree_node_e69cafe8afad: + description: "术语配置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "术语配置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*术语配置\\s*$/ })" + + tree_node_e8b584e6ba90: + description: "资源模板设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "资源模板设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*资源模板设置\\s*$/ })" + + tree_node_e5ad97e585b8: + description: "字典管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "字典管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*字典管理\\s*$/ })" + + tree_node_e68993e58db0: + description: "打印机配置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "打印机配置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*打印机配置\\s*$/ })" + + tree_node_e7bc96e7a081: + description: "编码规则管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "编码规则管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*编码规则管理\\s*$/ })" + + tree_node_504441e68e88: + description: "PDA授权" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "PDA授权" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*PDA授权\\s*$/ })" + + tree_node_504441e78988: + description: "PDA版本管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "PDA版本管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*PDA版本管理\\s*$/ })" + + tree_node_e7bc93e5ad98: + description: "缓存监控" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "缓存监控" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*缓存监控\\s*$/ })" + + tree_node_e6938de4bd9c: + description: "操作日志" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "操作日志" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*操作日志\\s*$/ })" + + tree_node_e6b688e681af: + description: "消息通知模板" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "消息通知模板" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*消息通知模板\\s*$/ })" + + tree_el_e6b688e681afe9809ae79fa5e8aebee7bdae_2: + description: "消息通知设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "消息通知设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*消息通知设置\\s*$/ })" + + tree_node_e58a9fe883bd: + description: "功能注册" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "功能注册" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*功能注册\\s*$/ })" + + tree_node_e4bbbbe58aa1: + description: "任务执行记录查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "任务执行记录查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*任务执行记录查询\\s*$/ })" + + tree_node_vendorManage_e58e82e59586: + description: "厂商管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "厂商管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*厂商管理\\s*$/ })" + + tree_node_vendorQuery_e58e82e59586: + description: "厂商查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "厂商查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*厂商查询\\s*$/ })" + + tree_node_vendorQual_e4be9be5ba94: + description: "供应商资质管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "供应商资质管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*供应商资质管理\\s*$/ })" + + tree_node_authFirst_e68e88e69d83: + description: "授权变更初审" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "授权变更初审" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*授权变更初审\\s*$/ })" + + tree_node_certFirst_e6b3a8e5868c: + description: "注册证变更初审" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "注册证变更初审" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*注册证变更初审\\s*$/ })" + + tree_node_certFinal_e6b3a8e5868c: + description: "注册证变更终审" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "注册证变更终审" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*注册证变更终审\\s*$/ })" + + tree_node_authFinal_e68e88e69d83: + description: "授权变更终审" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "授权变更终审" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*授权变更终审\\s*$/ })" + + tree_node_certDiff_e6b3a8e5868c: + description: "注册证差异对比" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "注册证差异对比" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*注册证差异对比\\s*$/ })" + + tree_node_evalScheme_e8af84e4bbb7: + description: "评价方案维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "评价方案维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*评价方案维护\\s*$/ })" + + tree_node_evalIndicator_e8af84e4bbb7: + description: "评价指标维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "评价指标维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*评价指标维护\\s*$/ })" + + tree_node_vendorHoliday_e4be9be5ba94: + description: "供应商节假日管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "供应商节假日管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*供应商节假日管理\\s*$/ })" + + tree_node_e4baa7e59381: + description: "产品管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品管理\\s*$/ })" + + tree_el_e4baa7e59381e6a0b8e5afb9e9878de696b0e58f91e8b5b7_2: + description: "产品核对重新发起" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品核对重新发起" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品核对重新发起\\s*$/ })" + + tree_el_e4baa7e59381e7bbb4e68aa4_2: + description: "产品维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品维护\\s*$/ })" + + tree_node_e789a9e69699: + description: "物料信息管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "物料信息管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*物料信息管理\\s*$/ })" + + tree_el_e4baa7e59381e5889de5aea1e6a0b8e5afb9_2: + description: "产品初审核对" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品初审核对" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品初审核对\\s*$/ })" + + tree_el_e4baa7e59381e4be9be5ba94e585b3e7b3bbe7bbb4e68aa4_2: + description: "产品供应关系维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品供应关系维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品供应关系维护\\s*$/ })" + + tree_el_e4baa7e59381e789a9e4bbb7e6a0b8e5afb9_2: + description: "产品物价核对" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品物价核对" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品物价核对\\s*$/ })" + + tree_el_e999a2e58cbae4baa7e59381e8aebee7bdae_2: + description: "院区产品设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "院区产品设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*院区产品设置\\s*$/ })" + + tree_el_e999a2e58cbae58d95e58583e58c85e8aebee7bdae_2: + description: "院区单元包设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "院区单元包设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*院区单元包设置\\s*$/ })" + + tree_node_e4b8ade5bf83: + description: "中心库上下限建议(AI)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库上下限建议(AI)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库上下限建议(AI)\\s*$/ })" + + tree_node_e8afb7e9a286: + description: "请领出库渠道设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领出库渠道设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领出库渠道设置\\s*$/ })" + + tree_el_e4b8ade5bf83e5ba93e4b88ae4b88be99990e5bbbae8aeae_2: + description: "中心库上下限建议" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库上下限建议" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库上下限建议\\s*$/ })" + + tree_node_e7bb93e7ae97: + description: "结算点设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结算点设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*结算点设置\\s*$/ })" + + tree_node_e8b083e4bbb7: + description: "调价管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "调价管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*调价管理\\s*$/ })" + + tree_el_e4baa7e59381e4bfa1e681afe58f98e69bb4_2: + description: "产品信息变更" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品信息变更" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品信息变更\\s*$/ })" + + tree_node_e58f98e69bb4: + description: "变更单明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "变更单明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*变更单明细查询\\s*$/ })" + + tree_node_e5a496e59bb4: + description: "外围系统产品新增" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "外围系统产品新增" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*外围系统产品新增\\s*$/ })" + + tree_el_e5a496e59bb4e7b3bbe7bb9fe4baa7e59381e58f98e69bb4e7aea1e79086_2: + description: "外围系统产品变更管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "外围系统产品变更管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*外围系统产品变更管理\\s*$/ })" + + tree_el_e4baa7e593814449e69fa5e8afa2_2: + description: "产品DI查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品DI查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品DI查询\\s*$/ })" + + tree_node_e7bcbae8b4a7: + description: "缺货目录维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "缺货目录维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*缺货目录维护\\s*$/ })" + + tree_el_e4b8ade5bf83e5ba93e5ba93e58cbae4baa7e59381e8aebee7bdae_2: + description: "中心库库区产品设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库库区产品设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库库区产品设置\\s*$/ })" + + tree_el_e7bcbae8b4a7e79baee5bd95e6988ee7bb86_2: + description: "缺货目录明细" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "缺货目录明细" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*缺货目录明细\\s*$/ })" + + tree_el_e789a9e69699e4bfa1e681afe58f98e69bb4_2: + description: "物料信息变更" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "物料信息变更" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*物料信息变更\\s*$/ })" + + tree_el_e4baa7e593814449e58f98e69bb4_2: + description: "产品DI变更" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品DI变更" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品DI变更\\s*$/ })" + + tree_el_e4baa7e59381e8b584e8b4a8e9bd90e585a8e8a784e58899_2: + description: "产品资质齐全规则" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品资质齐全规则" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品资质齐全规则\\s*$/ })" + + tree_el_e4baa7e59381e8b584e8b4a8e4b880e8a788_2: + description: "产品资质一览" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品资质一览" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品资质一览\\s*$/ })" + + tree_el_e789a9e69699e58f98e69bb4e58d95e6988ee7bb86e69fa5e8afa2_2: + description: "物料变更单明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "物料变更单明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*物料变更单明细查询\\s*$/ })" + + tree_el_e7a791e5aea4e7aea1e79086_2: + description: "科室管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室管理\\s*$/ })" + + tree_el_e7a791e5aea4e799bbe8aeb0_2: + description: "科室登记" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室登记" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室登记\\s*$/ })" + + tree_el_e7a791e5aea4e4baa7e59381e58886e9858d_2: + description: "科室产品分配" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室产品分配" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室产品分配\\s*$/ })" + + tree_el_e7a791e5aea4e5ba93e58cbae4baa7e59381e8aebee7bdae_2: + description: "科室库区产品设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室库区产品设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室库区产品设置\\s*$/ })" + + tree_el_e7a791e5aea4e8b79fe58fb0e99990e9a29de4bbbbe58aa1_2: + description: "科室跟台限额任务" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室跟台限额任务" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室跟台限额任务\\s*$/ })" + + tree_el_e8afb7e9a286e6a8a1e69dbfe7bbb4e68aa4_2: + description: "请领模板维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领模板维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领模板维护\\s*$/ })" + + tree_node_e887aae58aa8: + description: "自动请领排程" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "自动请领排程" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*自动请领排程\\s*$/ })" + + tree_el_e7a791e5aea4e4babae59198e69fa5e8afa2_2: + description: "科室人员查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室人员查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室人员查询\\s*$/ })" + + tree_el_e7a791e5aea4e4b88ae4b88be99990e5bbbae8aeae_2: + description: "科室上下限建议" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室上下限建议" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室上下限建议\\s*$/ })" + + tree_el_e7a791e5aea4e4b88ae4b88be99990e5bbbae8aeaeefbc884149efbc89_2: + description: "科室上下限建议(AI)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室上下限建议(AI)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室上下限建议(AI)\\s*$/ })" + + tree_el_e7a791e5aea4e9a284e7ae97e7aea1e79086_2: + description: "科室预算管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室预算管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室预算管理\\s*$/ })" + + tree_node_e6a380e9aa8c: + description: "检验机器管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "检验机器管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*检验机器管理\\s*$/ })" + + tree_el_e7a791e5aea4e98081e8b4a7e8aea1e58892_2: + description: "科室送货计划" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室送货计划" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室送货计划\\s*$/ })" + + tree_el_e7a791e5aea4e58886e7b1bbe7bbb4e68aa4_2: + description: "科室分类维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室分类维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室分类维护\\s*$/ })" + + tree_el_e7a791e5aea4e4baa7e593814149e9a284e6b58be7bb93e8aeba_2: + description: "科室产品AI预测结论" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室产品AI预测结论" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室产品AI预测结论\\s*$/ })" + + tree_node_e5ba93e688bf: + description: "库房管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库房管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库房管理\\s*$/ })" + + tree_node_e5ba93e58cba: + description: "库区设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库区设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库区设置\\s*$/ })" + + tree_node_e68ea2e99288: + description: "探针绑定" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "探针绑定" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*探针绑定\\s*$/ })" + + tree_node_e58d8ee6b885: + description: "华清通道" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "华清通道" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*华清通道\\s*$/ })" + + tree_node_userManage_e794a8e688b7: + description: "用户管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "用户管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*用户管理\\s*$/ })" + + tree_node_roleManage_e8a792e889b2: + description: "角色管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "角色管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*角色管理\\s*$/ })" + + tree_userManage_2: + description: "用户管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "用户管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*用户管理\\s*$/ })" + + tree_node_unionUserManage_e58cbbe585b1: + description: "医共体用户管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "医共体用户管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*医共体用户管理\\s*$/ })" + + tree_node_e6b581e7a88b: + description: "流程设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "流程设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*流程设置\\s*$/ })" + + tree_node_e5aea1e689b9: + description: "审批流程设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "审批流程设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*审批流程设置\\s*$/ })" + + tree_el_e5aea1e689b9e6b581e5ae9ee4be8b_2: + description: "审批流实例" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "审批流实例" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*审批流实例\\s*$/ })" + + tree_node_e68ea5e58fa3: + description: "接口管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "接口管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*接口管理\\s*$/ })" + + tree_el_e68ea5e58fa3e697a5e5bf97_2: + description: "接口日志" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "接口日志" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*接口日志\\s*$/ })" + + tree_node_e68ea8e98081: + description: "推送物价" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "推送物价" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*推送物价\\s*$/ })" + + tree_node_e694b6e8b4b9: + description: "收费项目对码" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "收费项目对码" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*收费项目对码\\s*$/ })" + + tree_el_e5a496e59bb4e4baa7e59381e58f98e69bb4_2: + description: "外围产品变更" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "外围产品变更" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*外围产品变更\\s*$/ })" + + tree_node_484953e4bbb7: + description: "HIS价对照查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "HIS价对照查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*HIS价对照查询\\s*$/ })" + + tree_node_e5a496e983a8: + description: "外部变更明细信息查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "外部变更明细信息查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*外部变更明细信息查询\\s*$/ })" + + tree_node_e58cbbe4bf9d: + description: "医保信息核对" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "医保信息核对" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*医保信息核对\\s*$/ })" + + tree_node_e58e82e59586: + description: "厂商对码" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "厂商对码" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*厂商对码\\s*$/ })" + + tree_node_e4bba3e4b8ba: + description: "代为科室请领(已确认)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "代为科室请领(已确认)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*代为科室请领(已确认)\\s*$/ })" + + tree_node_e4bba3e4ba8c: + description: "代二级科室请领产品" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "代二级科室请领产品" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*代二级科室请领产品\\s*$/ })" + + tree_node_484953e4baa7: + description: "HIS产品信息变更" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "HIS产品信息变更" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*HIS产品信息变更\\s*$/ })" + + tree_node_484953e7a791: + description: "HIS科室查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "HIS科室查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*HIS科室查询\\s*$/ })" + + tree_node_484953e4baba: + description: "HIS人员查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "HIS人员查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*HIS人员查询\\s*$/ })" + + tree_node_484953e694b6: + description: "HIS收费项与产品对照关系查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "HIS收费项与产品对照关系查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*HIS收费项与产品对照关系查询\\s*$/ })" + + tree_el_e999a2e58cbae5afb9e7a081_2: + description: "院区对码" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "院区对码" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*院区对码\\s*$/ })" + + tree_el_e999a2e58cbae5afb9e7a081_3: + description: "院区对码" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "院区对码" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*院区对码\\s*$/ })" + + tree_el_e999a2e58cbae5afb9e7a081_4: + description: "院区对码" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "院区对码" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*院区对码\\s*$/ })" + + tree_node_e4bd8ee580bc: + description: "低值计费接口查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "低值计费接口查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*低值计费接口查询\\s*$/ })" + + tree_node_e699bae883bd: + description: "智能柜与库区对码" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "智能柜与库区对码" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*智能柜与库区对码\\s*$/ })" + + tree_node_e789a9e8b584: + description: "物资字典推送记录" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "物资字典推送记录" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*物资字典推送记录\\s*$/ })" + + tree_node_e5b9b3e58fb0: + description: "平台产品价格差异查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "平台产品价格差异查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*平台产品价格差异查询\\s*$/ })" + + tree_el_e8afb7e9a286e58d95e68ea5e58fa3e69fa5e8afa2_2: + description: "请领单接口查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领单接口查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领单接口查询\\s*$/ })" + + tree_node_surgerySetting_e6898be69caf: + description: "手术套包设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术套包设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术套包设置\\s*$/ })" + + tree_node_basePkgMaintenance_e59fbae7a180: + description: "基础包维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "基础包维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*基础包维护\\s*$/ })" + + tree_node_operTypeMaintenance_e69cafe7a78d: + description: "术种维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "术种维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*术种维护\\s*$/ })" + + tree_node_stdPkgMaintenance_e6a087e58786: + description: "标准套包维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "标准套包维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*标准套包维护\\s*$/ })" + + tree_node_admitManage_e58786e585a5: + description: "准入管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "准入管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*准入管理\\s*$/ })" + + tree_node_admitMaterial_e789a9e69699: + description: "物料准入管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "物料准入管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*物料准入管理\\s*$/ })" + + tree_node_admitProductApply_e4baa7e59381: + description: "产品准入申请" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品准入申请" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品准入申请\\s*$/ })" + + tree_node_admitMaterialList_e789a9e69699: + description: "物料准入单列表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "物料准入单列表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*物料准入单列表\\s*$/ })" + + tree_node_admitProductList_e4baa7e59381: + description: "产品准入单列表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品准入单列表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品准入单列表\\s*$/ })" + + tree_node_admitSupply_e4be9be8b4a7: + description: "供货目录申请" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "供货目录申请" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*供货目录申请\\s*$/ })" + + tree_node_admitTask_e58786e585a5: + description: "准入任务分配" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "准入任务分配" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*准入任务分配\\s*$/ })" + + tree_node_purchase_e98787e8b4ad: + description: "采购管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购管理\\s*$/ })" + + tree_node_e98787e8b4ad: + description: "采购计划" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购计划" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购计划\\s*$/ })" + + tree_node_e99990e9878f: + description: "限量任务" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "限量任务" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*限量任务\\s*$/ })" + + tree_el_e98787e8b4ade58d95e7aea1e79086_2: + description: "采购单管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购单管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购单管理\\s*$/ })" + + tree_el_e98787e8b4ade8aea1e58892e6988ee7bb86_2: + description: "采购计划明细" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购计划明细" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购计划明细\\s*$/ })" + + tree_el_e8afb7e9a286e5aea1e6a0b8_2: + description: "请领审核" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领审核" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领审核\\s*$/ })" + + tree_node_e4b8b4e98787: + description: "临采任务" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "临采任务" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*临采任务\\s*$/ })" + + tree_node_e5b8a6e9878f: + description: "带量任务管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "带量任务管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*带量任务管理\\s*$/ })" + + tree_el_e5b8a6e9878fe98787e8b4ade79baee5bd95_2: + description: "带量采购目录" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "带量采购目录" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*带量采购目录\\s*$/ })" + + tree_node_e69c88e5baa6: + description: "月度备货计划" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "月度备货计划" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*月度备货计划\\s*$/ })" + + tree_node_e7a791e7a094: + description: "科研项目设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科研项目设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科研项目设置\\s*$/ })" + + tree_node_e6898be69caf: + description: "手术通知单" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术通知单" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术通知单\\s*$/ })" + + tree_node_e79c81e98787: + description: "省采订单" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "省采订单" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*省采订单\\s*$/ })" + + tree_node_e6b19fe88b8f: + description: "江苏省采" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "江苏省采" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*江苏省采\\s*$/ })" + + tree_node_e7bb8fe8b4b9: + description: "经费来源类型" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "经费来源类型" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*经费来源类型\\s*$/ })" + + tree_el_e7bcbae8b4a7e58f8de9a688_2: + description: "缺货反馈" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "缺货反馈" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*缺货反馈\\s*$/ })" + + tree_node_e68d90e8b5a0: + description: "捐赠活动设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "捐赠活动设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*捐赠活动设置\\s*$/ })" + + tree_node_e4be9be5ba94: + description: "供应商评价" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "供应商评价" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*供应商评价\\s*$/ })" + + tree_node_opening_e69c9fe5889d: + description: "期初管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "期初管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*期初管理\\s*$/ })" + + tree_node_e69c9fe5889d: + description: "期初录入" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "期初录入" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*期初录入\\s*$/ })" + + tree_el_e69c9fe5889de6988ee7bb86e69fa5e8afa2_2: + description: "期初明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "期初明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*期初明细查询\\s*$/ })" + + tree_node_e5bfabe9809f: + description: "快速盘点记录" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "快速盘点记录" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*快速盘点记录\\s*$/ })" + + tree_node_centerStock_e4b8ade5bf83: + description: "中心库管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库管理\\s*$/ })" + + tree_node_e585a5e5ba93: + description: "入库管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "入库管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*入库管理\\s*$/ })" + + tree_node_e9aa8ce694b6: + description: "验收管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "验收管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*验收管理\\s*$/ })" + + tree_el_e9aa8ce694b6e694b6e8b4a7_2: + description: "验收收货" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "验收收货" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*验收收货\\s*$/ })" + + tree_node_e4b88ae69eb6: + description: "上架入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "上架入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*上架入库\\s*$/ })" + + tree_el_e9aa8ce694b6e58fb0e8b4a6_2: + description: "验收台账" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "验收台账" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*验收台账\\s*$/ })" + + tree_el_e7a791e5aea4e9a9b3e59b9ee7a1aee8aea4_2: + description: "科室驳回确认" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室驳回确认" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室驳回确认\\s*$/ })" + + tree_node_e6898be5b7a5: + description: "手工入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手工入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手工入库\\s*$/ })" + + tree_node_e585b6e4bb96: + description: "其他物资入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "其他物资入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*其他物资入库\\s*$/ })" + + tree_node_e8af95e58982: + description: "试剂性能验证单" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "试剂性能验证单" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*试剂性能验证单\\s*$/ })" + + tree_node_e7b4a7e680a5: + description: "紧急使用登记" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "紧急使用登记" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*紧急使用登记\\s*$/ })" + + tree_node_e8b4b4e7a081: + description: "贴码核对" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "贴码核对" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*贴码核对\\s*$/ })" + + tree_el_e9aa8ce694b6e7a1aee8aea4_2: + description: "验收确认" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "验收确认" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*验收确认\\s*$/ })" + + tree_node_e68abde9aa8c: + description: "抽验记录查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "抽验记录查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*抽验记录查询\\s*$/ })" + + tree_el_e9aa8ce694b6e8b4b4e7a081e4bbbbe58aa1e58886e9858d_2: + description: "验收贴码任务分配" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "验收贴码任务分配" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*验收贴码任务分配\\s*$/ })" + + tree_node_e8b79fe58fb0: + description: "跟台耗材识别仪入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "跟台耗材识别仪入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*跟台耗材识别仪入库\\s*$/ })" + + tree_node_e587bae5ba93: + description: "出库管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库管理\\s*$/ })" + + tree_node_e7949fe68890: + description: "生成拣货单" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "生成拣货单" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*生成拣货单\\s*$/ })" + + tree_node_e68ba3e8b4a7: + description: "拣货执行" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "拣货执行" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*拣货执行\\s*$/ })" + + tree_el_e587bae5ba93e5a48de6a0b8_2: + description: "出库复核" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库复核" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库复核\\s*$/ })" + + tree_el_e6898be5b7a5e587bae5ba93_2: + description: "手工出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手工出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手工出库\\s*$/ })" + + tree_node_e68aa5e68d9f: + description: "报损出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "报损出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*报损出库\\s*$/ })" + + tree_node_e8b083e68ba8: + description: "调拨出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "调拨出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*调拨出库\\s*$/ })" + + tree_node_e7a7bbe5ba93: + description: "移库出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "移库出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*移库出库\\s*$/ })" + + tree_node_e98080e8b4a7: + description: "退货出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "退货出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*退货出库\\s*$/ })" + + tree_el_e68ba3e8b4a7e58d95e7aea1e79086_2: + description: "拣货单管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "拣货单管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*拣货单管理\\s*$/ })" + + tree_el_e587bae5ba93e58d95e58897e8a1a8_2: + description: "出库单列表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库单列表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库单列表\\s*$/ })" + + tree_el_e8b79fe58fb0e98080e8b4a7e587bae5ba93_2: + description: "跟台退货出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "跟台退货出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*跟台退货出库\\s*$/ })" + + tree_el_e98080e8b4a7e6988ee7bb86e69fa5e8afa2_2: + description: "退货明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "退货明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*退货明细查询\\s*$/ })" + + tree_el_e587bae5ba93e6988ee7bb86e69fa5e8afa2_2: + description: "出库明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库明细查询\\s*$/ })" + + tree_el_e68ba3e8b4a7e58886e9858de7aea1e79086_2: + description: "拣货分配管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "拣货分配管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*拣货分配管理\\s*$/ })" + + tree_el_e68ba3e8b4a7e6988ee7bb86e69fa5e8afa2_2: + description: "拣货明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "拣货明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*拣货明细查询\\s*$/ })" + + tree_el_e587bae5ba93e6a0b8e9aa8c_2: + description: "出库核验" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库核验" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库核验\\s*$/ })" + + tree_node_e59088e58d95: + description: "合单出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "合单出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*合单出库\\s*$/ })" + + tree_node_e8a385e7aeb1: + description: "装箱贴码" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "装箱贴码" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*装箱贴码\\s*$/ })" + + tree_el_e7a791e5aea4e59088e58d95e587bae5ba93_2: + description: "科室合单出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室合单出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室合单出库\\s*$/ })" + + tree_el_e59088e58d95e587bae5ba93e58d95_2: + description: "合单出库单" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "合单出库单" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*合单出库单\\s*$/ })" + + tree_node_e7aeb1e7a081: + description: "箱码打印记录" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "箱码打印记录" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*箱码打印记录\\s*$/ })" + + tree_node_e4baa4e68ea5: + description: "交接记录" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "交接记录" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*交接记录\\s*$/ })" + + tree_node_e68abde6a380: + description: "抽检记录查看" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "抽检记录查看" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*抽检记录查看\\s*$/ })" + + tree_el_e9aa8ce694b6e7a1aee8aea4_3: + description: "验收确认" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "验收确认" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*验收确认\\s*$/ })" + + tree_node_e5ba93e58685: + description: "库内作业" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库内作业" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库内作业\\s*$/ })" + + tree_node_e68993e7a081: + description: "打码中心" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "打码中心" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*打码中心\\s*$/ })" + + tree_node_e68891e8a681: + description: "我要加工" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "我要加工" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*我要加工\\s*$/ })" + + tree_node_e58aa0e5b7a5: + description: "加工执行" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "加工执行" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*加工执行\\s*$/ })" + + tree_el_e5bfabe9809fe58aa0e5b7a5_2: + description: "快速加工" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "快速加工" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*快速加工\\s*$/ })" + + tree_el_e5bfabe9809fe7bb84e58c85_2: + description: "快速组包" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "快速组包" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*快速组包\\s*$/ })" + + tree_node_e8b4a7e4bd8d: + description: "货位调整台账" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "货位调整台账" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*货位调整台账\\s*$/ })" + + tree_el_e58aa0e5b7a5e58d95e58897e8a1a8_2: + description: "加工单列表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "加工单列表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*加工单列表\\s*$/ })" + + tree_el_e8b4a7e4bd8de8b083e695b4_2: + description: "货位调整" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "货位调整" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*货位调整\\s*$/ })" + + tree_node_e68b86e99bb6: + description: "拆零加工" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "拆零加工" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*拆零加工\\s*$/ })" + + tree_el_e4b8ade5bf83e5ba93e5ba93e58cbae7a7bbe58aa8_2: + description: "中心库库区移动" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库库区移动" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库库区移动\\s*$/ })" + + tree_node_e5ba93e5ad98: + description: "库存查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存查询\\s*$/ })" + + tree_el_e5ba93e5ad98e6b187e680bbe69fa5e8afa2_2: + description: "库存汇总查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存汇总查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存汇总查询\\s*$/ })" + + tree_el_e5ba93e5ad98e6988ee7bb86e69fa5e8afa2_2: + description: "库存明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存明细查询\\s*$/ })" + + tree_node_e585a8e999a2: + description: "全院库存查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "全院库存查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*全院库存查询\\s*$/ })" + + tree_node_e4b88de889af: + description: "不良事件" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "不良事件" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*不良事件\\s*$/ })" + + tree_el_e4b88de889afe4ba8be4bbb6e7aea1e79086_2: + description: "不良事件管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "不良事件管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*不良事件管理\\s*$/ })" + + tree_el_e4b88de889afe4ba8be4bbb6e5aea1e689b9_2: + description: "不良事件审批" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "不良事件审批" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*不良事件审批\\s*$/ })" + + tree_node_e79b98e782b9: + description: "盘点管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "盘点管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*盘点管理\\s*$/ })" + + tree_el_e79b98e782b9e8aea1e58892e7aea1e79086_2: + description: "盘点计划管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "盘点计划管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*盘点计划管理\\s*$/ })" + + tree_el_e79b98e782b9e5bd95e585a5_2: + description: "盘点录入" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "盘点录入" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*盘点录入\\s*$/ })" + + tree_el_e79b98e782b9e7bb9fe8aea1_2: + description: "盘点统计" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "盘点统计" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*盘点统计\\s*$/ })" + + tree_el_e4b8ade5bf83e5ba93e5b7a5e4bd9ce58fb0_2: + description: "中心库工作台" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库工作台" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库工作台\\s*$/ })" + + tree_el_e4b8ade5bf83e5ba93e5b7a5e4bd9ce58fb0_3: + description: "中心库工作台" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库工作台" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库工作台\\s*$/ })" + + tree_el_e7a7bbe5ba93e794b3e8afb7e7aea1e79086_2: + description: "移库申请管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "移库申请管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*移库申请管理\\s*$/ })" + + tree_el_e7a7bbe5ba93e794b3e8afb7_2: + description: "移库申请" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "移库申请" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*移库申请\\s*$/ })" + + tree_el_e7a7bbe5ba93e794b3e8afb7e6988ee7bb86e69fa5e8afa2_2: + description: "移库申请明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "移库申请明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*移库申请明细查询\\s*$/ })" + + tree_el_e4b8ade5bf83e5ba93e7a7bbe5ba93e794b3e8afb7_2: + description: "中心库移库申请" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库移库申请" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库移库申请\\s*$/ })" + + tree_node_e585bbe68aa4: + description: "养护管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "养护管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*养护管理\\s*$/ })" + + tree_el_e585bbe68aa4e7aea1e79086_2: + description: "养护管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "养护管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*养护管理\\s*$/ })" + + tree_el_e699bae883bde98787e8b4ad_2: + description: "智能采购" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "智能采购" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*智能采购\\s*$/ })" + + tree_node_4149e98787e8: + description: "AI采购建议" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "AI采购建议" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*AI采购建议\\s*$/ })" + + tree_node_4149e9a284e6: + description: "AI预测分析比对-每日预测" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "AI预测分析比对-每日预测" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*AI预测分析比对-每日预测\\s*$/ })" + + tree_el_4149e9a284e6b58be58886e69e90e6af94e5afb92de697a5e59d87e9a284e6b58b_2: + description: "AI预测分析比对-日均预测" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "AI预测分析比对-日均预测" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*AI预测分析比对-日均预测\\s*$/ })" + + tree_node_deptBusiness_e7a791e5aea4: + description: "科室业务管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室业务管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室业务管理\\s*$/ })" + + tree_el_e8afb7e9a286e7aea1e79086_2: + description: "请领管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领管理\\s*$/ })" + + tree_el_e68891e8a681e8afb7e9a286_2: + description: "我要请领" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "我要请领" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*我要请领\\s*$/ })" + + tree_el_e7b4a7e680a5e8afb7e9a286_2: + description: "紧急请领" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "紧急请领" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*紧急请领\\s*$/ })" + + tree_node_e4bba3e7a791: + description: "代科室下单" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "代科室下单" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*代科室下单\\s*$/ })" + + tree_el_e8afb7e9a286e58d95e7a1aee8aea4_2: + description: "请领单确认" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领单确认" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领单确认\\s*$/ })" + + tree_el_e8afb7e9a286e58d95e69fa5e8afa2_2: + description: "请领单查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领单查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领单查询\\s*$/ })" + + tree_el_e8afb7e9a286e58d95e6988ee7bb86e69fa5e8afa2_2: + description: "请领单明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领单明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领单明细查询\\s*$/ })" + + tree_el_e8afb7e9a286e69caae5ae8ce68890e8bf9be5baa6e69fa5e8afa2_2: + description: "请领未完成进度查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领未完成进度查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领未完成进度查询\\s*$/ })" + + tree_el_e8b79fe58fb0e6898be69cafe794b3e8afb7_2: + description: "跟台手术申请" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "跟台手术申请" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*跟台手术申请\\s*$/ })" + + tree_el_e8b79fe58fb0e5a597e9a490e7bbb4e68aa4_2: + description: "跟台套餐维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "跟台套餐维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*跟台套餐维护\\s*$/ })" + + tree_node_e5a487e8b4a7: + description: "备货耗材包管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "备货耗材包管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*备货耗材包管理\\s*$/ })" + + tree_el_e585a5e5ba93e7aea1e79086_2: + description: "入库管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "入库管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*入库管理\\s*$/ })" + + tree_el_e7a791e5aea4e585a5e5ba93_2: + description: "科室入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室入库\\s*$/ })" + + tree_el_e585a5e5ba93e58d95e69fa5e8afa2_2: + description: "入库单查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "入库单查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*入库单查询\\s*$/ })" + + tree_node_e6b688e88097: + description: "消耗退回" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "消耗退回" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*消耗退回\\s*$/ })" + + tree_node_e98080e5ba93: + description: "退库核对" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "退库核对" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*退库核对\\s*$/ })" + + tree_el_e587bae5ba93e7aea1e79086_2: + description: "出库管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库管理\\s*$/ })" + + tree_el_e6898be5b7a5e6b688e88097_2: + description: "手工消耗" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手工消耗" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手工消耗\\s*$/ })" + + tree_el_e98080e5ba93e587bae5ba93_2: + description: "退库出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "退库出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*退库出库\\s*$/ })" + + tree_el_e587bae5ba93e58d95e69fa5e8afa2_2: + description: "出库单查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库单查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库单查询\\s*$/ })" + + tree_node_e9a286e794a8: + description: "领用出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "领用出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*领用出库\\s*$/ })" + + tree_el_e6898be5b7a5e9a286e794a8e587bae5ba93_2: + description: "手工领用出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手工领用出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手工领用出库\\s*$/ })" + + tree_el_e8b79fe58fb0e4bdbfe794a8e7a1aee8aea4_2: + description: "跟台使用确认" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "跟台使用确认" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*跟台使用确认\\s*$/ })" + + tree_node_e4b889e7baa7: + description: "三级库调拨" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "三级库调拨" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*三级库调拨\\s*$/ })" + + tree_node_e4ba8ce7baa7: + description: "二级库调拨" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "二级库调拨" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*二级库调拨\\s*$/ })" + + tree_el_e7a791e5aea4e98080e5ba9328e585ade999a229_2: + description: "科室退库(六院)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室退库(六院)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室退库(六院)\\s*$/ })" + + tree_node_e69da1e7a081: + description: "条码使用确认" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "条码使用确认" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*条码使用确认\\s*$/ })" + + tree_el_e5ba93e58685e7aea1e79086_2: + description: "库内管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库内管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库内管理\\s*$/ })" + + tree_node_e689abe7a081: + description: "扫码发放" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "扫码发放" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*扫码发放\\s*$/ })" + + tree_el_e5ba93e58cbae7a7bbe58aa8_2: + description: "库区移动" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库区移动" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库区移动\\s*$/ })" + + tree_node_e58f91e694be: + description: "发放单查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发放单查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发放单查询\\s*$/ })" + + tree_node_e58d95e793b6: + description: "单瓶条码补打" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "单瓶条码补打" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*单瓶条码补打\\s*$/ })" + + tree_node_e6b497e6b688: + description: "洗消管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "洗消管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*洗消管理\\s*$/ })" + + tree_el_e8b79fe58fb0e88097e69d90e5a48de9aa8c_2: + description: "跟台耗材复验" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "跟台耗材复验" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*跟台耗材复验\\s*$/ })" + + tree_el_e4b889e7baa7e5ba93e697a5e6b885e7aea1e79086_2: + description: "三级库日清管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "三级库日清管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*三级库日清管理\\s*$/ })" + + tree_el_e6898be5b7a5e6a0b8e99480e7bb93e7ae97_2: + description: "手工核销结算" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手工核销结算" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手工核销结算\\s*$/ })" + + tree_el_e8b79fe58fb0e69da1e7a081e68993e58db0_2: + description: "跟台条码打印" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "跟台条码打印" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*跟台条码打印\\s*$/ })" + + tree_el_e5ba93e5ad98e69fa5e8afa2_2: + description: "库存查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存查询\\s*$/ })" + + tree_el_e7a791e5aea4e5ba93e5ad98e69fa5e8afa2_2: + description: "科室库存查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室库存查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室库存查询\\s*$/ })" + + tree_el_e6898be69cafe5a597e58c85e7aea1e79086_2: + description: "手术套包管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术套包管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术套包管理\\s*$/ })" + + tree_node_e59fbae7a180: + description: "基础包加工" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "基础包加工" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*基础包加工\\s*$/ })" + + tree_el_e59fbae7a180e58c85e5ba93e5ad98e69fa5e8afa2_2: + description: "基础包库存查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "基础包库存查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*基础包库存查询\\s*$/ })" + + tree_el_e6898be69cafe68e92e7a88be7aea1e79086_2: + description: "手术排程管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术排程管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术排程管理\\s*$/ })" + + tree_el_e6898be69cafe5a597e58c85e794b3e8afb7_2: + description: "手术套包申请" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术套包申请" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术套包申请\\s*$/ })" + + tree_el_e6898be69cafe5a597e58c85e58aa0e5b7a5_2: + description: "手术套包加工" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术套包加工" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术套包加工\\s*$/ })" + + tree_el_e6898be69cafe5a597e58c85e585a5e5ba93_2: + description: "手术套包入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术套包入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术套包入库\\s*$/ })" + + tree_el_e6898be69cafe5a597e58c85e9a284e58aa0e5b7a5_2: + description: "手术套包预加工" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术套包预加工" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术套包预加工\\s*$/ })" + + tree_el_e6898be69cafe5a597e58c85e587bae5ba93_2: + description: "手术套包出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术套包出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术套包出库\\s*$/ })" + + tree_el_e6898be69cafe5a597e58c85e6b688e88097_2: + description: "手术套包消耗" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术套包消耗" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术套包消耗\\s*$/ })" + + tree_el_e6898be69cafe5a597e58c85e98080e5ba93_2: + description: "手术套包退库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术套包退库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术套包退库\\s*$/ })" + + tree_el_e6898be69cafe5a597e58c85e5ba93e5ad98e69fa5e8afa2_2: + description: "手术套包库存查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手术套包库存查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手术套包库存查询\\s*$/ })" + + tree_el_e79b98e782b9e7aea1e79086_2: + description: "盘点管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "盘点管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*盘点管理\\s*$/ })" + + tree_el_e79b98e782b9e8aea1e58892e7aea1e79086_3: + description: "盘点计划管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "盘点计划管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*盘点计划管理\\s*$/ })" + + tree_el_e79b98e782b9e5bd95e585a5_3: + description: "盘点录入" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "盘点录入" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*盘点录入\\s*$/ })" + + tree_node_e79b98e4ba8f: + description: "盘亏处理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "盘亏处理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*盘亏处理\\s*$/ })" + + tree_el_e79b98e782b9e7bb9fe8aea1_3: + description: "盘点统计" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "盘点统计" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*盘点统计\\s*$/ })" + + tree_el_e7a791e5aea4e5b7a5e4bd9ce58fb0_2: + description: "科室工作台" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室工作台" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室工作台\\s*$/ })" + + tree_el_e7a791e5aea4e5b7a5e4bd9ce58fb0_3: + description: "科室工作台" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室工作台" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室工作台\\s*$/ })" + + tree_el_e5a487e8b4a7e8aea1e58892_2: + description: "备货计划" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "备货计划" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*备货计划\\s*$/ })" + + tree_el_e7a791e5aea4e5a487e8b4a7e8aea1e58892e58897e8a1a8_2: + description: "科室备货计划列表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室备货计划列表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室备货计划列表\\s*$/ })" + + tree_el_e7a791e5aea4e5a487e8b4a7e8aea1e58892e69fa5e8afa2_2: + description: "科室备货计划查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室备货计划查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室备货计划查询\\s*$/ })" + + tree_el_e5a487e8b4a7e8aea1e58892e6b187e680bbe5aea1e689b9_2: + description: "备货计划汇总审批" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "备货计划汇总审批" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*备货计划汇总审批\\s*$/ })" + + tree_el_e7a791e5aea4e699bae883bde8a1a5e8b4a7_2: + description: "科室智能补货" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室智能补货" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室智能补货\\s*$/ })" + + tree_el_e5ba93e5ad98e699bae883bde7aea1e79086e9a9bee9a9b6e888b1_2: + description: "库存智能管理驾驶舱" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存智能管理驾驶舱" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存智能管理驾驶舱\\s*$/ })" + + tree_node_4149e8a1a5e8: + description: "AI补货建议" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "AI补货建议" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*AI补货建议\\s*$/ })" + + tree_el_4149e9a284e6b58be58886e69e90e6af94e5afb92de6af8fe697a5e9a284e6b58b_2: + description: "AI预测分析比对-每日预测" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "AI预测分析比对-每日预测" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*AI预测分析比对-每日预测\\s*$/ })" + + tree_el_4149e9a284e6b58be58886e69e90e6af94e5afb92de697a5e59d87e9a284e6b58b_3: + description: "AI预测分析比对-日均预测" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "AI预测分析比对-日均预测" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*AI预测分析比对-日均预测\\s*$/ })" + + tree_node_finance_e8b4a2e58aa1: + description: "财务管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "财务管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*财务管理\\s*$/ })" + + tree_el_e7bb93e7ae97e7aea1e79086_2: + description: "结算管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结算管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*结算管理\\s*$/ })" + + tree_node_e585a5e8b4a6: + description: "入账明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "入账明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*入账明细查询\\s*$/ })" + + tree_el_e7bb93e7ae97e58d95e5aea1e6a0b8_2: + description: "结算单审核" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结算单审核" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*结算单审核\\s*$/ })" + + tree_el_e7949fe68890e7bb93e7ae97e58d95_2: + description: "生成结算单" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "生成结算单" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*生成结算单\\s*$/ })" + + tree_el_e7bb93e7ae97e6988ee7bb86e7a791e5aea4e6a0b8e5afb9_2: + description: "结算明细科室核对" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结算明细科室核对" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*结算明细科室核对\\s*$/ })" + + tree_node_e58f91e7a5a8: + description: "发票核对" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发票核对" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发票核对\\s*$/ })" + + tree_el_e58f91e7a5a8e68f90e4baa4_2: + description: "发票提交" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发票提交" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发票提交\\s*$/ })" + + tree_el_e58f91e7a5a8e5bd92e6a1a3_2: + description: "发票归档" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发票归档" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发票归档\\s*$/ })" + + tree_el_e7949fe68890e4bb98e6acbee58d95_2: + description: "生成付款单" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "生成付款单" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*生成付款单\\s*$/ })" + + tree_el_e58f91e7a5a8e6988ee7bb86e7a791e5aea4e6a0b8e5afb9_2: + description: "发票明细科室核对" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发票明细科室核对" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发票明细科室核对\\s*$/ })" + + tree_el_e58f91e7a5a8e69fa5e8afa2_2: + description: "发票查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发票查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发票查询\\s*$/ })" + + tree_el_e7bb93e7ae97e5bc80e7a5a8e6988ee7bb86e69fa5e8afa2_2: + description: "结算开票明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结算开票明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*结算开票明细查询\\s*$/ })" + + tree_el_e58f91e7a5a8e5bd95e585a528e8b4a7e7a5a8e5908ce8a18c29_2: + description: "发票录入(货票同行)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发票录入(货票同行)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发票录入(货票同行)\\s*$/ })" + + tree_node_e68890e69cac: + description: "成本明细科室核对" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "成本明细科室核对" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*成本明细科室核对\\s*$/ })" + + tree_el_e68ea5e58fa3e58f91e7a5a8e7a1aee8aea4efbc88e8b4a7e7a5a8e5908ce8a18cefbc89_2: + description: "接口发票确认(货票同行)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "接口发票确认(货票同行)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*接口发票确认(货票同行)\\s*$/ })" + + tree_node_e7bb93e5ad98: + description: "结存管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结存管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*结存管理\\s*$/ })" + + tree_node_e8b4a2e58aa1: + description: "财务结存" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "财务结存" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*财务结存\\s*$/ })" + + tree_node_e789a9e6b581: + description: "物流结存" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "物流结存" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*物流结存\\s*$/ })" + + tree_node_orthoVolume_e9aaa8e7a791: + description: "骨科带量" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "骨科带量" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*骨科带量\\s*$/ })" + + tree_el_e59fbae7a180e695b0e68dae_2: + description: "基础数据" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "基础数据" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*基础数据\\s*$/ })" + + tree_el_e5b8a6e9878fe7b3bbe7bb9fe7aea1e79086_2: + description: "带量系统管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "带量系统管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*带量系统管理\\s*$/ })" + + tree_node_e9aaa8e7a791: + description: "骨科带量目录" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "骨科带量目录" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*骨科带量目录\\s*$/ })" + + tree_node_e695b0e68dae: + description: "数据上报" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "数据上报" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*数据上报\\s*$/ })" + + tree_node_e69caae6b885: + description: "未清洗数据" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "未清洗数据" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*未清洗数据\\s*$/ })" + + tree_node_e5be85e7a1ae: + description: "待确认数据" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "待确认数据" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*待确认数据\\s*$/ })" + + tree_node_e5be85e4b88a: + description: "待上报数据" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "待上报数据" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*待上报数据\\s*$/ })" + + tree_node_e5b7b2e4b88a: + description: "已上报数据" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "已上报数据" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*已上报数据\\s*$/ })" + + tree_el_e5b8a6e9878fe68aa5e8a1a8e58886e69e90_2: + description: "带量报表分析" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "带量报表分析" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*带量报表分析\\s*$/ })" + + tree_el_e9aaa8e7a791e5b8a6e9878fe5ae8ce68890e8bf9be5baa6_2: + description: "骨科带量完成进度" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "骨科带量完成进度" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*骨科带量完成进度\\s*$/ })" + + tree_node_general_e7bbbce59088: + description: "综合管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "综合管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*综合管理\\s*$/ })" + + tree_node_e8aea1e98080: + description: "计退费管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "计退费管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*计退费管理\\s*$/ })" + + tree_el_e6b688e88097e8aea1e8b4b9_2: + description: "消耗计费" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "消耗计费" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*消耗计费\\s*$/ })" + + tree_el_e7a791e5aea4e587bae5ba93e6b187e680bbe68aa5e8a1a8_2: + description: "科室出库汇总报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室出库汇总报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室出库汇总报表\\s*$/ })" + + tree_el_e7b4a7e680a5e585a5e5ba93e58f8ae8aea1e8b4b9_2: + description: "紧急入库及计费" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "紧急入库及计费" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*紧急入库及计费\\s*$/ })" + + tree_node_e9ab98e580bc: + description: "高值计退费" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "高值计退费" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*高值计退费\\s*$/ })" + + tree_el_e4be9be5ba94e59586e585a5e5ba93e6b187e680bbe68aa5e8a1a8_2: + description: "供应商入库汇总报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "供应商入库汇总报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*供应商入库汇总报表\\s*$/ })" + + tree_node_e694b6e694af: + description: "收支汇总报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "收支汇总报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*收支汇总报表\\s*$/ })" + + tree_el_e8aea1e98080e8b4b9e8aeb0e5bd95_2: + description: "计退费记录" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "计退费记录" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*计退费记录\\s*$/ })" + + tree_el_e7a791e5aea4e587bae5ba93e6b187e680bbe68aa5e8a1a87632_2: + description: "科室出库汇总报表v2" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室出库汇总报表v2" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室出库汇总报表v2\\s*$/ })" + + tree_node_e88097e69d90: + description: "耗材发放记录" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "耗材发放记录" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*耗材发放记录\\s*$/ })" + + tree_el_e4b8ade5bf83e5ba93e4b89ae58aa1_2: + description: "中心库业务" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库业务" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库业务\\s*$/ })" + + tree_el_e587bae5ba93e6a0b8e9aa8c_3: + description: "出库核验" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库核验" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库核验\\s*$/ })" + + tree_el_e7a791e5aea4e8afb7e9a286e5aea1e99885_2: + description: "科室请领审阅" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室请领审阅" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室请领审阅\\s*$/ })" + + tree_el_e98787e8b4ade6b187e680bbe5aea1e689b9_2: + description: "采购汇总审批" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购汇总审批" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购汇总审批\\s*$/ })" + + tree_finance_2: + description: "财务管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "财务管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*财务管理\\s*$/ })" + + tree_el_e7a791e5aea4e68890e69cace7a1aee8aea4_2: + description: "科室成本确认" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室成本确认" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室成本确认\\s*$/ })" + + tree_el_e6a380e9aa8ce9a1b9e79baee4b88ee6a380e9aa8ce8af95e58982e5afb9e785a7e7bbb4e68aa4_2: + description: "检验项目与检验试剂对照维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "检验项目与检验试剂对照维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*检验项目与检验试剂对照维护\\s*$/ })" + + tree_node_4249e68aa5e8: + description: "BI报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "BI报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*BI报表\\s*$/ })" + + tree_el_4249e68aa5e8a1a8e58fafe8a786e58c96_2: + description: "BI报表可视化" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "BI报表可视化" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*BI报表可视化\\s*$/ })" + + tree_node_yangcai_e998b3e98787: + description: "阳采管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "阳采管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*阳采管理\\s*$/ })" + + tree_node_e998b3e98787: + description: "阳采字典管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "阳采字典管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*阳采字典管理\\s*$/ })" + + tree_node_processCenter_e6b581e7a88b: + description: "流程中心" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "流程中心" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*流程中心\\s*$/ })" + + tree_el_e5aea1e689b9e4b8ade5bf83_2: + description: "审批中心" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "审批中心" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*审批中心\\s*$/ })" + + tree_node_e68891e79a84: + description: "我的" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "我的" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*我的\\s*$/ })" + + tree_node_e983a8e997a8: + description: "部门待审批单据" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "部门待审批单据" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*部门待审批单据\\s*$/ })" + + tree_node_logistics_e5908ee58ba4: + description: "后勤物资管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "后勤物资管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*后勤物资管理\\s*$/ })" + + tree_basicSetting_2: + description: "基础设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "基础设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*基础设置\\s*$/ })" + + tree_el_e4be9be5ba94e59586e7aea1e79086_2: + description: "供应商管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "供应商管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*供应商管理\\s*$/ })" + + tree_el_e789a9e8b584e5ad97e585b8e7aea1e79086_2: + description: "物资字典管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "物资字典管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*物资字典管理\\s*$/ })" + + tree_el_e7a791e5aea4e5ad97e585b8e58886e9858d_2: + description: "科室字典分配" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室字典分配" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室字典分配\\s*$/ })" + + tree_el_e999a2e58cbae789a9e8b584e8aebee7bdae_2: + description: "院区物资设置" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "院区物资设置" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*院区物资设置\\s*$/ })" + + tree_el_e8afb7e9a286e6a8a1e78988e7bbb4e68aa4_2: + description: "请领模版维护" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领模版维护" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领模版维护\\s*$/ })" + + tree_purchase_2: + description: "采购管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购管理\\s*$/ })" + + tree_el_e7a791e5aea4e5a487e8b4a7e8aea1e58892_2: + description: "科室备货计划" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室备货计划" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室备货计划\\s*$/ })" + + tree_el_e5a487e8b4a7e8aea1e58892e6b187e680bb_2: + description: "备货计划汇总" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "备货计划汇总" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*备货计划汇总\\s*$/ })" + + tree_el_e98787e8b4ade8aea1e58892_2: + description: "采购计划" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购计划" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购计划\\s*$/ })" + + tree_el_e98787e8b4ade8aea1e58892e6988ee7bb86e69fa5e8afa2_2: + description: "采购计划明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购计划明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购计划明细查询\\s*$/ })" + + tree_el_e98787e8b4ade58d95e7aea1e79086_3: + description: "采购单管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购单管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购单管理\\s*$/ })" + + tree_node_e9858de98081: + description: "配送管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "配送管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*配送管理\\s*$/ })" + + tree_el_e887aae58aa8e98787e8b4ade5bbbae8aeae_2: + description: "自动采购建议" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "自动采购建议" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*自动采购建议\\s*$/ })" + + tree_el_e4b8ade5bf83e5ba93e4b89ae58aa1_3: + description: "中心库业务" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库业务" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库业务\\s*$/ })" + + tree_el_e9aa8ce694b6e7aea1e79086_2: + description: "验收管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "验收管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*验收管理\\s*$/ })" + + tree_el_e585a5e5ba93e58d95e58897e8a1a8_2: + description: "入库单列表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "入库单列表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*入库单列表\\s*$/ })" + + tree_el_e6898be5b7a5e585a5e5ba93_2: + description: "手工入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手工入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手工入库\\s*$/ })" + + tree_el_e6898be5b7a5e587bae5ba93_3: + description: "手工出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手工出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手工出库\\s*$/ })" + + tree_el_e587bae5ba93e58d95e58897e8a1a8_3: + description: "出库单列表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库单列表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库单列表\\s*$/ })" + + tree_el_e98080e8b4a7e587bae5ba93_2: + description: "退货出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "退货出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*退货出库\\s*$/ })" + + tree_el_e587bae5ba93e58d95e5aea1e6a0b8_2: + description: "出库单审核" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库单审核" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库单审核\\s*$/ })" + + tree_el_e8afb7e9a286e4b8ade5bf83e5ba93e5aea1e6a0b8_2: + description: "请领中心库审核" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领中心库审核" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领中心库审核\\s*$/ })" + + tree_el_e8afb7e9a286e587bae5ba93_2: + description: "请领出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领出库\\s*$/ })" + + tree_el_e4b88ae69eb6e585a5e5ba93_2: + description: "上架入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "上架入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*上架入库\\s*$/ })" + + tree_el_e98080e8b4a7e6988ee7bb86e69fa5e8afa2_3: + description: "退货明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "退货明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*退货明细查询\\s*$/ })" + + tree_el_e7a791e5aea4e4b89ae58aa1_2: + description: "科室业务" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室业务" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室业务\\s*$/ })" + + tree_el_e7a791e5aea4e585a5e5ba93_3: + description: "科室入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室入库\\s*$/ })" + + tree_el_e68891e8a681e8afb7e9a286_3: + description: "我要请领" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "我要请领" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*我要请领\\s*$/ })" + + tree_el_e8afb7e9a286e7a791e5aea4e5aea1e6a0b8_2: + description: "请领科室审核" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领科室审核" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领科室审核\\s*$/ })" + + tree_el_e585a5e5ba93e58d95e58897e8a1a8_3: + description: "入库单列表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "入库单列表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*入库单列表\\s*$/ })" + + tree_el_e587bae5ba93e58d95e58897e8a1a8_4: + description: "出库单列表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库单列表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库单列表\\s*$/ })" + + tree_el_e7a791e5aea4e6b688e88097_2: + description: "科室消耗" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室消耗" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室消耗\\s*$/ })" + + tree_el_e7a791e5aea4e98080e5ba93_2: + description: "科室退库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室退库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室退库\\s*$/ })" + + tree_el_e5ba93e5ad98e69fa5e8afa2_3: + description: "库存查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存查询\\s*$/ })" + + tree_el_e5ba93e5ad98e6b187e680bbe69fa5e8afa2_3: + description: "库存汇总查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存汇总查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存汇总查询\\s*$/ })" + + tree_el_e5ba93e5ad98e6988ee7bb86_2: + description: "库存明细" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存明细" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存明细\\s*$/ })" + + tree_finance_3: + description: "财务管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "财务管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*财务管理\\s*$/ })" + + tree_el_e58f91e7a5a8e5bd95e585a5_2: + description: "发票录入" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发票录入" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发票录入\\s*$/ })" + + tree_el_e58f91e7a5a8e5aea1e6a0b8_2: + description: "发票审核" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发票审核" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发票审核\\s*$/ })" + + tree_el_e58f91e7a5a8e69fa5e8afa2_3: + description: "发票查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发票查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发票查询\\s*$/ })" + + tree_node_statReport_e7bb9fe8aea1: + description: "统计报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "统计报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*统计报表\\s*$/ })" + + tree_el_e4b8ade5bf83e5ba93e587bae585a5e5ba93e68aa5e8a1a8_2: + description: "中心库出入库报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库出入库报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库出入库报表\\s*$/ })" + + tree_node_inOutDetailQuery_e587bae585a5: + description: "出入库明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出入库明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出入库明细查询\\s*$/ })" + + tree_node_inboundSummaryQuery_e585a5e5ba93: + description: "入库汇总查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "入库汇总查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*入库汇总查询\\s*$/ })" + + tree_node_outboundSummaryQuery_e587bae5ba93: + description: "出库汇总查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库汇总查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库汇总查询\\s*$/ })" + + tree_el_e4b8ade5bf83e5ba93e4baa7e59381e8bf9be99480e5ad98_2: + description: "中心库产品进销存" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库产品进销存" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库产品进销存\\s*$/ })" + + tree_node_e587bae585a5: + description: "出入库明细查询(后勤)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出入库明细查询(后勤)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出入库明细查询(后勤)\\s*$/ })" + + tree_el_e4baa7e59381e591a8e8bdace78e87e68aa5e8a1a8_2: + description: "产品周转率报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品周转率报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品周转率报表\\s*$/ })" + + tree_el_e4b8ade5bf83e5ba93e8bf9be99480e5ad9828e5908ee58ba429_2: + description: "中心库进销存(后勤)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库进销存(后勤)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库进销存(后勤)\\s*$/ })" + + tree_node_e58780e585a5: + description: "净入库汇总查询(多维度)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "净入库汇总查询(多维度)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*净入库汇总查询(多维度)\\s*$/ })" + + tree_node_e58780e587ba: + description: "净出库汇总查询(多维度)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "净出库汇总查询(多维度)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*净出库汇总查询(多维度)\\s*$/ })" + + tree_el_e7a791e5aea4e587bae585a5e5ba93e68aa5e8a1a8_2: + description: "科室出入库报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室出入库报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室出入库报表\\s*$/ })" + + tree_inOutDetailQuery_2: + description: "出入库明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出入库明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出入库明细查询\\s*$/ })" + + tree_inboundSummaryQuery_2: + description: "入库汇总查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "入库汇总查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*入库汇总查询\\s*$/ })" + + tree_outboundSummaryQuery_2: + description: "出库汇总查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库汇总查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库汇总查询\\s*$/ })" + + tree_el_e7a791e5aea4e4baa7e59381e8bf9be99480e5ad98_2: + description: "科室产品进销存" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室产品进销存" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室产品进销存\\s*$/ })" + + tree_el_e587bae585a5e5ba93e6988ee7bb86e69fa5e8afa228e5908ee58ba4efbc89_2: + description: "出入库明细查询(后勤)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出入库明细查询(后勤)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出入库明细查询(后勤)\\s*$/ })" + + tree_el_e7a791e5aea4e4baa7e59381e591a8e8bdace78e87e68aa5e8a1a8_2: + description: "科室产品周转率报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室产品周转率报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室产品周转率报表\\s*$/ })" + + tree_el_e7bb93e7ae97e68890e69cace68aa5e8a1a8_2: + description: "结算成本报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结算成本报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*结算成本报表\\s*$/ })" + + tree_el_e68890e69cace585a5e8b4a6e6988ee7bb86e69fa5e8afa2_2: + description: "成本入账明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "成本入账明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*成本入账明细查询\\s*$/ })" + + tree_el_e7bb93e7ae97e585a5e8b4a6e6988ee7bb86e69fa5e8afa2_2: + description: "结算入账明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结算入账明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*结算入账明细查询\\s*$/ })" + + tree_el_e8b4a2e58aa1e58886e7b1bbe8bf9be99480e5ad98_2: + description: "财务分类进销存" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "财务分类进销存" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*财务分类进销存\\s*$/ })" + + tree_el_e8b4a2e58aa1e4baa7e59381e8bf9be99480e5ad98_2: + description: "财务产品进销存" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "财务产品进销存" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*财务产品进销存\\s*$/ })" + + tree_el_e7a791e5aea4e68890e69cace58886e7b1bbe6b187e680bb_2: + description: "科室成本分类汇总" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室成本分类汇总" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室成本分类汇总\\s*$/ })" + + tree_el_e4be9be5ba94e59586e585a5e8b4a6e58886e7b1bbe6b187e680bb_2: + description: "供应商入账分类汇总" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "供应商入账分类汇总" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*供应商入账分类汇总\\s*$/ })" + + tree_el_e7a791e5aea4e68890e69cace58886e7b1bbe6b187e680bb28e8aea1e8b4b9e7a791e5aea429_2: + description: "科室成本分类汇总(计费科室)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室成本分类汇总(计费科室)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室成本分类汇总(计费科室)\\s*$/ })" + + tree_el_e7a791e5aea4e58780e585a5e68890e69cace69fa5e8afa2_2: + description: "科室净入成本查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室净入成本查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室净入成本查询\\s*$/ })" + + tree_el_e7a791e5aea4e68890e69cace68e92e5908d_2: + description: "科室成本排名" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室成本排名" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室成本排名\\s*$/ })" + + tree_el_e4baa7e59381e68890e69cace68e92e5908d_2: + description: "产品成本排名" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "产品成本排名" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*产品成本排名\\s*$/ })" + + tree_el_e68890e69cace585a5e8b4a6e6b187e680bbe69fa5e8afa2_2: + description: "成本入账汇总查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "成本入账汇总查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*成本入账汇总查询\\s*$/ })" + + tree_el_e7a791e5aea4e68890e69cace58886e7b1bbe6b187e680bb_3: + description: "科室成本分类汇总" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室成本分类汇总" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室成本分类汇总\\s*$/ })" + + tree_el_e4be9be5ba94e59586e585a5e8b4a6e58886e7b1bbe6b187e680bbe697a7_2: + description: "供应商入账分类汇总旧" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "供应商入账分类汇总旧" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*供应商入账分类汇总旧\\s*$/ })" + + tree_el_e7a791e5aea4e68890e69cace58886e7b1bbe6b187e680bb28e8aea1e8b4b9e7a791e5aea429e697a7_2: + description: "科室成本分类汇总(计费科室)旧" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室成本分类汇总(计费科室)旧" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室成本分类汇总(计费科室)旧\\s*$/ })" + + tree_el_e8b4a2e58aa1e585a5e5ba93e58d95_2: + description: "财务入库单" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "财务入库单" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*财务入库单\\s*$/ })" + + tree_el_e8b4a2e58aa1e587bae5ba93e58d95_2: + description: "财务出库单" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "财务出库单" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*财务出库单\\s*$/ })" + + tree_el_e68890e69cace585a5e8b4a6e6988ee7bb86e69fa5e8afa2efbc88e689a7e8a18ce7a791e5aea4e7bbb4e5baa6efbc89_2: + description: "成本入账明细查询(执行科室维度)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "成本入账明细查询(执行科室维度)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*成本入账明细查询(执行科室维度)\\s*$/ })" + + tree_el_e7a791e5aea4e68890e69cace58886e7b1bbe6b187e680bb28e5908ee58ba429_2: + description: "科室成本分类汇总(后勤)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室成本分类汇总(后勤)" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室成本分类汇总(后勤)\\s*$/ })" + + tree_node_e5afb9e8b4a6: + description: "对账报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "对账报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*对账报表\\s*$/ })" + + tree_el_e7a791e5aea4e5afb9e8b4a6e69fa5e8afa2_2: + description: "科室对账查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室对账查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室对账查询\\s*$/ })" + + tree_el_e7a791e5aea4e58780e585a5e6b187e680bbe8a1a8_2: + description: "科室净入汇总表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室净入汇总表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室净入汇总表\\s*$/ })" + + tree_el_e8afb7e9a286e587bae5ba93e58886e7b1bbe6b187e680bbe8a1a8_2: + description: "请领出库分类汇总表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "请领出库分类汇总表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*请领出库分类汇总表\\s*$/ })" + + tree_el_e98787e8b4ade58886e7b1bbe6b187e680bb_2: + description: "采购分类汇总" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购分类汇总" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购分类汇总\\s*$/ })" + + tree_el_e98787e8b4ade585a5e5ba93e6b187e680bbe8a1a8_2: + description: "采购入库汇总表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购入库汇总表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购入库汇总表\\s*$/ })" + + tree_el_e4be9be5ba94e59586e5afb9e8b4a6e69fa5e8afa2_2: + description: "供应商对账查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "供应商对账查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*供应商对账查询\\s*$/ })" + + tree_el_e7a791e5aea4e58780e587bae6b187e680bbe8a1a8_2: + description: "科室净出汇总表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室净出汇总表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室净出汇总表\\s*$/ })" + + tree_node_e8bfbde6baaf: + description: "追溯报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "追溯报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*追溯报表\\s*$/ })" + + tree_node_e79bb4e9858d: + description: "直配请领单追溯" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "直配请领单追溯" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*直配请领单追溯\\s*$/ })" + + tree_el_e69da1e7a081e8bfbde6baaf_2: + description: "条码追溯" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "条码追溯" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*条码追溯\\s*$/ })" + + tree_el_e79bb4e9858de8afb7e9a286e6988ee7bb86e69fa5e8afa2_2: + description: "直配请领明细查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "直配请领明细查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*直配请领明细查询\\s*$/ })" + + tree_el_e7a791e5aea4e8aea2e58d95e5ae8ce68890e68385e586b5e8bfbde6baafe68aa5e8a1a8_2: + description: "科室订单完成情况追溯报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室订单完成情况追溯报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室订单完成情况追溯报表\\s*$/ })" + + tree_el_e98787e8b4ade58d95e8bfbde6baaf_2: + description: "采购单追溯" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "采购单追溯" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*采购单追溯\\s*$/ })" + + tree_el_e7a791e5aea4e8afb7e9a286e58d95e8bfbde6baaf_2: + description: "科室请领单追溯" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室请领单追溯" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室请领单追溯\\s*$/ })" + + tree_el_e7bb93e7ae97e5bc80e7a5a8e69fa5e8afa2_2: + description: "结算开票查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结算开票查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*结算开票查询\\s*$/ })" + + tree_el_e7bb93e7ae97e5bc80e7a5a8e69fa5e8afa2_3: + description: "结算开票查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结算开票查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*结算开票查询\\s*$/ })" + + tree_el_e4be9be5ba94e59586e5bc80e7a5a8e58886e7b1bbe6b187e680bb_2: + description: "供应商开票分类汇总" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "供应商开票分类汇总" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*供应商开票分类汇总\\s*$/ })" + + tree_el_e7bb93e7ae97e58d95e58f91e7a5a8e78ab6e68081e69fa5e8afa2_2: + description: "结算单发票状态查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结算单发票状态查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*结算单发票状态查询\\s*$/ })" + + tree_el_e58f91e694bee6b688e88097e68aa5e8a1a8_2: + description: "发放消耗报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发放消耗报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发放消耗报表\\s*$/ })" + + tree_el_e9ab98e580bce6b688e88097e8bfbde6baaf_2: + description: "高值消耗追溯" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "高值消耗追溯" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*高值消耗追溯\\s*$/ })" + + tree_el_e6b688e88097e8b68be58abfe58886e69e90_2: + description: "消耗趋势分析" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "消耗趋势分析" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*消耗趋势分析\\s*$/ })" + + tree_node_e682a3e88085: + description: "患者消耗追溯" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "患者消耗追溯" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*患者消耗追溯\\s*$/ })" + + tree_node_e5a49ae6aca1: + description: "多次计费耗材追溯" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "多次计费耗材追溯" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*多次计费耗材追溯\\s*$/ })" + + tree_el_e7a791e5aea4e6b688e88097e68e92e5908d_2: + description: "科室消耗排名" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室消耗排名" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室消耗排名\\s*$/ })" + + tree_node_e9a284e8ada6: + description: "预警报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "预警报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*预警报表\\s*$/ })" + + tree_node_e6bb9ee79599: + description: "滞留预警" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "滞留预警" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*滞留预警\\s*$/ })" + + tree_el_e7bcbae8b4a7e68aa5e8a1a8_2: + description: "缺货报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "缺货报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*缺货报表\\s*$/ })" + + tree_node_e8bf91e69c9f: + description: "近期效期查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "近期效期查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*近期效期查询\\s*$/ })" + + tree_node_e4bd8ee582a8: + description: "低储预警" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "低储预警" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*低储预警\\s*$/ })" + + tree_node_e5bc82e5b8b8: + description: "异常分析报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "异常分析报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*异常分析报表\\s*$/ })" + + tree_node_e8aea1e8b4b9: + description: "计费消耗差异报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "计费消耗差异报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*计费消耗差异报表\\s*$/ })" + + tree_el_e4bd8ee580bce8aea1e8b4b9e68ea5e58fa3e5b7aee5bc82e7bb9fe8aea1_2: + description: "低值计费接口差异统计" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "低值计费接口差异统计" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*低值计费接口差异统计\\s*$/ })" + + tree_el_e4bd8ee580bce8aea1e8b4b9e6b688e88097e5b7aee5bc82e7bb9fe8aea1_2: + description: "低值计费消耗差异统计" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "低值计费消耗差异统计" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*低值计费消耗差异统计\\s*$/ })" + + tree_el_e8bf90e7bbb4e68aa5e8a1a8_2: + description: "运维报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "运维报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*运维报表\\s*$/ })" + + tree_el_e585a5e8b4a6e6a0b8e5afb9e69fa5e8afa2_2: + description: "入账核对查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "入账核对查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*入账核对查询\\s*$/ })" + + tree_el_e699bae883bde69f9ce68aa5e8a1a8_2: + description: "智能柜报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "智能柜报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*智能柜报表\\s*$/ })" + + tree_el_e699bae883bde69f9ce5ba93e5ad98e69fa5e8afa2_2: + description: "智能柜库存查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "智能柜库存查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*智能柜库存查询\\s*$/ })" + + tree_el_e6938de4bd9ce6b581e6b0b4e69fa5e8afa2_2: + description: "操作流水查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "操作流水查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*操作流水查询\\s*$/ })" + + tree_el_e9a286e794a8e69caae8bf98e59b9ee69fa5e8afa2_2: + description: "领用未还回查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "领用未还回查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*领用未还回查询\\s*$/ })" + + tree_el_e699bae883bde69f9ce5b7aee5bc82e68aa5e8a1a8_2: + description: "智能柜差异报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "智能柜差异报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*智能柜差异报表\\s*$/ })" + + tree_node_e694bfe7ad96: + description: "政策导向" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "政策导向" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*政策导向\\s*$/ })" + + tree_node_e9878de782b9: + description: "重点管控耗材统计" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "重点管控耗材统计" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*重点管控耗材统计\\s*$/ })" + + tree_el_e5b8a6e9878fe4bbbbe58aa1e7bb9fe8aea1_2: + description: "带量任务统计" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "带量任务统计" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*带量任务统计\\s*$/ })" + + tree_el_e5b8a6e9878fe6988ee7bb86e5ae8ce68890e78e87e69fa5e8afa2_2: + description: "带量明细完成率查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "带量明细完成率查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*带量明细完成率查询\\s*$/ })" + + tree_el_e5b8a6e9878fe7a791e5aea4e4bbbbe58aa1e6988ee7bb86_2: + description: "带量科室任务明细" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "带量科室任务明细" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*带量科室任务明细\\s*$/ })" + + tree_el_e5b8a6e9878fe79baee5bd95e6b688e88097e695b0e68daee7bb9fe8aea1_2: + description: "带量目录消耗数据统计" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "带量目录消耗数据统计" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*带量目录消耗数据统计\\s*$/ })" + + tree_node_37e4bd8de58c: + description: "7位医保码带量分析" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "7位医保码带量分析" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*7位医保码带量分析\\s*$/ })" + + tree_node_e7bb9fe8aea1: + description: "统计分析" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "统计分析" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*统计分析\\s*$/ })" + + tree_el_e8b083e4bbb7e7bb9fe8aea1e68aa5e8a1a8_2: + description: "调价统计报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "调价统计报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*调价统计报表\\s*$/ })" + + tree_el_e8b4a2e58aa1e695b0e68daee58886e69e902de7a791e5aea4e68890e69cac_2: + description: "财务数据分析-科室成本" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "财务数据分析-科室成本" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*财务数据分析-科室成本\\s*$/ })" + + tree_el_e8b4a2e58aa1e695b0e68daee58886e69e902de4be9be5ba94e59586e585a5e8b4a6_2: + description: "财务数据分析-供应商入账" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "财务数据分析-供应商入账" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*财务数据分析-供应商入账\\s*$/ })" + + tree_el_e8af95e58982e689b9e58fb7e7bb9fe8aea1_2: + description: "试剂批号统计" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "试剂批号统计" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*试剂批号统计\\s*$/ })" + + tree_el_e8af95e58982e689b9e58fb7e7bb9fe8aea1e6988ee7bb86_2: + description: "试剂批号统计明细" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "试剂批号统计明细" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*试剂批号统计明细\\s*$/ })" + + tree_el_e8af95e58982e689b9e58fb7e7bb9fe8aea1e6988ee7bb86_3: + description: "试剂批号统计明细" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "试剂批号统计明细" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*试剂批号统计明细\\s*$/ })" + + tree_el_e8af95e58982e689b9e58fb7e7bb9fe8aea1_3: + description: "试剂批号统计" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "试剂批号统计" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*试剂批号统计\\s*$/ })" + + tree_el_e8af95e58982e689b9e58fb7e7bb9fe8aea1e6988ee7bb86_4: + description: "试剂批号统计明细" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "试剂批号统计明细" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*试剂批号统计明细\\s*$/ })" + + tree_el_e8af95e58982e689b9e58fb7e7bb9fe8aea1e6988ee7bb86_5: + description: "试剂批号统计明细" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "试剂批号统计明细" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*试剂批号统计明细\\s*$/ })" + + tree_node_dataViz_e695b0e68dae: + description: "数据可视化" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "数据可视化" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*数据可视化\\s*$/ })" + + tree_node_e6b58be8af95: + description: "测试0422" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "测试0422" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*测试0422\\s*$/ })" + + tree_el_e4be9be5ba94e59586e585b3e88194e4baa4e69893e79c8be69dbf_2: + description: "供应商关联交易看板" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "供应商关联交易看板" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*供应商关联交易看板\\s*$/ })" + + tree_node_31: + description: "1" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "1" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*1\\s*$/ })" + + tree_node_e8b7b3e8bdac: + description: "跳转测试6" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "跳转测试6" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*跳转测试6\\s*$/ })" + + tree_node_3132313232: + description: "12122" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "12122" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*12122\\s*$/ })" + + tree_el_e8b7b3e8bdace6b58be8af9537_2: + description: "跳转测试7" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "跳转测试7" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*跳转测试7\\s*$/ })" + + el_535044e58685e7bd91e7b3bbe7bb9f706461: + description: "SPD内网系统pda" + element_type: checkbox + section: "main::main" + strategy: text + value: "SPD内网系统pda" + playwright: "page.getByText('SPD内网系统pda')" + + tree_centerStock_2: + description: "中心库管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "中心库管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*中心库管理\\s*$/ })" + + tree_node_e588b0e8b4a7: + description: "到货验收" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "到货验收" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*到货验收\\s*$/ })" + + tree_el_e4b88ae69eb6e585a5e5ba93_3: + description: "上架入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "上架入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*上架入库\\s*$/ })" + + tree_el_e68ba3e8b4a7e4b88be69eb6_2: + description: "拣货下架" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "拣货下架" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*拣货下架\\s*$/ })" + + tree_el_e6898be5b7a5e587bae5ba93_4: + description: "手工出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手工出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手工出库\\s*$/ })" + + tree_el_e8b083e68ba8e587bae5ba93_2: + description: "调拨出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "调拨出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*调拨出库\\s*$/ })" + + tree_el_e98080e8b4a7e587bae5ba93_3: + description: "退货出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "退货出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*退货出库\\s*$/ })" + + tree_el_e7a7bbe5ba93e587bae5ba93_2: + description: "移库出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "移库出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*移库出库\\s*$/ })" + + tree_el_e587bae5ba93e5a48de6a0b8_3: + description: "出库复核" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库复核" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库复核\\s*$/ })" + + tree_el_e5bfabe9809fe58aa0e5b7a5_3: + description: "快速加工" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "快速加工" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*快速加工\\s*$/ })" + + tree_el_e58aa0e5b7a5e689a7e8a18c_2: + description: "加工执行" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "加工执行" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*加工执行\\s*$/ })" + + tree_el_e5ba93e5ad98e79b98e782b9_2: + description: "库存盘点" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存盘点" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存盘点\\s*$/ })" + + tree_el_e5ba93e5ad98e69fa5e8afa2_4: + description: "库存查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存查询\\s*$/ })" + + tree_el_e8b4a7e4bd8de8b083e695b4_3: + description: "货位调整" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "货位调整" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*货位调整\\s*$/ })" + + tree_el_e5bfabe9809fe689abe7a081_2: + description: "快速扫码" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "快速扫码" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*快速扫码\\s*$/ })" + + tree_el_e69c9fe5889de5bd95e585a5_2: + description: "期初录入" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "期初录入" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*期初录入\\s*$/ })" + + tree_node_e59b9ee58d95: + description: "回单管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "回单管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*回单管理\\s*$/ })" + + tree_node_e689b9e9878f: + description: "批量货位调整" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "批量货位调整" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*批量货位调整\\s*$/ })" + + tree_node_e8819ae58d95: + description: "聚单拣货" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "聚单拣货" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*聚单拣货\\s*$/ })" + + tree_el_e8819ae58d95e5a48de6a0b8_2: + description: "聚单复核" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "聚单复核" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*聚单复核\\s*$/ })" + + tree_el_e4bba3e7a791e5aea4e585a5e5ba93_2: + description: "代科室入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "代科室入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*代科室入库\\s*$/ })" + + tree_el_e68abde9aa8ce7aea1e79086_2: + description: "抽验管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "抽验管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*抽验管理\\s*$/ })" + + tree_el_e587bae5ba93e58d95e7bb91e5ae9a_2: + description: "出库单绑定" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "出库单绑定" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*出库单绑定\\s*$/ })" + + tree_node_e999a2e58685: + description: "院内配送" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "院内配送" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*院内配送\\s*$/ })" + + tree_el_e999a2e58685e98081e8bebe_2: + description: "院内送达" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "院内送达" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*院内送达\\s*$/ })" + + tree_el_e5bfabe9809fe68b86e58c85_2: + description: "快速拆包" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "快速拆包" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*快速拆包\\s*$/ })" + + tree_node_e694b6e8b4a7: + description: "收货复核" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "收货复核" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*收货复核\\s*$/ })" + + tree_el_e7aeb1e7a081e68993e58db0_2: + description: "箱码打印" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "箱码打印" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*箱码打印\\s*$/ })" + + tree_el_e8819ae58d95e9aa8ce694b6_2: + description: "聚单验收" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "聚单验收" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*聚单验收\\s*$/ })" + + tree_node_e58cbbe5b7a5: + description: "医工抽检" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "医工抽检" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*医工抽检\\s*$/ })" + + tree_el_e7a791e5aea4e5ba93e7aea1e79086_2: + description: "科室库管理" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室库管理" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室库管理\\s*$/ })" + + tree_el_e7a791e5aea4e585a5e5ba93_4: + description: "科室入库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "科室入库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*科室入库\\s*$/ })" + + tree_el_e58f91e694bee587bae5ba93_2: + description: "发放出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "发放出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*发放出库\\s*$/ })" + + tree_el_e98080e5ba93e587bae5ba93_3: + description: "退库出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "退库出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*退库出库\\s*$/ })" + + tree_el_e9a286e794a8e587bae5ba93_2: + description: "领用出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "领用出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*领用出库\\s*$/ })" + + tree_el_e6898be5b7a5e9a286e794a8e587bae5ba93_3: + description: "手工领用出库" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手工领用出库" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手工领用出库\\s*$/ })" + + tree_el_e6898be5b7a5e6b688e88097_3: + description: "手工消耗" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "手工消耗" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*手工消耗\\s*$/ })" + + tree_el_e5ba93e5ad98e79b98e782b9_3: + description: "库存盘点" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存盘点" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存盘点\\s*$/ })" + + tree_el_e5ba93e5ad98e69fa5e8afa2_5: + description: "库存查询" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库存查询" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库存查询\\s*$/ })" + + tree_el_e98080e5ba93e5a48de6a0b8_2: + description: "退库复核" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "退库复核" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*退库复核\\s*$/ })" + + tree_el_e5ba93e58cbae8b4a7e4bd8de7a7bbe58aa8_2: + description: "库区货位移动" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "库区货位移动" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*库区货位移动\\s*$/ })" + + tree_statReport_2: + description: "统计报表" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "统计报表" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*统计报表\\s*$/ })" + + tree_el_e69da1e7a081e8bfbde6baaf_3: + description: "条码追溯" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "条码追溯" + tree: ".menu-role-tree" + playwright: "page.locator('.menu-role-tree .ant-tree-title').filter({ hasText: /^\\s*条码追溯\\s*$/ })" diff --git a/locators/spd/basic-shared.locator.ts b/locators/spd/basic-shared.locator.ts new file mode 100644 index 0000000..f4cd411 --- /dev/null +++ b/locators/spd/basic-shared.locator.ts @@ -0,0 +1,46 @@ +import type { Locator, Page } from '@playwright/test'; + +/** 基础设置 · 医院资料 共用菜单 / 表格 / 分页 / 弹窗。 */ +export class BasicSharedLocators { + readonly menuBasic: Locator; + readonly menuHospital: Locator; + readonly table: Locator; + readonly tableHeader: Locator; + readonly dataRows: Locator; + readonly empty: Locator; + readonly pagination: Locator; + readonly paginationTotal: Locator; + readonly pageJumpInput: Locator; + readonly nextPage: Locator; + readonly dialog: Locator; + readonly confirm: Locator; + readonly formError: Locator; + + constructor(protected readonly page: Page) { + this.menuBasic = page.getByRole('menuitem', { name: '基础设置' }); + this.menuHospital = page.getByRole('menuitem', { name: '医院资料' }); + this.table = page.locator('.ant-table').first(); + this.tableHeader = page.locator('.ant-table-thead th'); + this.dataRows = page.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)'); + this.empty = page.locator('.ant-empty, .ant-empty-description').first(); + this.pagination = page.locator('.ant-pagination').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.dialog = page.getByRole('dialog'); + this.confirm = page.locator('.ant-modal-confirm, .ant-popconfirm, .ant-modal').filter({ hasText: '提示' }).last(); + this.formError = page.locator('.ant-form-item-explain-error'); + } + + menuItem(name: string): Locator { + return this.page.getByRole('menuitem', { name, exact: true }); + } + + button(name: string | RegExp): Locator { + return this.page.getByRole('button', { name }); + } + + dialogButton(name: string | RegExp): Locator { + return this.dialog.getByRole('button', { name }); + } +} diff --git a/locators/spd/basic-user-shared.locator.ts b/locators/spd/basic-user-shared.locator.ts new file mode 100644 index 0000000..13efac4 --- /dev/null +++ b/locators/spd/basic-user-shared.locator.ts @@ -0,0 +1,46 @@ +import type { Locator, Page } from '@playwright/test'; + +/** 基础设置 · 用户管理。叶子菜单是 .ant-menu-item;一级与叶子均可叫「用户管理」。 */ +export class BasicUserSharedLocators { + readonly menuBasic: Locator; + readonly userSubmenuTitle: Locator; + readonly tableHeader: Locator; + readonly empty: Locator; + readonly paginationTotal: Locator; + readonly nextPage: Locator; + readonly formError: Locator; + readonly advancedFilter: Locator; + readonly nameInput: Locator; + readonly dialog: Locator; + + constructor(protected readonly page: Page) { + this.menuBasic = page.getByRole('menuitem', { name: '基础设置' }); + this.userSubmenuTitle = 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.nextPage = page.locator('.ant-pagination-next').first(); + this.formError = page.locator('.ant-form-item-explain-error, .ant-message-notice, .ant-notification-notice'); + this.advancedFilter = page.locator('.advance-filter').first(); + this.nameInput = page.getByPlaceholder('姓名、登录账号'); + this.dialog = page.locator('.ant-modal-wrap .ant-modal:visible, .ant-drawer-open').last(); + } + + 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 }); + } + + formItem(label: string, root: Locator | Page = this.page): Locator { + return root.locator('.ant-form-item').filter({ hasText: label }).first(); + } + + formSelect(label: string, root: Locator | Page = this.page): Locator { + return this.formItem(label, root).locator('.ant-select').first(); + } +} diff --git a/locators/spd/basic-user-union.locator.ts b/locators/spd/basic-user-union.locator.ts new file mode 100644 index 0000000..a9677f9 --- /dev/null +++ b/locators/spd/basic-user-union.locator.ts @@ -0,0 +1,10 @@ +import type { Page } from '@playwright/test'; + +import { BasicUserLocators } from './basic-user.locator'; + +/** 医共体用户管理。选择器来自 locators/spd/basic-user-union.locator.yaml */ +export class BasicUserUnionLocators extends BasicUserLocators { + constructor(page: Page) { + super(page); + } +} diff --git a/locators/spd/basic-user-union.locator.yaml b/locators/spd/basic-user-union.locator.yaml new file mode 100644 index 0000000..a278e97 --- /dev/null +++ b/locators/spd/basic-user-union.locator.yaml @@ -0,0 +1,533 @@ +# Auto-generated from basic-user-union-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/user/platform/personCom +# Scanned: 2026-10-20T07:55:55.473Z +# Page: 基础设置 > 用户管理 > 医共体用户管理 +# Note: advanced filter panel was opened during scan +# Note: filter dropdown was opened during scan +# Note: edit dialog was opened during scan + +page: basic-user-union +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/user/platform/personCom + +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_roleManage: + description: "角色管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "角色管理" + playwright: "page.getByRole('menuitem', { name: '角色管理' })" + + menu_userManage: + description: "用户管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "用户管理" + playwright: "page.getByRole('menuitem', { name: '用户管理' })" + + menu_unionUserManage: + description: "医共体用户管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "医共体用户管理" + playwright: "page.getByRole('menuitem', { name: '医共体用户管理' })" + + unionUserManage: + description: "医共体用户管理" + element_type: tab + section: "main::main" + strategy: text + value: "医共体用户管理" + playwright: "page.getByText('医共体用户管理')" + + tab_unionUserManage: + description: "医共体用户管理" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/user/platform/personCom" + playwright: "page.locator('#rc-tabs-0-tab-/basic/user/platform/personCom')" + + el_e5a793e5908dInput: + description: "姓名、登录账号" + element_type: text + section: "main::main" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + el_e68980e5b19ee58cbbe999a2Select: + description: "邹城市人民医院(test)" + element_type: select + section: "main::main" + strategy: label_text + value: "所属医院" + playwright: "page.getByText('所属医院', { exact: true })" + + el_e68980e5b19ee58cbbe999a2Input: + description: "所属医院" + element_type: search + section: "main::main" + strategy: id + value: "form_item_corpId" + playwright: "page.locator('#form_item_corpId')" + + el_e88194e7b3bbe696b9e5bc8fInput: + description: "联系方式" + element_type: text + section: "main::main" + strategy: id + value: "form_item_tel" + playwright: "page.locator('#form_item_tel')" + + 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: '重 置' })" + + advancedFilterBtn: + description: "高级筛选" + element_type: filter_trigger + section: "main::main" + strategy: css + value: ".advance-filter" + playwright: "page.locator('.advance-filter')" + + addBtn: + description: "新 增" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新 增" + playwright: "page.getByRole('button', { name: '新 增' })" + + el_457863656ce5afbce585a5Btn: + description: "Excel导入" + element_type: button + section: "main::main" + strategy: role + role: button + name: "Excel导入" + playwright: "page.getByRole('button', { name: 'Excel导入' })" + + tableDensity: + description: "column-height" + element_type: span + section: "main::main" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + editBtn: + description: "编辑" + element_type: button + section: "main::main" + strategy: role + role: button + name: "编辑" + playwright: "page.getByRole('button', { name: '编辑' })" + + el_e68e88e69d83Btn: + description: "授权" + element_type: button + section: "main::main" + strategy: role + role: button + name: "授权" + playwright: "page.getByRole('button', { name: '授权' })" + + el_e9878de7bdaee5af86e7a081Btn: + description: "重置密码" + element_type: button + section: "main::main" + strategy: role + role: button + name: "重置密码" + playwright: "page.getByRole('button', { name: '重置密码' })" + + el_e7a791e5aea4Select: + description: "请选择" + element_type: select + section: "main::main" + strategy: label_text + value: "科室" + playwright: "page.getByText('科室', { exact: true })" + + el_e5a793e5908dInput_2: + description: "姓名" + element_type: search + section: "main::main" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + el_e8a792e889b2Select: + description: "请选择" + element_type: select + section: "main::main" + strategy: label_text + value: "角色" + playwright: "page.getByText('角色', { exact: true })" + + el_e8a792e889b2Input: + description: "角色" + element_type: search + section: "main::main" + strategy: id + value: "form_item_roleCode" + playwright: "page.locator('#form_item_roleCode')" + + filterApplyBtn: + description: "筛 选" + element_type: button + section: "main::main" + strategy: role + role: button + name: "筛 选" + playwright: "page.getByRole('button', { name: '筛 选' })" + + # --- main::pagination --- + pagination_el_e585b1313432e69da13132333530e69da12fe9a1b5e8b7b3e887b3e9a1b5: + description: "共 142 条12350 条/页跳至页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 142 条12350 条/页跳至页" + playwright: "page.getByText('共 142 条12350 条/页跳至页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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_page_2: + description: "2" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "2" + playwright: "page.getByText('2')" + + pagination_page_3: + description: "3" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "3" + playwright: "page.getByText('3')" + + pagination_nextPageBtn: + description: "下一页" + element_type: li + section: "main::pagination" + strategy: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + # --- main::panel:高级筛选 --- + drawer_el_e5a793e5908dInput: + description: "姓名、登录账号" + element_type: text + section: "main::panel:高级筛选" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + drawer_el_e68980e5b19ee58cbbe999a2Select: + description: "邹城市人民医院(test)" + element_type: select + section: "main::panel:高级筛选" + strategy: label_text + value: "所属医院" + playwright: "page.getByText('所属医院', { exact: true })" + + drawer_el_e68980e5b19ee58cbbe999a2Input: + description: "所属医院" + element_type: search + section: "main::panel:高级筛选" + strategy: id + value: "form_item_corpId" + playwright: "page.locator('#form_item_corpId')" + + drawer_el_e88194e7b3bbe696b9e5bc8fInput: + description: "联系方式" + element_type: text + section: "main::panel:高级筛选" + strategy: id + value: "form_item_tel" + playwright: "page.locator('#form_item_tel')" + + drawer_el_e7a791e5aea4Select: + description: "请选择" + element_type: select + section: "main::panel:高级筛选" + strategy: label_text + value: "科室" + playwright: "page.getByText('科室', { exact: true })" + + drawer_el_e5a793e5908dInput_2: + description: "姓名" + element_type: search + section: "main::panel:高级筛选" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + drawer_el_e8a792e889b2Select: + description: "请选择" + element_type: select + section: "main::panel:高级筛选" + strategy: label_text + value: "角色" + playwright: "page.getByText('角色', { exact: true })" + + drawer_el_e8a792e889b2Input: + description: "角色" + element_type: search + section: "main::panel:高级筛选" + strategy: id + value: "form_item_roleCode" + playwright: "page.locator('#form_item_roleCode')" + + # --- main::panel:drawer:footer --- + # --- main::modal:编辑用户 --- + el_e8b4a6e58fb7Input: + description: "请输入" + element_type: text + section: "main::modal:编辑用户" + strategy: id + value: "form_item_userCode" + playwright: "page.locator('#form_item_userCode')" + + el_e5a793e5908dInput_3: + description: "请输入" + element_type: text + section: "main::modal:编辑用户" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + el_e88194e7b3bbe696b9e5bc8fInput_2: + description: "请输入" + element_type: text + section: "main::modal:编辑用户" + strategy: id + value: "form_item_tel" + playwright: "page.locator('#form_item_tel')" + + el_e58cbbe999a2e59198e5b7a5e7bc96e58fb7Input: + description: "请输入" + element_type: text + section: "main::modal:编辑用户" + strategy: id + value: "form_item_erpCode" + playwright: "page.locator('#form_item_erpCode')" + + el_e7adbee5908de59bbee78987: + description: "签名图片" + element_type: span + section: "main::modal:编辑用户" + strategy: role + role: button + name: "签名图片" + playwright: "page.getByRole('button', { name: '签名图片' })" + + el_e7a681e794a8: + description: "禁用" + element_type: radio + section: "main::modal:编辑用户" + strategy: text + value: "禁用" + playwright: "page.getByText('禁用')" + + enable: + description: "启用" + element_type: radio + section: "main::modal:编辑用户" + strategy: text + value: "启用" + playwright: "page.getByText('启用')" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:编辑用户" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + confirmBtn: + description: "确 定" + element_type: button + section: "main::modal:编辑用户" + strategy: role + role: button + name: "确 定" + playwright: "page.getByRole('button', { name: '确 定' })" diff --git a/locators/spd/basic-user.locator.ts b/locators/spd/basic-user.locator.ts new file mode 100644 index 0000000..d2f192e --- /dev/null +++ b/locators/spd/basic-user.locator.ts @@ -0,0 +1,35 @@ +import type { Locator, Page } from '@playwright/test'; + +import { BasicUserSharedLocators } from './basic-user-shared.locator'; + +/** 用户管理。选择器来自 locators/spd/basic-user.locator.yaml */ +export class BasicUserLocators extends BasicUserSharedLocators { + readonly deptInput: Locator; + readonly roleInput: Locator; + readonly excelImport: Locator; + readonly signature: Locator; + + constructor(page: Page) { + super(page); + this.deptInput = page.locator('#form_item_deptId'); + this.roleInput = page.locator('#form_item_roleCode'); + this.excelImport = page.getByRole('button', { name: 'Excel导入' }); + this.signature = page.getByText('签名图片'); + } + + dialogUserCode(): Locator { + return this.dialog.locator('#form_item_userCode'); + } + + dialogName(): Locator { + return this.dialog.locator('#form_item_cname'); + } + + dialogTel(): Locator { + return this.dialog.locator('#form_item_tel'); + } + + dialogErp(): Locator { + return this.dialog.locator('#form_item_erpCode'); + } +} diff --git a/locators/spd/basic-user.locator.yaml b/locators/spd/basic-user.locator.yaml new file mode 100644 index 0000000..7077c9c --- /dev/null +++ b/locators/spd/basic-user.locator.yaml @@ -0,0 +1,517 @@ +# Auto-generated from basic-user-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/user/platform/person +# Scanned: 2026-10-20T07:54:40.566Z +# Page: 基础设置 > 用户管理 > 用户管理 +# Note: advanced filter panel was opened during scan +# Note: filter dropdown was opened during scan +# Note: edit dialog was opened during scan + +page: basic-user +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/user/platform/person + +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_roleManage: + description: "角色管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "角色管理" + playwright: "page.getByRole('menuitem', { name: '角色管理' })" + + menu_userManage: + description: "用户管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "用户管理" + playwright: "page.getByRole('menuitem', { name: '用户管理' })" + + menu_unionUserManage: + description: "医共体用户管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "医共体用户管理" + playwright: "page.getByRole('menuitem', { name: '医共体用户管理' })" + + userManage: + description: "用户管理" + element_type: tab + section: "main::main" + strategy: text + value: "用户管理" + playwright: "page.getByText('用户管理')" + + tab_userManage: + description: "用户管理" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/user/platform/person" + playwright: "page.locator('#rc-tabs-0-tab-/basic/user/platform/person')" + + el_e5a793e5908dInput: + description: "姓名、登录账号" + element_type: text + section: "main::main" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + el_e7a791e5aea4Select: + description: "请选择" + element_type: select + section: "main::main" + strategy: label_text + value: "科室" + playwright: "page.getByText('科室', { exact: true })" + + el_e5a793e5908dInput_2: + description: "姓名" + element_type: search + section: "main::main" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + el_e8a792e889b2Select: + description: "请选择" + element_type: select + section: "main::main" + strategy: label_text + value: "角色" + playwright: "page.getByText('角色', { exact: true })" + + el_e8a792e889b2Input: + description: "角色" + element_type: search + section: "main::main" + strategy: id + value: "form_item_roleCode" + playwright: "page.locator('#form_item_roleCode')" + + 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: '重 置' })" + + advancedFilterBtn: + description: "高级筛选" + element_type: filter_trigger + section: "main::main" + strategy: css + value: ".advance-filter" + playwright: "page.locator('.advance-filter')" + + addBtn: + description: "新 增" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新 增" + playwright: "page.getByRole('button', { name: '新 增' })" + + el_457863656ce5afbce585a5Btn: + description: "Excel导入" + element_type: button + section: "main::main" + strategy: role + role: button + name: "Excel导入" + playwright: "page.getByRole('button', { name: 'Excel导入' })" + + tableDensity: + description: "column-height" + element_type: span + section: "main::main" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + editBtn: + description: "编辑" + element_type: button + section: "main::main" + strategy: role + role: button + name: "编辑" + playwright: "page.getByRole('button', { name: '编辑' })" + + el_e68e88e69d83Btn: + description: "授权" + element_type: button + section: "main::main" + strategy: role + role: button + name: "授权" + playwright: "page.getByRole('button', { name: '授权' })" + + el_e9878de7bdaee5af86e7a081Btn: + description: "重置密码" + element_type: button + section: "main::main" + strategy: role + role: button + name: "重置密码" + playwright: "page.getByRole('button', { name: '重置密码' })" + + el_e5a793e5908dInput_3: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + el_e7a791e5aea4Input: + description: "科室" + element_type: search + section: "main::main" + strategy: id + value: "form_item_deptId" + playwright: "page.locator('#form_item_deptId')" + + statusSelect: + description: "状态" + element_type: select + section: "main::main" + strategy: label_text + value: "状态" + playwright: "page.getByText('状态', { exact: true })" + + filterApplyBtn: + description: "筛 选" + element_type: button + section: "main::main" + strategy: role + role: button + name: "筛 选" + playwright: "page.getByRole('button', { name: '筛 选' })" + + # --- main::pagination --- + pagination_el_e585b1313432e69da13132333530e69da12fe9a1b5e8b7b3e887b3e9a1b5: + description: "共 142 条12350 条/页跳至页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 142 条12350 条/页跳至页" + playwright: "page.getByText('共 142 条12350 条/页跳至页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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_page_2: + description: "2" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "2" + playwright: "page.getByText('2')" + + pagination_page_3: + description: "3" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "3" + playwright: "page.getByText('3')" + + pagination_nextPageBtn: + description: "下一页" + element_type: li + section: "main::pagination" + strategy: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + # --- main::panel:高级筛选 --- + drawer_el_e5a793e5908dInput: + description: "请输入" + element_type: text + section: "main::panel:高级筛选" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + drawer_el_e7a791e5aea4Select: + description: "请选择" + element_type: select + section: "main::panel:高级筛选" + strategy: label_text + value: "科室" + playwright: "page.getByText('科室', { exact: true })" + + drawer_el_e7a791e5aea4Input: + description: "科室" + element_type: search + section: "main::panel:高级筛选" + strategy: id + value: "form_item_deptId" + playwright: "page.locator('#form_item_deptId')" + + drawer_el_e8a792e889b2Select: + description: "请选择" + element_type: select + section: "main::panel:高级筛选" + strategy: label_text + value: "角色" + playwright: "page.getByText('角色', { exact: true })" + + drawer_el_e8a792e889b2Input: + description: "角色" + element_type: search + section: "main::panel:高级筛选" + strategy: id + value: "form_item_roleCode" + playwright: "page.locator('#form_item_roleCode')" + + drawer_statusSelect: + description: "状态" + element_type: select + section: "main::panel:高级筛选" + strategy: label_text + value: "状态" + playwright: "page.getByText('状态', { exact: true })" + + # --- main::panel:drawer:footer --- + # --- main::modal:编辑用户 --- + el_e8b4a6e58fb7Input: + description: "请输入" + element_type: text + section: "main::modal:编辑用户" + strategy: id + value: "form_item_userCode" + playwright: "page.locator('#form_item_userCode')" + + el_e5a793e5908dInput_4: + description: "请输入" + element_type: text + section: "main::modal:编辑用户" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + el_e88194e7b3bbe696b9e5bc8fInput: + description: "请输入" + element_type: text + section: "main::modal:编辑用户" + strategy: id + value: "form_item_tel" + playwright: "page.locator('#form_item_tel')" + + el_e58cbbe999a2e59198e5b7a5e7bc96e58fb7Input: + description: "请输入" + element_type: text + section: "main::modal:编辑用户" + strategy: id + value: "form_item_erpCode" + playwright: "page.locator('#form_item_erpCode')" + + el_e7adbee5908de59bbee78987: + description: "签名图片" + element_type: span + section: "main::modal:编辑用户" + strategy: role + role: button + name: "签名图片" + playwright: "page.getByRole('button', { name: '签名图片' })" + + el_e7a681e794a8: + description: "禁用" + element_type: radio + section: "main::modal:编辑用户" + strategy: text + value: "禁用" + playwright: "page.getByText('禁用')" + + enable: + description: "启用" + element_type: radio + section: "main::modal:编辑用户" + strategy: text + value: "启用" + playwright: "page.getByText('启用')" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:编辑用户" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + confirmBtn: + description: "确 定" + element_type: button + section: "main::modal:编辑用户" + strategy: role + role: button + name: "确 定" + playwright: "page.getByRole('button', { name: '确 定' })" diff --git a/locators/spd/flow-def.locator.ts b/locators/spd/flow-def.locator.ts new file mode 100644 index 0000000..be08d40 --- /dev/null +++ b/locators/spd/flow-def.locator.ts @@ -0,0 +1,37 @@ +import type { Locator, Page } from '@playwright/test'; + +import { WhFlowSharedLocators } from './wh-flow-shared.locator'; + +/** 审批流程设置。选择器来自 locators/spd/flow-def.locator.yaml */ +export class FlowDefLocators extends WhFlowSharedLocators { + readonly typeSearch: Locator; + readonly addFlowBtn: Locator; + readonly saveBtn: Locator; + readonly resetBtn: Locator; + readonly defaultFlow: Locator; + readonly customFlow: Locator; + readonly conditionCancel: Locator; + + constructor(page: Page) { + super(page); + this.typeSearch = page.getByPlaceholder('流程名称'); + this.addFlowBtn = page.getByRole('button', { name: '新增流程' }); + this.saveBtn = page.getByRole('button', { name: /保\s*存/ }); + this.resetBtn = page.getByRole('button', { name: /重\s*置/ }); + this.defaultFlow = page.getByText('默认审批流', { exact: true }).first(); + this.customFlow = page.getByText('自定义审批流', { exact: true }).first(); + this.conditionCancel = page.getByRole('button', { name: /取\s*消/ }); + } + + typeItems(): Locator { + return this.page.locator( + '.ant-layout-sider .ant-menu-item, .ant-layout-sider li, [class*="left"] .ant-menu-item, [class*="biz"] li', + ); + } + + selectedType(): Locator { + return this.page + .locator('.ant-layout-sider .ant-menu-item-selected, .ant-layout-sider .ant-menu-item-active, [class*="selected"]') + .first(); + } +} diff --git a/locators/spd/flow-def.locator.yaml b/locators/spd/flow-def.locator.yaml new file mode 100644 index 0000000..77068ad --- /dev/null +++ b/locators/spd/flow-def.locator.yaml @@ -0,0 +1,298 @@ +# Auto-generated from flow-def-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/flowSet/workflow/flowSet +# Scanned: 2026-10-20T09:05:57.781Z +# Page: 基础设置 > 流程设置 > 审批流程设置 + +page: flow-def +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/flowSet/workflow/flowSet + +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_el_e5aea1e689b9e6b581e7a88be8aebee7bdae: + description: "审批流程设置" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "审批流程设置" + playwright: "page.getByRole('menuitem', { name: '审批流程设置' })" + + menu_el_e5aea1e689b9e6b581e5ae9ee4be8b: + description: "审批流实例" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "审批流实例" + playwright: "page.getByRole('menuitem', { name: '审批流实例' })" + + el_e5aea1e689b9e6b581e7a88be8aebee7bdae: + description: "审批流程设置" + element_type: tab + section: "main::main" + strategy: text + value: "审批流程设置" + playwright: "page.getByText('审批流程设置')" + + tab_el_e5aea1e689b9e6b581e7a88be8aebee7bdae: + description: "审批流程设置" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/flowSet/workflow/flowSet" + playwright: "page.locator('#rc-tabs-0-tab-/basic/flowSet/workflow/flowSet')" + + el_e5aea1e689b9e6b581e7a88be8aebee7bdaeInput: + description: "流程名称" + element_type: text + section: "main::main" + strategy: placeholder + value: "流程名称" + playwright: "page.getByPlaceholder('流程名称')" + + el_e696b0e5a29ee6b581e7a88bBtn: + description: "新增流程" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新增流程" + playwright: "page.getByRole('button', { name: '新增流程' })" + + Input: + description: "1" + element_type: text + section: "main::main" + strategy: id + value: "form_item_pageList_0_modelName" + playwright: "page.locator('#form_item_pageList_0_modelName')" + + editBtn: + description: "编辑" + element_type: button + section: "main::main" + strategy: role + role: button + name: "编辑" + playwright: "page.getByRole('button', { name: '编辑' })" + + el_e588a0e999a4Btn: + description: "删除" + element_type: button + section: "main::main" + strategy: role + role: button + name: "删除" + playwright: "page.getByRole('button', { name: '删除' })" + + 2Input_2: + description: "2" + element_type: text + section: "main::main" + strategy: id + value: "form_item_pageList_1_modelName" + playwright: "page.locator('#form_item_pageList_1_modelName')" + + saveBtn: + 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: '重 置' })" + + form_item_pagelist_2_modelnameInput: + description: "text" + element_type: text + section: "main::main" + strategy: id + value: "form_item_pageList_2_modelName" + playwright: "page.locator('#form_item_pageList_2_modelName')" + + # --- main::modal:条件配置 --- + el_e6b7bbe58aa0Btn: + description: "添 加" + element_type: button + section: "main::modal:条件配置" + strategy: role + role: button + name: "添 加" + playwright: "page.getByRole('button', { name: '添 加' })" + + el_e698afe590a6e8b685e99990Select: + description: "是否超限" + element_type: select + section: "main::modal:条件配置" + strategy: text + value: "是否超限" + playwright: "page.getByText('是否超限')" + + form_item_tablelist_0_conditiontypeInput: + description: "search" + element_type: search + section: "main::modal:条件配置" + strategy: id + value: "form_item_tableList_0_conditionType" + playwright: "page.locator('#form_item_tableList_0_conditionType')" + + el_e698afSelect: + description: "是" + element_type: select + section: "main::modal:条件配置" + strategy: text + value: "是" + playwright: "page.getByText('是')" + + form_item_tablelist_0_conditiontextInput: + description: "search" + element_type: search + section: "main::modal:条件配置" + strategy: id + value: "form_item_tableList_0_conditionText" + playwright: "page.locator('#form_item_tableList_0_conditionText')" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:条件配置" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" diff --git a/locators/spd/flow-inst.locator.ts b/locators/spd/flow-inst.locator.ts new file mode 100644 index 0000000..7146b18 --- /dev/null +++ b/locators/spd/flow-inst.locator.ts @@ -0,0 +1,35 @@ +import type { Locator, Page } from '@playwright/test'; + +import { WhFlowSharedLocators } from './wh-flow-shared.locator'; + +/** 审批流实例。选择器来自 locators/spd/flow-inst.locator.yaml */ +export class FlowInstLocators extends WhFlowSharedLocators { + readonly orderNo: Locator; + readonly summary: Locator; + readonly startDate: Locator; + readonly endDate: Locator; + readonly queryBtn: Locator; + readonly resetBtn: Locator; + readonly detailBtn: Locator; + readonly backBtn: Locator; + + constructor(page: Page) { + super(page); + this.orderNo = page.locator('#form_item_orderNo').or(page.getByPlaceholder('单据编号')).first(); + this.summary = page.locator('#form_item_msg').or(page.getByPlaceholder('摘要')).first(); + this.startDate = page.locator('#form_item_dateValue').first(); + this.endDate = page.getByPlaceholder('截止时间').first(); + this.queryBtn = page.getByRole('button', { name: /查\s*询/ }); + this.resetBtn = page.getByRole('button', { name: /重\s*置/ }); + this.detailBtn = page.getByRole('button', { name: '详情' }); + this.backBtn = page.getByRole('button', { name: /返\s*回/ }); + } + + statusSelect(): Locator { + return this.page.locator('.ant-form-item').filter({ hasText: '审批状态' }).locator('.ant-select').first(); + } + + bizTypeSelect(): Locator { + return this.page.locator('.ant-form-item').filter({ hasText: '业务类型' }).locator('.ant-select').first(); + } +} diff --git a/locators/spd/flow-inst.locator.yaml b/locators/spd/flow-inst.locator.yaml new file mode 100644 index 0000000..1965fd1 --- /dev/null +++ b/locators/spd/flow-inst.locator.yaml @@ -0,0 +1,444 @@ +# Auto-generated from flow-inst-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/flowSet/workflow/flowCase +# Scanned: 2026-10-20T09:08:51.288Z +# Page: 基础设置 > 流程设置 > 审批流实例 +# Note: filter dropdown was opened during scan +# Note: detail view was opened during scan + +page: flow-inst +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/flowSet/workflow/flowCase + +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_el_e5aea1e689b9e6b581e7a88be8aebee7bdae: + description: "审批流程设置" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "审批流程设置" + playwright: "page.getByRole('menuitem', { name: '审批流程设置' })" + + menu_el_e5aea1e689b9e6b581e5ae9ee4be8b: + description: "审批流实例" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "审批流实例" + playwright: "page.getByRole('menuitem', { name: '审批流实例' })" + + el_e5aea1e689b9e6b581e5ae9ee4be8b: + description: "审批流实例" + element_type: tab + section: "main::main" + strategy: text + value: "审批流实例" + playwright: "page.getByText('审批流实例')" + + tab_el_e5aea1e689b9e6b581e5ae9ee4be8b: + description: "审批流实例" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/flowSet/workflow/flowCase" + playwright: "page.locator('#rc-tabs-0-tab-/basic/flowSet/workflow/flowCase')" + + documentNo: + description: "单据编号" + element_type: text + section: "main::main" + strategy: id + value: "form_item_orderNo" + playwright: "page.locator('#form_item_orderNo')" + + el_e5aea1e689b9e78ab6e68081Select: + description: "审批状态" + element_type: select + section: "main::main" + strategy: label_text + value: "审批状态" + playwright: "page.getByText('审批状态', { exact: true })" + + el_e5aea1e689b9e78ab6e68081Input: + description: "审批状态" + element_type: search + section: "main::main" + strategy: id + value: "form_item_procState" + playwright: "page.locator('#form_item_procState')" + + el_e58f91e8b5b7e697b6e997b4DateRange: + description: "发起时间" + element_type: datepicker + section: "main::main" + strategy: label_text + value: "发起时间" + playwright: "page.getByText('发起时间', { exact: true })" + + el_e58f91e8b5b7e697b6e997b4Input: + description: "开始时间" + element_type: text + section: "main::main" + strategy: id + value: "form_item_dateValue" + playwright: "page.locator('#form_item_dateValue')" + + el_e58f91e8b5b7e697b6e997b4Input_2: + description: "截止时间" + element_type: text + section: "main::main" + strategy: placeholder + value: "截止时间" + playwright: "page.getByPlaceholder('截止时间')" + + el_e4b89ae58aa1e7b1bbe59e8bSelect: + description: "业务类型" + element_type: select + section: "main::main" + strategy: label_text + value: "业务类型" + playwright: "page.getByText('业务类型', { exact: true })" + + el_e4b89ae58aa1e7b1bbe59e8bInput: + description: "业务类型" + element_type: search + section: "main::main" + strategy: id + value: "form_item_businessType" + playwright: "page.locator('#form_item_businessType')" + + el_e69198e8a681Input: + description: "摘要" + element_type: text + section: "main::main" + strategy: id + value: "form_item_msg" + playwright: "page.locator('#form_item_msg')" + + 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: '重 置' })" + + tableDensity: + description: "column-height" + element_type: span + section: "main::main" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + detailBtn: + description: "详情" + element_type: button + section: "main::main" + strategy: role + role: button + name: "详情" + playwright: "page.getByRole('button', { name: '详情' })" + + el_e58f91e8b5b7e5aea1e689b9e8928be58d8ee58f91e8b5b7e4babae5a484e79086e697b6e997b4efbc9a323032362d31302d323031363a34383a353028e88097e697b630e5b08fe697b630e58886e9929f30e7a79229e5aea1e689b9e6848fe8a781efbc9ae28094: + description: "发起审批蒋华发起人处理时间:2026-10-20 16:48:50(耗时0小时0分钟0秒)审批意见:—" + element_type: div + section: "main::main" + strategy: role + role: button + name: "发起审批蒋华发起人处理时间:2026-10-20 16:48:50(耗时0小时0分钟0秒)审批意见:—" + playwright: "page.getByRole('button', { name: '发起审批蒋华发起人处理时间:2026-10-20 16:48:50(耗时0小时0分钟0秒)审批意见:—' })" + + el_e5aea1e689b9e9809ae8bf87e8928be58d8ee8bf90e890a5e4b8bbe7aea1e5a484e79086e697b6e997b4efbc9a323032362d31302d323031363a34383a353028e88097e697b630e5b08fe697b630e58886e9929f31e7a79229e5aea1e689b9e6848fe8a781efbc9ae887aae58aa8e5aea1e689b9: + description: "审批通过蒋华运营主管处理时间:2026-10-20 16:48:50(耗时0小时0分钟1秒)审批意见:自动审批" + element_type: div + section: "main::main" + strategy: role + role: button + name: "审批通过蒋华运营主管处理时间:2026-10-20 16:48:50(耗时0小时0分钟1秒)审批意见:自动审批" + playwright: "page.getByRole('button', { name: '审批通过蒋华运营主管处理时间:2026-10-20 16:48:50(耗时0小时0分钟1秒)审批意见:自动审批' })" + + # --- main::pagination --- + pagination_el_e585b132333632e69da13132333435e280a2e280a2e280a234383530e69da12fe9a1b5e8b7b3e887b3e9a1b5: + description: "共 2362 条12345•••4850 条/页跳至页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 2362 条12345•••4850 条/页跳至页" + playwright: "page.getByText('共 2362 条12345•••4850 条/页跳至页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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_page_2: + description: "2" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "2" + playwright: "page.getByText('2')" + + pagination_page_3: + description: "3" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "3" + playwright: "page.getByText('3')" + + pagination_page_4: + description: "4" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "4" + playwright: "page.getByText('4')" + + pagination_page_5: + description: "5" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "5" + playwright: "page.getByText('5')" + + pagination_el_e59091e5908e35e9a1b5: + description: "向后 5 页" + element_type: li + section: "main::pagination" + strategy: text + value: "向后 5 页" + playwright: "page.getByText('向后 5 页')" + + pagination_page_48: + description: "48" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "48" + playwright: "page.getByText('48')" + + pagination_nextPageBtn: + description: "下一页" + element_type: li + section: "main::pagination" + strategy: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + # --- main::detail:采购计划详情 --- + backBtn: + description: "返 回" + element_type: button + section: "main::detail:采购计划详情" + strategy: role + role: button + name: "返 回" + playwright: "page.getByRole('button', { name: '返 回' })" + + tableDensity_2: + description: "column-height" + element_type: span + section: "main::detail:采购计划详情" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + el_e585b132e69da1313530e69da12fe9a1b5: + description: "共 2 条150 条/页" + element_type: ul + section: "main::detail:采购计划详情" + strategy: text + value: "共 2 条150 条/页" + playwright: "page.getByText('共 2 条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 条/页')" diff --git a/locators/spd/surgery-base-pkg.locator.yaml b/locators/spd/surgery-base-pkg.locator.yaml new file mode 100644 index 0000000..b6d5821 --- /dev/null +++ b/locators/spd/surgery-base-pkg.locator.yaml @@ -0,0 +1,636 @@ +# Auto-generated from surgery-base-pkg-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/operationSetting/baseMaintenance +# Scanned: 2026-10-20T08:47:17.301Z +# Page: 基础设置 > 手术套包设置 > 基础包维护 +# Note: dialog/modal was opened during scan + +page: surgery-base-pkg +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/operationSetting/baseMaintenance + +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_basePkgMaintenance: + description: "基础包维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "基础包维护" + playwright: "page.getByRole('menuitem', { name: '基础包维护' })" + + menu_operTypeMaintenance: + description: "术种维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "术种维护" + playwright: "page.getByRole('menuitem', { name: '术种维护' })" + + menu_stdPkgMaintenance: + description: "标准套包维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "标准套包维护" + playwright: "page.getByRole('menuitem', { name: '标准套包维护' })" + + basePkgMaintenance: + description: "基础包维护" + element_type: tab + section: "main::main" + strategy: text + value: "基础包维护" + playwright: "page.getByText('基础包维护')" + + tab_basePkgMaintenance: + description: "基础包维护" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/operationSetting/baseMaintenance" + playwright: "page.locator('#rc-tabs-0-tab-/basic/operationSetting/baseMaintenance')" + + addCategoryBtn: + description: "新增分类" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新增分类" + playwright: "page.getByRole('button', { name: '新增分类' })" + + addBasePkgBtn: + description: "新增基础包" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新增基础包" + playwright: "page.getByRole('button', { name: '新增基础包' })" + + categorySearchInput: + description: "请输入分类名称" + element_type: text + section: "main::main" + strategy: placeholder + value: "请输入分类名称" + playwright: "page.getByPlaceholder('请输入分类名称')" + + tree_node_e7b1bb313231: + description: "类12121313" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类12121313" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类12121313\\s*$/ })" + + tree_node_e58c85323131: + description: "包2112412" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "包2112412" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*包2112412\\s*$/ })" + + tree_node_e58c85415554: + description: "包AUTO-BASE-1792473862012-33" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "包AUTO-BASE-1792473862012-33" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*包AUTO-BASE-1792473862012-33\\s*$/ })" + + tree_el_e58c854155544f2d424153452d313739323437333839313334382d313539_2: + description: "包AUTO-BASE-1792473891348-159" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "包AUTO-BASE-1792473891348-159" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*包AUTO-BASE-1792473891348-159\\s*$/ })" + + tree_el_e58c854155544f2d424153452d313739323437333933343839312d393337_2: + description: "包AUTO-BASE-1792473934891-937" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "包AUTO-BASE-1792473934891-937" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*包AUTO-BASE-1792473934891-937\\s*$/ })" + + tree_node_e7b1bb323032: + description: "类20240324" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类20240324" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类20240324\\s*$/ })" + + tree_el_e7b1bb3230323530353136_2: + description: "类20250516" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类20250516" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类20250516\\s*$/ })" + + tree_el_e7b1bb3230323630373039_2: + description: "类20260709" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类20260709" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类20260709\\s*$/ })" + + tree_el_e7b1bb32303236e59b9ee5bd92e6b58be8af95_2: + description: "类2026回归测试" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类2026回归测试" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类2026回归测试\\s*$/ })" + + tree_node_e7b1bb323531: + description: "类2512回归" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类2512回归" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类2512回归\\s*$/ })" + + tree_node_e7b1bb544553: + description: "类TEST" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类TEST" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类TEST\\s*$/ })" + + tree_node_e7b1bb6a6d5f: + description: "类jm_基础包分类01" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类jm_基础包分类01" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类jm_基础包分类01\\s*$/ })" + + tree_el_e7b1bb6a6d5fe59fbae7a180e58c85e58886e7b1bb3032_2: + description: "类jm_基础包分类02" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类jm_基础包分类02" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类jm_基础包分类02\\s*$/ })" + + tree_node_e7b1bbe58fa3: + description: "类口腔科基础包分类1" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类口腔科基础包分类1" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类口腔科基础包分类1\\s*$/ })" + + tree_node_e7b1bbe59fba: + description: "类基础包0613" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类基础包0613" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类基础包0613\\s*$/ })" + + tree_el_e7b1bbe59fbae7a180e58c8531_2: + description: "类基础包1" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类基础包1" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类基础包1\\s*$/ })" + + tree_node_e7b1bbe5a496: + description: "类外科手术" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类外科手术" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类外科手术\\s*$/ })" + + tree_node_e7b1bbe5b091: + description: "类少" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类少" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类少\\s*$/ })" + + tree_node_e7b1bbe6898b: + description: "类手术分类" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类手术分类" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类手术分类\\s*$/ })" + + tree_node_e7b1bbe69588: + description: "类效期" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类效期" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类效期\\s*$/ })" + + tree_node_e7b1bbe88386: + description: "类胆囊手术" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类胆囊手术" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类胆囊手术\\s*$/ })" + + tree_node_e7b1bbe9ab98: + description: "类高值包" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类高值包" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类高值包\\s*$/ })" + + tree_node_e7b1bb31: + description: "类1" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类1" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类1\\s*$/ })" + + tree_node_e7b1bb33: + description: "类3" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类3" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类3\\s*$/ })" + + tree_node_e7b1bb6a6d37: + description: "类jm73" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类jm73" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类jm73\\s*$/ })" + + tree_node_e7b1bbe5bf83: + description: "类心内科手术" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类心内科手术" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类心内科手术\\s*$/ })" + + tree_node_e7b1bbe9aaa8: + description: "类骨科手术" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类骨科手术" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类骨科手术\\s*$/ })" + + productName: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_keyword" + playwright: "page.locator('#table_search_form_keyword')" + + specModel: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_goodsSpec" + playwright: "page.locator('#table_search_form_goodsSpec')" + + manufacturer: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_goodsMfrsName" + playwright: "page.locator('#table_search_form_goodsMfrsName')" + + 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: '展开' })" + + addProductBtn: + 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: '编辑' })" + + removeBtn: + description: "移除" + element_type: button + section: "main::main" + strategy: role + role: button + name: "移除" + playwright: "page.getByRole('button', { name: '移除' })" + + # --- main::pagination --- + pagination_el_e585b133e69da1313530e69da12fe9a1b5: + description: "共 3 条150 条/页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 3 条150 条/页" + playwright: "page.getByText('共 3 条150 条/页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + # --- main::modal:新增基础包 --- + basePkgName: + description: "请输入" + element_type: text + section: "main::modal:新增基础包" + strategy: id + value: "form_item_pkgName" + playwright: "page.locator('#form_item_pkgName')" + + basePkgCategorySelect: + description: "请选择" + element_type: select + section: "main::modal:新增基础包" + strategy: label_text + value: "所属分类" + playwright: "page.getByText('所属分类', { exact: true })" + + basePkgCategory: + description: "所属分类" + element_type: search + section: "main::modal:新增基础包" + strategy: id + value: "form_item_kindId" + playwright: "page.locator('#form_item_kindId')" + + basePkgCode: + description: "请输入" + element_type: text + section: "main::modal:新增基础包" + strategy: id + value: "form_item_pkgCode" + playwright: "page.locator('#form_item_pkgCode')" + + remark: + description: "请输入" + element_type: textarea + section: "main::modal:新增基础包" + strategy: id + value: "form_item_remark" + playwright: "page.locator('#form_item_remark')" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:新增基础包" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + confirmBtn: + description: "确 认" + element_type: button + section: "main::modal:新增基础包" + strategy: role + role: button + name: "确 认" + playwright: "page.getByRole('button', { name: '确 认' })" + + # --- main::modal:新增分类 --- + basePkgCategoryName: + description: "请输入" + element_type: text + section: "main::modal:新增分类" + strategy: id + value: "form_item_kindName" + playwright: "page.locator('#form_item_kindName')" diff --git a/locators/spd/surgery-oper-type.locator.yaml b/locators/spd/surgery-oper-type.locator.yaml new file mode 100644 index 0000000..92e169e --- /dev/null +++ b/locators/spd/surgery-oper-type.locator.yaml @@ -0,0 +1,335 @@ +# Auto-generated from surgery-oper-type-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/operationSetting/typeMaintenance +# Scanned: 2026-10-20T08:47:41.945Z +# Page: 基础设置 > 手术套包设置 > 术种维护 +# Note: filter dropdown was opened during scan +# Note: dialog/modal was opened during scan + +page: surgery-oper-type +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/operationSetting/typeMaintenance + +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_basePkgMaintenance: + description: "基础包维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "基础包维护" + playwright: "page.getByRole('menuitem', { name: '基础包维护' })" + + menu_operTypeMaintenance: + description: "术种维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "术种维护" + playwright: "page.getByRole('menuitem', { name: '术种维护' })" + + menu_stdPkgMaintenance: + description: "标准套包维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "标准套包维护" + playwright: "page.getByRole('menuitem', { name: '标准套包维护' })" + + operTypeMaintenance: + description: "术种维护" + element_type: tab + section: "main::main" + strategy: text + value: "术种维护" + playwright: "page.getByText('术种维护')" + + tab_operTypeMaintenance: + description: "术种维护" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/operationSetting/typeMaintenance" + playwright: "page.locator('#rc-tabs-0-tab-/basic/operationSetting/typeMaintenance')" + + addBtn: + description: "新增" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新增" + playwright: "page.getByRole('button', { name: '新增' })" + + operTypeCategoryInput: + description: "请输入术种分类名称" + element_type: text + section: "main::main" + strategy: placeholder + value: "请输入术种分类名称" + playwright: "page.getByPlaceholder('请输入术种分类名称')" + + operationName: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_operationName" + playwright: "page.locator('#table_search_form_operationName')" + + diseaseName: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_disease" + playwright: "page.locator('#table_search_form_disease')" + + 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: '重 置' })" + + addOperTypeBtn: + 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: '编辑' })" + + enableBtn: + description: "启用" + element_type: button + section: "main::main" + strategy: role + role: button + name: "启用" + playwright: "page.getByRole('button', { name: '启用' })" + + # --- main::pagination --- + pagination_el_e585b132e69da1313530e69da12fe9a1b5: + description: "共 2 条150 条/页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 2 条150 条/页" + playwright: "page.getByText('共 2 条150 条/页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + pagination_pageSizeSelect_2: + description: "50 条/页205010020 条/页50 条/页100 条/页200 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页205010020 条/页50 条/页100 条/页200 条/页" + playwright: "page.getByText('50 条/页205010020 条/页50 条/页100 条/页200 条/页')" + + # --- main::modal:分类维护 --- + operTypeCategoryName: + description: "请输入" + element_type: text + section: "main::modal:分类维护" + strategy: id + value: "basic_kindName" + playwright: "page.locator('#basic_kindName')" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:分类维护" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + saveBtn: + description: "保 存" + element_type: button + section: "main::modal:分类维护" + strategy: role + role: button + name: "保 存" + playwright: "page.getByRole('button', { name: '保 存' })" diff --git a/locators/spd/surgery-shared.locator.ts b/locators/spd/surgery-shared.locator.ts new file mode 100644 index 0000000..4874578 --- /dev/null +++ b/locators/spd/surgery-shared.locator.ts @@ -0,0 +1,103 @@ +import type { Locator, Page } from '@playwright/test'; + +/** + * 基础设置 · 手术套包设置(基础包维护 / 术种维护 / 标准套包维护)。 + * 定位来源:locators/spd/surgery-*.locator.yaml(由交互元素扫描提炼)。 + * 三页同组页签 + 左树 + 右列表 + 分页 + 弹窗,共用类只放稳定定位。 + */ +export class SurgerySharedLocators { + readonly menuBasic: Locator; + readonly surgerySubmenuTitle: Locator; + + readonly leftTree: Locator; + readonly treeSearchInput: Locator; + + readonly tableHeader: Locator; + readonly tableBody: Locator; + readonly empty: Locator; + + readonly pagination: Locator; + readonly paginationTotal: Locator; + readonly paginationActive: Locator; + readonly paginationPrev: Locator; + readonly paginationNext: Locator; + + readonly dialog: Locator; + readonly formError: Locator; + + constructor(protected readonly page: Page) { + this.menuBasic = page.getByRole('menuitem', { name: '基础设置' }); + this.surgerySubmenuTitle = page + .locator('.ant-menu-submenu-title') + .filter({ hasText: /^\s*手术套包设置\s*$/ }) + .first(); + + this.leftTree = page.locator('.ant-tree').first(); + this.treeSearchInput = page.locator('.ant-tree ~ * input, .ant-tree input').first(); + + this.tableHeader = page.locator('.ant-table-thead th'); + this.tableBody = page.locator('.ant-table-tbody'); + this.empty = page.locator('.ant-empty, .ant-empty-description').first(); + + this.pagination = page.locator('.ant-pagination').first(); + this.paginationTotal = page.locator('.ant-pagination-total-text').first(); + this.paginationActive = page.locator('.ant-pagination-item-active').first(); + this.paginationPrev = page.locator('.ant-pagination-prev').first(); + this.paginationNext = page.locator('.ant-pagination-next').first(); + + this.dialog = page.locator('.ant-modal-content, .ant-modal-wrap .ant-modal, .ant-drawer-open').last(); + this.formError = page.locator('.ant-form-item-explain-error, .ant-message-error, .ant-notification-notice'); + } + + /** 侧栏叶子菜单(基础包维护/术种维护/标准套包维护,既有 role=menuitem 也有 .ant-menu-item)。 */ + menuLeaf(name: string): Locator { + const pattern = new RegExp(`^\\s*${name}\\s*$`); + return this.page + .getByRole('menuitem', { name: pattern }) + .or(this.page.locator('.ant-menu-item').filter({ hasText: pattern })) + .first(); + } + + /** 同组页签(URL 型 rc-tabs 或文本 Tab 均覆盖)。 */ + tab(name: string): Locator { + const pattern = new RegExp(name); + return this.page + .locator('.ant-tabs-tab') + .filter({ hasText: pattern }) + .first() + .or(this.page.getByRole('tab', { name: pattern }).first()) + .or(this.page.getByText(pattern).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 }); + } + + /** 树节点:按可见文本包含匹配。 */ + treeNode(text: string): Locator { + return this.leftTree.locator('.ant-tree-node-content-wrapper').filter({ hasText: text }).first(); + } + + treeNodes(): Locator { + return this.leftTree.locator('.ant-tree-node-content-wrapper'); + } + + /** 弹窗内表单控件按 id 定位(CSV 明确给出 #form_item_xxx / #basic_kindName)。 */ + byId(id: string): Locator { + return this.page.locator(id.startsWith('#') ? id : `#${id}`).first(); + } + + /** 弹窗内确定类按钮(「确 认」「确 定」「保 存」「确定(N)」)。 */ + confirmLikeButton(root: Locator | Page = this.dialog): Locator { + return root + .getByRole('button') + .filter({ hasText: /确\s*[认定]|保\s*存/ }) + .first(); + } + + cancelLikeButton(root: Locator | Page = this.dialog): Locator { + return root.getByRole('button', { name: /取\s*消/ }).first(); + } +} diff --git a/locators/spd/surgery-std-pkg.locator.yaml b/locators/spd/surgery-std-pkg.locator.yaml new file mode 100644 index 0000000..5378749 --- /dev/null +++ b/locators/spd/surgery-std-pkg.locator.yaml @@ -0,0 +1,688 @@ +# Auto-generated from surgery-std-pkg-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/operationSetting/standardMaintenance +# Scanned: 2026-10-20T08:47:54.322Z +# Page: 基础设置 > 手术套包设置 > 标准套包维护 + +page: surgery-std-pkg +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/operationSetting/standardMaintenance + +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_basePkgMaintenance: + description: "基础包维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "基础包维护" + playwright: "page.getByRole('menuitem', { name: '基础包维护' })" + + menu_operTypeMaintenance: + description: "术种维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "术种维护" + playwright: "page.getByRole('menuitem', { name: '术种维护' })" + + menu_stdPkgMaintenance: + description: "标准套包维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "标准套包维护" + playwright: "page.getByRole('menuitem', { name: '标准套包维护' })" + + stdPkgMaintenance: + description: "标准套包维护" + element_type: tab + section: "main::main" + strategy: text + value: "标准套包维护" + playwright: "page.getByText('标准套包维护')" + + tab_stdPkgMaintenance: + description: "标准套包维护" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/operationSetting/standardMaintenance" + playwright: "page.locator('#rc-tabs-0-tab-/basic/operationSetting/standardMaintenance')" + + addBtn: + description: "新 增" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新 增" + playwright: "page.getByRole('button', { name: '新 增' })" + + stdPkgTreeSearchInput: + description: "请输入术种分类名称、术种名称、标准套包名称" + element_type: text + section: "main::main" + strategy: placeholder + value: "请输入术种分类名称、术种名称、标准套包名称" + playwright: "page.getByPlaceholder('请输入术种分类名称、术种名称、标准套包名称')" + + tree_node_e7b1bbe69caa: + description: "类未建分类(2)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类未建分类(2)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类未建分类(2)\\s*$/ })" + + tree_node_e69caf313128: + description: "术11(4)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "术11(4)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*术11(4)\\s*$/ })" + + tree_node_e58c857171: + description: "包qq" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "包qq" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*包qq\\s*$/ })" + + tree_node_e58c857474: + description: "包tt" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "包tt" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*包tt\\s*$/ })" + + tree_node_e58c85e695a3: + description: "包散货包" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "包散货包" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*包散货包\\s*$/ })" + + tree_node_e58c85e6b7b7: + description: "包混包" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "包混包" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*包混包\\s*$/ })" + + tree_node_e69caf313231: + description: "术1212(0)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "术1212(0)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*术1212(0)\\s*$/ })" + + tree_node_e7b1bb313334: + description: "类134(2)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类134(2)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类134(2)\\s*$/ })" + + tree_node_e7b1bb323032: + description: "类20240325(1)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类20240325(1)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类20240325(1)\\s*$/ })" + + tree_el_e7b1bb3230323530353136283129_2: + description: "类20250516(1)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类20250516(1)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类20250516(1)\\s*$/ })" + + tree_el_e7b1bb32303236283129_2: + description: "类2026(1)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类2026(1)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类2026(1)\\s*$/ })" + + tree_node_e7b1bb323531: + description: "类2512回归(1)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类2512回归(1)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类2512回归(1)\\s*$/ })" + + tree_node_e7b1bb414141: + description: "类AAAAA(1)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类AAAAA(1)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类AAAAA(1)\\s*$/ })" + + tree_node_e7b1bb544553: + description: "类TEST(1)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类TEST(1)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类TEST(1)\\s*$/ })" + + tree_node_e7b1bb6a6d37: + description: "类jm73(2)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类jm73(2)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类jm73(2)\\s*$/ })" + + tree_node_e7b1bb6a6d5f: + description: "类jm_术种分类01(4)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类jm_术种分类01(4)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类jm_术种分类01(4)\\s*$/ })" + + tree_node_e7b1bbe58fa3: + description: "类口腔内科手术(1)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类口腔内科手术(1)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类口腔内科手术(1)\\s*$/ })" + + tree_el_e7b1bbe58fa3e88594e5a496e7a791e6898be69caf283329_2: + description: "类口腔外科手术(3)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类口腔外科手术(3)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类口腔外科手术(3)\\s*$/ })" + + tree_node_e7b1bbe69caf: + description: "类术种0613(1)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类术种0613(1)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类术种0613(1)\\s*$/ })" + + tree_node_e7b1bbe883b8: + description: "类胸外科(1)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类胸外科(1)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类胸外科(1)\\s*$/ })" + + tree_node_e7b1bbe9aaa8: + description: "类骨外科(8)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类骨外科(8)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类骨外科(8)\\s*$/ })" + + tree_el_e7b1bbe9aaa8e7a791283229_2: + description: "类骨科(2)" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "类骨科(2)" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类骨科(2)\\s*$/ })" + + productName: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_keyword" + playwright: "page.locator('#table_search_form_keyword')" + + specModel: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_goodsSpec" + playwright: "page.locator('#table_search_form_goodsSpec')" + + manufacturer: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_goodsMfrsName" + playwright: "page.locator('#table_search_form_goodsMfrsName')" + + 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: '展开' })" + + addBasePkgBtn: + description: "添加基础包" + element_type: button + section: "main::main" + strategy: role + role: button + name: "添加基础包" + playwright: "page.getByRole('button', { name: '添加基础包' })" + + addLoosePartBtn: + description: "添加散件" + element_type: button + section: "main::main" + strategy: role + role: button + name: "添加散件" + playwright: "page.getByRole('button', { name: '添加散件' })" + + productOverviewBtn: + 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: '编辑' })" + + removeBtn: + description: "移除" + element_type: button + section: "main::main" + strategy: role + role: button + name: "移除" + playwright: "page.getByRole('button', { name: '移除' })" + + # --- main::modal:添加基础包 --- + addBasePkgSearchInput: + description: "请输入分类/基础包名称" + element_type: text + section: "main::modal:添加基础包" + strategy: placeholder + value: "请输入分类/基础包名称" + playwright: "page.getByPlaceholder('请输入分类/基础包名称')" + + tree_node_e7b1bb313231: + description: "类12121313" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类12121313" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类12121313\\s*$/ })" + + tree_node_e58c85415554: + description: "包AUTO-BASE-1792473862012-33" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "包AUTO-BASE-1792473862012-33" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*包AUTO-BASE-1792473862012-33\\s*$/ })" + + Input: + description: "1" + element_type: text + section: "main::modal:添加基础包" + strategy: role + role: spinbutton + name: "1" + playwright: "page.getByRole('spinbutton', { name: '1' })" + + tree_el_e58c854155544f2d424153452d313739323437333839313334382d313539_2: + description: "包AUTO-BASE-1792473891348-159" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "包AUTO-BASE-1792473891348-159" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*包AUTO-BASE-1792473891348-159\\s*$/ })" + + tree_el_e58c854155544f2d424153452d313739323437333933343839312d393337_2: + description: "包AUTO-BASE-1792473934891-937" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "包AUTO-BASE-1792473934891-937" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*包AUTO-BASE-1792473934891-937\\s*$/ })" + + tree_el_e7b1bb3230323430333234_2: + description: "类20240324" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类20240324" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类20240324\\s*$/ })" + + tree_el_e7b1bb3230323530353136_2: + description: "类20250516" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类20250516" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类20250516\\s*$/ })" + + tree_el_e7b1bb32303236e59b9ee5bd92e6b58be8af95_2: + description: "类2026回归测试" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类2026回归测试" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类2026回归测试\\s*$/ })" + + tree_el_e7b1bb32353132e59b9ee5bd92_2: + description: "类2512回归" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类2512回归" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类2512回归\\s*$/ })" + + tree_el_e7b1bb54455354_2: + description: "类TEST" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类TEST" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类TEST\\s*$/ })" + + tree_el_e7b1bb6a6d5fe59fbae7a180e58c85e58886e7b1bb3031_2: + description: "类jm_基础包分类01" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类jm_基础包分类01" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类jm_基础包分类01\\s*$/ })" + + tree_el_e7b1bb6a6d5fe59fbae7a180e58c85e58886e7b1bb3032_2: + description: "类jm_基础包分类02" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类jm_基础包分类02" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类jm_基础包分类02\\s*$/ })" + + tree_el_e7b1bbe58fa3e88594e7a791e59fbae7a180e58c85e58886e7b1bb31_2: + description: "类口腔科基础包分类1" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类口腔科基础包分类1" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类口腔科基础包分类1\\s*$/ })" + + tree_node_e7b1bbe59fba: + description: "类基础包0613" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类基础包0613" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类基础包0613\\s*$/ })" + + tree_node_e7b1bbe5a496: + description: "类外科手术" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类外科手术" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类外科手术\\s*$/ })" + + tree_node_e7b1bbe69588: + description: "类效期" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类效期" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类效期\\s*$/ })" + + tree_node_e7b1bbe88386: + description: "类胆囊手术" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类胆囊手术" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类胆囊手术\\s*$/ })" + + tree_node_e7b1bbe9ab98: + description: "类高值包" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类高值包" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类高值包\\s*$/ })" + + tree_el_e7b1bb6a6d3733_2: + description: "类jm73" + element_type: tree_title + section: "main::modal:添加基础包" + strategy: tree_node + value: "类jm73" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*类jm73\\s*$/ })" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:添加基础包" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + confirmBtn: + description: "确定(0)" + element_type: button + section: "main::modal:添加基础包" + strategy: role + role: button + name: "确定(0)" + playwright: "page.getByRole('button', { name: '确定(0)' })" diff --git a/locators/spd/vendor-auth-final.locator.yaml b/locators/spd/vendor-auth-final.locator.yaml new file mode 100644 index 0000000..a86cf0a --- /dev/null +++ b/locators/spd/vendor-auth-final.locator.yaml @@ -0,0 +1,318 @@ +# Auto-generated from vendor-auth-final-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/authAuditFinal +# Scanned: 2026-10-20T08:41:58.554Z +# Page: 基础设置 > 厂商管理 > 授权变更终审 +# Note: filter dropdown was opened during scan + +page: vendor-auth-final +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/authAuditFinal + +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_vendorQuery: + description: "厂商查询" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "厂商查询" + playwright: "page.getByRole('menuitem', { name: '厂商查询' })" + + menu_vendorQual: + description: "供应商资质管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商资质管理" + playwright: "page.getByRole('menuitem', { name: '供应商资质管理' })" + + menu_authFirst: + description: "授权变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更初审" + playwright: "page.getByRole('menuitem', { name: '授权变更初审' })" + + menu_certFirst: + description: "注册证变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更初审" + playwright: "page.getByRole('menuitem', { name: '注册证变更初审' })" + + menu_certFinal: + description: "注册证变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更终审" + playwright: "page.getByRole('menuitem', { name: '注册证变更终审' })" + + menu_authFinal: + description: "授权变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更终审" + playwright: "page.getByRole('menuitem', { name: '授权变更终审' })" + + menu_certDiff: + description: "注册证差异对比" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证差异对比" + playwright: "page.getByRole('menuitem', { name: '注册证差异对比' })" + + menu_evalScheme: + description: "评价方案维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价方案维护" + playwright: "page.getByRole('menuitem', { name: '评价方案维护' })" + + menu_evalIndicator: + description: "评价指标维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价指标维护" + playwright: "page.getByRole('menuitem', { name: '评价指标维护' })" + + menu_vendorHoliday: + description: "供应商节假日管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商节假日管理" + playwright: "page.getByRole('menuitem', { name: '供应商节假日管理' })" + + authFinal: + description: "授权变更终审" + element_type: tab + section: "main::main" + strategy: text + value: "授权变更终审" + playwright: "page.getByText('授权变更终审')" + + tab_authFinal: + description: "授权变更终审" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/authAuditFinal" + playwright: "page.locator('#rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/authAuditFinal')" + + 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('结束日期')" + + documentNo: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_orderNo" + playwright: "page.locator('#table_search_form_orderNo')" + + el_e99480e594aee59586Select: + description: "请选择" + element_type: select + section: "main::main" + strategy: label_text + value: "销售商" + playwright: "page.getByText('销售商', { exact: true })" + + supplierId: + description: "销售商" + element_type: search + section: "main::main" + strategy: id + value: "table_search_form_provId" + playwright: "page.locator('#table_search_form_provId')" + + 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: '展开' })" diff --git a/locators/spd/vendor-auth-first.locator.yaml b/locators/spd/vendor-auth-first.locator.yaml new file mode 100644 index 0000000..ce73903 --- /dev/null +++ b/locators/spd/vendor-auth-first.locator.yaml @@ -0,0 +1,318 @@ +# Auto-generated from vendor-auth-first-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/authAudit +# Scanned: 2026-10-20T08:41:49.683Z +# Page: 基础设置 > 厂商管理 > 授权变更初审 +# Note: filter dropdown was opened during scan + +page: vendor-auth-first +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/authAudit + +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_vendorQuery: + description: "厂商查询" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "厂商查询" + playwright: "page.getByRole('menuitem', { name: '厂商查询' })" + + menu_vendorQual: + description: "供应商资质管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商资质管理" + playwright: "page.getByRole('menuitem', { name: '供应商资质管理' })" + + menu_authFirst: + description: "授权变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更初审" + playwright: "page.getByRole('menuitem', { name: '授权变更初审' })" + + menu_certFirst: + description: "注册证变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更初审" + playwright: "page.getByRole('menuitem', { name: '注册证变更初审' })" + + menu_certFinal: + description: "注册证变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更终审" + playwright: "page.getByRole('menuitem', { name: '注册证变更终审' })" + + menu_authFinal: + description: "授权变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更终审" + playwright: "page.getByRole('menuitem', { name: '授权变更终审' })" + + menu_certDiff: + description: "注册证差异对比" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证差异对比" + playwright: "page.getByRole('menuitem', { name: '注册证差异对比' })" + + menu_evalScheme: + description: "评价方案维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价方案维护" + playwright: "page.getByRole('menuitem', { name: '评价方案维护' })" + + menu_evalIndicator: + description: "评价指标维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价指标维护" + playwright: "page.getByRole('menuitem', { name: '评价指标维护' })" + + menu_vendorHoliday: + description: "供应商节假日管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商节假日管理" + playwright: "page.getByRole('menuitem', { name: '供应商节假日管理' })" + + authFirst: + description: "授权变更初审" + element_type: tab + section: "main::main" + strategy: text + value: "授权变更初审" + playwright: "page.getByText('授权变更初审')" + + tab_authFirst: + description: "授权变更初审" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/authAudit" + playwright: "page.locator('#rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/authAudit')" + + 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('结束日期')" + + documentNo: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_orderNo" + playwright: "page.locator('#table_search_form_orderNo')" + + el_e99480e594aee59586Select: + description: "请选择" + element_type: select + section: "main::main" + strategy: label_text + value: "销售商" + playwright: "page.getByText('销售商', { exact: true })" + + supplierId: + description: "销售商" + element_type: search + section: "main::main" + strategy: id + value: "table_search_form_provId" + playwright: "page.locator('#table_search_form_provId')" + + 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: '展开' })" diff --git a/locators/spd/vendor-cert-diff.locator.yaml b/locators/spd/vendor-cert-diff.locator.yaml new file mode 100644 index 0000000..c456b08 --- /dev/null +++ b/locators/spd/vendor-cert-diff.locator.yaml @@ -0,0 +1,408 @@ +# Auto-generated from vendor-cert-diff-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/scjs/yc/certCompare +# Scanned: 2026-10-20T08:43:43.547Z +# Page: 基础设置 > 厂商管理 > 注册证差异对比 + +page: vendor-cert-diff +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/scjs/yc/certCompare + +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_vendorQuery: + description: "厂商查询" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "厂商查询" + playwright: "page.getByRole('menuitem', { name: '厂商查询' })" + + menu_vendorQual: + description: "供应商资质管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商资质管理" + playwright: "page.getByRole('menuitem', { name: '供应商资质管理' })" + + menu_authFirst: + description: "授权变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更初审" + playwright: "page.getByRole('menuitem', { name: '授权变更初审' })" + + menu_certFirst: + description: "注册证变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更初审" + playwright: "page.getByRole('menuitem', { name: '注册证变更初审' })" + + menu_certFinal: + description: "注册证变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更终审" + playwright: "page.getByRole('menuitem', { name: '注册证变更终审' })" + + menu_authFinal: + description: "授权变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更终审" + playwright: "page.getByRole('menuitem', { name: '授权变更终审' })" + + menu_certDiff: + description: "注册证差异对比" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证差异对比" + playwright: "page.getByRole('menuitem', { name: '注册证差异对比' })" + + menu_evalScheme: + description: "评价方案维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价方案维护" + playwright: "page.getByRole('menuitem', { name: '评价方案维护' })" + + menu_evalIndicator: + description: "评价指标维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价指标维护" + playwright: "page.getByRole('menuitem', { name: '评价指标维护' })" + + menu_vendorHoliday: + description: "供应商节假日管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商节假日管理" + playwright: "page.getByRole('menuitem', { name: '供应商节假日管理' })" + + certDiff: + description: "注册证差异对比" + element_type: tab + section: "main::main" + strategy: text + value: "注册证差异对比" + playwright: "page.getByText('注册证差异对比')" + + tab_certDiff: + description: "注册证差异对比" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/mfrs/scjs/yc/certCompare" + playwright: "page.locator('#rc-tabs-0-tab-/basic/mfrs/scjs/yc/certCompare')" + + el_e789a9e69699e5908de7a7b0Input: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_goodsName" + playwright: "page.locator('#table_search_form_goodsName')" + + specModel: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_goodsSpec" + playwright: "page.locator('#table_search_form_goodsSpec')" + + statusSelect: + description: "启用" + element_type: select + section: "main::main" + strategy: label_text + value: "状态" + playwright: "page.getByText('状态', { exact: true })" + + statusInput: + description: "状态" + element_type: search + section: "main::main" + strategy: id + value: "table_search_form_mgiTbStatus" + playwright: "page.locator('#table_search_form_mgiTbStatus')" + + 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: '展开' })" + + el_e7ab8be58db3e69bb4e696b0Btn: + description: "立即更新" + element_type: button + section: "main::main" + strategy: role + role: button + name: "立即更新" + playwright: "page.getByRole('button', { name: '立即更新' })" + + exportBtn: + description: "导出" + element_type: button + section: "main::main" + strategy: role + role: button + name: "导出" + playwright: "page.getByRole('button', { name: '导出' })" + + # --- main::pagination --- + pagination_el_e585b137363537e69da13132333435e280a2e280a2e280a23339323030e69da12fe9a1b5e8b7b3e887b3e9a1b5: + description: "共 7657 条12345•••39200 条/页跳至页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 7657 条12345•••39200 条/页跳至页" + playwright: "page.getByText('共 7657 条12345•••39200 条/页跳至页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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_page_2: + description: "2" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "2" + playwright: "page.getByText('2')" + + pagination_page_3: + description: "3" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "3" + playwright: "page.getByText('3')" + + pagination_page_4: + description: "4" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "4" + playwright: "page.getByText('4')" + + pagination_page_5: + description: "5" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "5" + playwright: "page.getByText('5')" + + pagination_el_e59091e5908e35e9a1b5: + description: "向后 5 页" + element_type: li + section: "main::pagination" + strategy: text + value: "向后 5 页" + playwright: "page.getByText('向后 5 页')" + + pagination_page_39: + description: "39" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "39" + playwright: "page.getByText('39')" + + pagination_nextPageBtn: + description: "下一页" + element_type: li + section: "main::pagination" + strategy: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "200 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "200 条/页" + playwright: "page.getByText('200 条/页')" diff --git a/locators/spd/vendor-cert-final.locator.yaml b/locators/spd/vendor-cert-final.locator.yaml new file mode 100644 index 0000000..b1aa0c6 --- /dev/null +++ b/locators/spd/vendor-cert-final.locator.yaml @@ -0,0 +1,318 @@ +# Auto-generated from vendor-cert-final-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal +# Scanned: 2026-10-20T08:42:16.674Z +# Page: 基础设置 > 厂商管理 > 注册证变更终审 +# Note: filter dropdown was opened during scan + +page: vendor-cert-final +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal + +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_vendorQuery: + description: "厂商查询" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "厂商查询" + playwright: "page.getByRole('menuitem', { name: '厂商查询' })" + + menu_vendorQual: + description: "供应商资质管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商资质管理" + playwright: "page.getByRole('menuitem', { name: '供应商资质管理' })" + + menu_authFirst: + description: "授权变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更初审" + playwright: "page.getByRole('menuitem', { name: '授权变更初审' })" + + menu_certFirst: + description: "注册证变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更初审" + playwright: "page.getByRole('menuitem', { name: '注册证变更初审' })" + + menu_certFinal: + description: "注册证变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更终审" + playwright: "page.getByRole('menuitem', { name: '注册证变更终审' })" + + menu_authFinal: + description: "授权变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更终审" + playwright: "page.getByRole('menuitem', { name: '授权变更终审' })" + + menu_certDiff: + description: "注册证差异对比" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证差异对比" + playwright: "page.getByRole('menuitem', { name: '注册证差异对比' })" + + menu_evalScheme: + description: "评价方案维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价方案维护" + playwright: "page.getByRole('menuitem', { name: '评价方案维护' })" + + menu_evalIndicator: + description: "评价指标维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价指标维护" + playwright: "page.getByRole('menuitem', { name: '评价指标维护' })" + + menu_vendorHoliday: + description: "供应商节假日管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商节假日管理" + playwright: "page.getByRole('menuitem', { name: '供应商节假日管理' })" + + certFinal: + description: "注册证变更终审" + element_type: tab + section: "main::main" + strategy: text + value: "注册证变更终审" + playwright: "page.getByText('注册证变更终审')" + + tab_certFinal: + description: "注册证变更终审" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/regCertAuditFinal" + playwright: "page.locator('#rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/regCertAuditFinal')" + + 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('结束日期')" + + documentNo: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_orderNo" + playwright: "page.locator('#table_search_form_orderNo')" + + changeTypeSelect: + description: "请选择" + element_type: select + section: "main::main" + strategy: label_text + value: "变更类型" + playwright: "page.getByText('变更类型', { exact: true })" + + changeTypeInput: + description: "变更类型" + element_type: search + section: "main::main" + strategy: id + value: "table_search_form_chgType" + playwright: "page.locator('#table_search_form_chgType')" + + 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: '展开' })" diff --git a/locators/spd/vendor-cert-first.locator.yaml b/locators/spd/vendor-cert-first.locator.yaml new file mode 100644 index 0000000..16ce563 --- /dev/null +++ b/locators/spd/vendor-cert-first.locator.yaml @@ -0,0 +1,318 @@ +# Auto-generated from vendor-cert-first-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/regCertAudit +# Scanned: 2026-10-20T08:42:07.556Z +# Page: 基础设置 > 厂商管理 > 注册证变更初审 +# Note: filter dropdown was opened during scan + +page: vendor-cert-first +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/regCertAudit + +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_vendorQuery: + description: "厂商查询" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "厂商查询" + playwright: "page.getByRole('menuitem', { name: '厂商查询' })" + + menu_vendorQual: + description: "供应商资质管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商资质管理" + playwright: "page.getByRole('menuitem', { name: '供应商资质管理' })" + + menu_authFirst: + description: "授权变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更初审" + playwright: "page.getByRole('menuitem', { name: '授权变更初审' })" + + menu_certFirst: + description: "注册证变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更初审" + playwright: "page.getByRole('menuitem', { name: '注册证变更初审' })" + + menu_certFinal: + description: "注册证变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更终审" + playwright: "page.getByRole('menuitem', { name: '注册证变更终审' })" + + menu_authFinal: + description: "授权变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更终审" + playwright: "page.getByRole('menuitem', { name: '授权变更终审' })" + + menu_certDiff: + description: "注册证差异对比" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证差异对比" + playwright: "page.getByRole('menuitem', { name: '注册证差异对比' })" + + menu_evalScheme: + description: "评价方案维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价方案维护" + playwright: "page.getByRole('menuitem', { name: '评价方案维护' })" + + menu_evalIndicator: + description: "评价指标维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价指标维护" + playwright: "page.getByRole('menuitem', { name: '评价指标维护' })" + + menu_vendorHoliday: + description: "供应商节假日管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商节假日管理" + playwright: "page.getByRole('menuitem', { name: '供应商节假日管理' })" + + certFirst: + description: "注册证变更初审" + element_type: tab + section: "main::main" + strategy: text + value: "注册证变更初审" + playwright: "page.getByText('注册证变更初审')" + + tab_certFirst: + description: "注册证变更初审" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/regCertAudit" + playwright: "page.locator('#rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/regCertAudit')" + + 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('结束日期')" + + documentNo: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_orderNo" + playwright: "page.locator('#table_search_form_orderNo')" + + changeTypeSelect: + description: "请选择" + element_type: select + section: "main::main" + strategy: label_text + value: "变更类型" + playwright: "page.getByText('变更类型', { exact: true })" + + changeTypeInput: + description: "变更类型" + element_type: search + section: "main::main" + strategy: id + value: "table_search_form_chgType" + playwright: "page.locator('#table_search_form_chgType')" + + 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: '展开' })" diff --git a/locators/spd/vendor-eval-indicator.locator.yaml b/locators/spd/vendor-eval-indicator.locator.yaml new file mode 100644 index 0000000..d6f01bf --- /dev/null +++ b/locators/spd/vendor-eval-indicator.locator.yaml @@ -0,0 +1,509 @@ +# Auto-generated from vendor-eval-indicator-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/indicators +# Scanned: 2026-10-20T08:44:01.895Z +# Page: 基础设置 > 厂商管理 > 评价指标维护 +# Note: filter dropdown was opened during scan +# Note: dialog/modal was opened during scan + +page: vendor-eval-indicator +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/indicators + +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_vendorQuery: + description: "厂商查询" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "厂商查询" + playwright: "page.getByRole('menuitem', { name: '厂商查询' })" + + menu_vendorQual: + description: "供应商资质管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商资质管理" + playwright: "page.getByRole('menuitem', { name: '供应商资质管理' })" + + menu_authFirst: + description: "授权变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更初审" + playwright: "page.getByRole('menuitem', { name: '授权变更初审' })" + + menu_certFirst: + description: "注册证变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更初审" + playwright: "page.getByRole('menuitem', { name: '注册证变更初审' })" + + menu_certFinal: + description: "注册证变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更终审" + playwright: "page.getByRole('menuitem', { name: '注册证变更终审' })" + + menu_authFinal: + description: "授权变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更终审" + playwright: "page.getByRole('menuitem', { name: '授权变更终审' })" + + menu_certDiff: + description: "注册证差异对比" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证差异对比" + playwright: "page.getByRole('menuitem', { name: '注册证差异对比' })" + + menu_evalScheme: + description: "评价方案维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价方案维护" + playwright: "page.getByRole('menuitem', { name: '评价方案维护' })" + + menu_evalIndicator: + description: "评价指标维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价指标维护" + playwright: "page.getByRole('menuitem', { name: '评价指标维护' })" + + menu_vendorHoliday: + description: "供应商节假日管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商节假日管理" + playwright: "page.getByRole('menuitem', { name: '供应商节假日管理' })" + + evalIndicator: + description: "评价指标维护" + element_type: tab + section: "main::main" + strategy: text + value: "评价指标维护" + playwright: "page.getByText('评价指标维护')" + + tab_evalIndicator: + description: "评价指标维护" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/indicators" + playwright: "page.locator('#rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/indicators')" + + el_e68c87e6a087e58886e7b1bb: + description: "指标分类" + element_type: a + section: "main::main" + strategy: text + value: "指标分类" + playwright: "page.getByText('指标分类')" + + addBtn: + description: "新增" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新增" + playwright: "page.getByRole('button', { name: '新增' })" + + categoryNameInput: + description: "分类名称" + element_type: text + section: "main::main" + strategy: placeholder + value: "分类名称" + playwright: "page.getByPlaceholder('分类名称')" + + tree_node_e68c87e6a087: + description: "指标一级分类" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "指标一级分类" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*指标一级分类\\s*$/ })" + + tree_node_313131: + description: "111" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "111" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*111\\s*$/ })" + + tree_node_323232: + description: "222" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "222" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*222\\s*$/ })" + + tree_node_53e6b58be8af: + description: "S测试" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "S测试" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*S测试\\s*$/ })" + + tree_node_e6b58be8af95: + description: "测试指标" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "测试指标" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*测试指标\\s*$/ })" + + tree_node_323630363236: + description: "260626回归指标分类" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "260626回归指标分类" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*260626回归指标分类\\s*$/ })" + + tree_node_30363137e68c: + description: "0617指标" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "0617指标" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*0617指标\\s*$/ })" + + el_e68c87e6a087e7bc96e7a081Input: + description: "请输入指标编码" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_indicatorsCode" + playwright: "page.locator('#table_search_form_indicatorsCode')" + + el_e68c87e6a087e5908de7a7b0Input: + description: "请输入指标名称" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_indicatorsName" + playwright: "page.locator('#table_search_form_indicatorsName')" + + 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: '重 置' })" + + editBtn: + 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: '详情' })" + + logBtn: + description: "日志" + element_type: button + section: "main::main" + strategy: role + role: button + name: "日志" + playwright: "page.getByRole('button', { name: '日志' })" + + # --- main::pagination --- + pagination_indicatorCode: + description: "共 31 条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_indicatorCodeBtn: + 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 })" + + pagination_indicatorCode_2: + description: "共 31 条150 条/页205010020 条/页50 条/页100 条/页200 条/页" + element_type: ul + section: "main::pagination" + strategy: label_text + value: "指标编号" + playwright: "page.getByText('指标编号', { exact: true })" + + pagination_pageSizeSelect_2: + description: "50 条/页205010020 条/页50 条/页100 条/页200 条/页" + element_type: select + section: "main::pagination" + strategy: label_text + value: "指标编号" + playwright: "page.getByText('指标编号', { exact: true })" + + # --- main::modal:新增 --- + el_e4b88ae7baa7e58886e7b1bbSelect: + description: "请选择" + element_type: select + section: "main::modal:新增" + strategy: label_text + value: "上级分类" + playwright: "page.getByText('上级分类', { exact: true })" + + basePkgCategoryName: + description: "分类名称" + element_type: text + section: "main::modal:新增" + strategy: id + value: "form_item_kindName" + playwright: "page.locator('#form_item_kindName')" + + enable: + description: "启用" + element_type: radio + section: "main::modal:新增" + strategy: text + value: "启用" + playwright: "page.getByText('启用')" + + disable: + description: "停用" + element_type: radio + section: "main::modal:新增" + strategy: text + value: "停用" + playwright: "page.getByText('停用')" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:新增" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + confirmBtn: + description: "确 定" + element_type: button + section: "main::modal:新增" + strategy: role + role: button + name: "确 定" + playwright: "page.getByRole('button', { name: '确 定' })" diff --git a/locators/spd/vendor-eval-scheme.locator.yaml b/locators/spd/vendor-eval-scheme.locator.yaml new file mode 100644 index 0000000..6ba7b30 --- /dev/null +++ b/locators/spd/vendor-eval-scheme.locator.yaml @@ -0,0 +1,370 @@ +# Auto-generated from vendor-eval-scheme-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/evaluationPlan +# Scanned: 2026-10-20T08:43:52.262Z +# Page: 基础设置 > 厂商管理 > 评价方案维护 +# Note: dialog/modal was opened during scan + +page: vendor-eval-scheme +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/evaluationPlan + +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_vendorQuery: + description: "厂商查询" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "厂商查询" + playwright: "page.getByRole('menuitem', { name: '厂商查询' })" + + menu_vendorQual: + description: "供应商资质管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商资质管理" + playwright: "page.getByRole('menuitem', { name: '供应商资质管理' })" + + menu_authFirst: + description: "授权变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更初审" + playwright: "page.getByRole('menuitem', { name: '授权变更初审' })" + + menu_certFirst: + description: "注册证变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更初审" + playwright: "page.getByRole('menuitem', { name: '注册证变更初审' })" + + menu_certFinal: + description: "注册证变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更终审" + playwright: "page.getByRole('menuitem', { name: '注册证变更终审' })" + + menu_authFinal: + description: "授权变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更终审" + playwright: "page.getByRole('menuitem', { name: '授权变更终审' })" + + menu_certDiff: + description: "注册证差异对比" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证差异对比" + playwright: "page.getByRole('menuitem', { name: '注册证差异对比' })" + + menu_evalScheme: + description: "评价方案维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价方案维护" + playwright: "page.getByRole('menuitem', { name: '评价方案维护' })" + + menu_evalIndicator: + description: "评价指标维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价指标维护" + playwright: "page.getByRole('menuitem', { name: '评价指标维护' })" + + menu_vendorHoliday: + description: "供应商节假日管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商节假日管理" + playwright: "page.getByRole('menuitem', { name: '供应商节假日管理' })" + + evalScheme: + description: "评价方案维护" + element_type: tab + section: "main::main" + strategy: text + value: "评价方案维护" + playwright: "page.getByText('评价方案维护')" + + tab_evalScheme: + description: "评价方案维护" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/evaluationPlan" + playwright: "page.locator('#rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/evaluationPlan')" + + el_e5889be5bbbae697a5e69c9fDateRange: + 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')" + + el_e5889be5bbbae697a5e69c9fInput: + description: "结束日期" + element_type: text + section: "main::main" + strategy: placeholder + value: "结束日期" + playwright: "page.getByPlaceholder('结束日期')" + + el_e590afe794a8e697a5e69c9fDateRange: + description: "启用日期" + element_type: datepicker + section: "main::main" + strategy: label_text + value: "启用日期" + playwright: "page.getByText('启用日期', { exact: true })" + + el_e590afe794a8e697a5e69c9fInput: + description: "开始日期" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_startTimeValue" + playwright: "page.locator('#table_search_form_startTimeValue')" + + el_e590afe794a8e697a5e69c9fInput_2: + description: "结束日期" + element_type: text + section: "main::main" + strategy: placeholder + value: "结束日期" + playwright: "page.getByPlaceholder('结束日期')" + + schemeCodeInput: + description: "请输入方案编码" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_planCode" + playwright: "page.locator('#table_search_form_planCode')" + + 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: '展开' })" + + addBtn: + description: "新 增" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新 增" + playwright: "page.getByRole('button', { name: '新 增' })" + + # --- main::modal:新增方案 --- + el_e696b9e6a188e5908de7a7b0Input: + description: "方案名称" + element_type: text + section: "main::modal:新增方案" + strategy: id + value: "form_item_planName" + playwright: "page.locator('#form_item_planName')" + + remark: + description: "限255字符" + element_type: textarea + section: "main::modal:新增方案" + strategy: id + value: "form_item_remark" + playwright: "page.locator('#form_item_remark')" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:新增方案" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + confirmBtn: + description: "确 定" + element_type: button + section: "main::modal:新增方案" + strategy: role + role: button + name: "确 定" + playwright: "page.getByRole('button', { name: '确 定' })" diff --git a/locators/spd/vendor-holiday.locator.yaml b/locators/spd/vendor-holiday.locator.yaml new file mode 100644 index 0000000..bd1d0eb --- /dev/null +++ b/locators/spd/vendor-holiday.locator.yaml @@ -0,0 +1,468 @@ +# Auto-generated from vendor-holiday-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/basic/prov/holiday +# Scanned: 2026-10-20T08:44:28.895Z +# Page: 基础设置 > 厂商管理 > 供应商节假日管理 +# Note: dialog/modal was opened during scan +# Note: edit dialog was opened during scan + +page: vendor-holiday +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/basic/prov/holiday + +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_vendorQuery: + description: "厂商查询" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "厂商查询" + playwright: "page.getByRole('menuitem', { name: '厂商查询' })" + + menu_vendorQual: + description: "供应商资质管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商资质管理" + playwright: "page.getByRole('menuitem', { name: '供应商资质管理' })" + + menu_authFirst: + description: "授权变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更初审" + playwright: "page.getByRole('menuitem', { name: '授权变更初审' })" + + menu_certFirst: + description: "注册证变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更初审" + playwright: "page.getByRole('menuitem', { name: '注册证变更初审' })" + + menu_certFinal: + description: "注册证变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更终审" + playwright: "page.getByRole('menuitem', { name: '注册证变更终审' })" + + menu_authFinal: + description: "授权变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更终审" + playwright: "page.getByRole('menuitem', { name: '授权变更终审' })" + + menu_certDiff: + description: "注册证差异对比" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证差异对比" + playwright: "page.getByRole('menuitem', { name: '注册证差异对比' })" + + menu_evalScheme: + description: "评价方案维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价方案维护" + playwright: "page.getByRole('menuitem', { name: '评价方案维护' })" + + menu_evalIndicator: + description: "评价指标维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价指标维护" + playwright: "page.getByRole('menuitem', { name: '评价指标维护' })" + + menu_vendorHoliday: + description: "供应商节假日管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商节假日管理" + playwright: "page.getByRole('menuitem', { name: '供应商节假日管理' })" + + vendorHoliday: + description: "供应商节假日管理" + element_type: tab + section: "main::main" + strategy: text + value: "供应商节假日管理" + playwright: "page.getByText('供应商节假日管理')" + + tab_vendorHoliday: + description: "供应商节假日管理" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/mfrs/basic/prov/holiday" + playwright: "page.locator('#rc-tabs-0-tab-/basic/mfrs/basic/prov/holiday')" + + addBtn: + description: "新 增" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新 增" + playwright: "page.getByRole('button', { name: '新 增' })" + + nameOrYearInput: + description: "请输入名称/年度" + element_type: text + section: "main::main" + strategy: placeholder + value: "请输入名称/年度" + playwright: "page.getByPlaceholder('请输入名称/年度')" + + editBtn: + description: "编辑" + element_type: button + section: "main::main" + strategy: role + role: button + name: "编辑" + playwright: "page.getByRole('button', { name: '编辑' })" + + el_e689b9e9878fe588a0e999a4Btn: + description: "批量删除" + element_type: button + section: "main::main" + strategy: role + role: button + name: "批量删除" + playwright: "page.getByRole('button', { name: '批量删除' })" + + # --- main::pagination --- + pagination_add: + description: "共 16 条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_addBtn: + 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::modal:新增 --- + el_e5908de7a7b0Input: + description: "请输入" + element_type: text + section: "main::modal:新增" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + yearDateRange: + description: "年度" + element_type: datepicker + section: "main::modal:新增" + strategy: label_text + value: "年度" + playwright: "page.getByText('年度', { exact: true })" + + yearInput: + description: "2026" + element_type: text + section: "main::modal:新增" + strategy: id + value: "form_item_yearly" + playwright: "page.locator('#form_item_yearly')" + + el_e8b5b7e6ada2e697a5e69c9fDateRange: + description: "起止日期" + element_type: datepicker + section: "main::modal:新增" + strategy: label_text + value: "起止日期" + playwright: "page.getByText('起止日期', { exact: true })" + + el_e8b5b7e6ada2e697a5e69c9fInput: + description: "开始日期" + element_type: text + section: "main::modal:新增" + strategy: id + value: "form_item_holidayDate" + playwright: "page.locator('#form_item_holidayDate')" + + el_e8b5b7e6ada2e697a5e69c9fInput_2: + description: "结束日期" + element_type: text + section: "main::modal:新增" + strategy: placeholder + value: "结束日期" + playwright: "page.getByPlaceholder('结束日期')" + + remark: + description: "请输入" + element_type: textarea + section: "main::modal:新增" + strategy: id + value: "form_item_remark" + playwright: "page.locator('#form_item_remark')" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:新增" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + confirmBtn: + description: "确 认" + element_type: button + section: "main::modal:新增" + strategy: role + role: button + name: "确 认" + playwright: "page.getByRole('button', { name: '确 认' })" + + # --- main::modal:编辑 --- + el_e5908de7a7b0Input_2: + description: "请输入" + element_type: text + section: "main::modal:编辑" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + el_e5908de7a7b0: + description: "close-circle" + element_type: span + section: "main::modal:编辑" + strategy: label + value: "close-circle" + playwright: "page.getByLabel('close-circle')" + + yearDateRange_2: + description: "年度" + element_type: datepicker + section: "main::modal:编辑" + strategy: label_text + value: "年度" + playwright: "page.getByText('年度', { exact: true })" + + yearInput_2: + description: "2026" + element_type: text + section: "main::modal:编辑" + strategy: id + value: "form_item_yearly" + playwright: "page.locator('#form_item_yearly')" + + el_e8b5b7e6ada2e697a5e69c9fDateRange_2: + description: "起止日期" + element_type: datepicker + section: "main::modal:编辑" + strategy: label_text + value: "起止日期" + playwright: "page.getByText('起止日期', { exact: true })" + + el_e8b5b7e6ada2e697a5e69c9fInput_3: + description: "开始日期" + element_type: text + section: "main::modal:编辑" + strategy: id + value: "form_item_holidayDate" + playwright: "page.locator('#form_item_holidayDate')" + + el_e8b5b7e6ada2e697a5e69c9fInput_4: + description: "结束日期" + element_type: text + section: "main::modal:编辑" + strategy: placeholder + value: "结束日期" + playwright: "page.getByPlaceholder('结束日期')" + + remark_2: + description: "请输入" + element_type: textarea + section: "main::modal:编辑" + strategy: id + value: "form_item_remark" + playwright: "page.locator('#form_item_remark')" diff --git a/locators/spd/vendor-qual.locator.yaml b/locators/spd/vendor-qual.locator.yaml new file mode 100644 index 0000000..183cf02 --- /dev/null +++ b/locators/spd/vendor-qual.locator.yaml @@ -0,0 +1,362 @@ +# Auto-generated from vendor-qual-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/basic/qcManage +# Scanned: 2026-10-20T08:41:30.230Z +# Page: 基础设置 > 厂商管理 > 供应商资质管理 +# Note: filter dropdown was opened during scan + +page: vendor-qual +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/basic/qcManage + +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_vendorQuery: + description: "厂商查询" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "厂商查询" + playwright: "page.getByRole('menuitem', { name: '厂商查询' })" + + menu_vendorQual: + description: "供应商资质管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商资质管理" + playwright: "page.getByRole('menuitem', { name: '供应商资质管理' })" + + menu_authFirst: + description: "授权变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更初审" + playwright: "page.getByRole('menuitem', { name: '授权变更初审' })" + + menu_certFirst: + description: "注册证变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更初审" + playwright: "page.getByRole('menuitem', { name: '注册证变更初审' })" + + menu_certFinal: + description: "注册证变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更终审" + playwright: "page.getByRole('menuitem', { name: '注册证变更终审' })" + + menu_authFinal: + description: "授权变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更终审" + playwright: "page.getByRole('menuitem', { name: '授权变更终审' })" + + menu_certDiff: + description: "注册证差异对比" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证差异对比" + playwright: "page.getByRole('menuitem', { name: '注册证差异对比' })" + + menu_evalScheme: + description: "评价方案维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价方案维护" + playwright: "page.getByRole('menuitem', { name: '评价方案维护' })" + + menu_evalIndicator: + description: "评价指标维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价指标维护" + playwright: "page.getByRole('menuitem', { name: '评价指标维护' })" + + menu_vendorHoliday: + description: "供应商节假日管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商节假日管理" + playwright: "page.getByRole('menuitem', { name: '供应商节假日管理' })" + + vendorQual: + description: "供应商资质管理" + element_type: tab + section: "main::main" + strategy: text + value: "供应商资质管理" + playwright: "page.getByText('供应商资质管理')" + + tab_vendorQual: + description: "供应商资质管理" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/mfrs/basic/qcManage" + playwright: "page.locator('#rc-tabs-0-tab-/basic/mfrs/basic/qcManage')" + + supplierName: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "table_search_form_provName" + playwright: "page.locator('#table_search_form_provName')" + + el_e8af81e4bbb6e7b1bbe59e8bSelect: + description: "请选择" + element_type: select + section: "main::main" + strategy: label_text + value: "证件类型" + playwright: "page.getByText('证件类型', { exact: true })" + + el_e8af81e4bbb6e7b1bbe59e8bInput: + description: "证件类型" + element_type: search + section: "main::main" + strategy: id + value: "table_search_form_certType" + playwright: "page.locator('#table_search_form_certType')" + + 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: '重 置' })" + + exportBtn: + description: "导 出" + element_type: button + section: "main::main" + strategy: role + role: button + name: "导 出" + playwright: "page.getByRole('button', { name: '导 出' })" + + viewDetailBtn: + description: "查看" + element_type: button + section: "main::main" + strategy: role + role: button + name: "查看" + playwright: "page.getByRole('button', { name: '查看' })" + + # --- main::pagination --- + pagination_action: + description: "共 30 条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::modal:承诺书 --- + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:承诺书" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" diff --git a/locators/spd/vendor-query.locator.yaml b/locators/spd/vendor-query.locator.yaml new file mode 100644 index 0000000..45d44d8 --- /dev/null +++ b/locators/spd/vendor-query.locator.yaml @@ -0,0 +1,347 @@ +# Auto-generated from vendor-query-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/factory +# Scanned: 2026-10-20T08:41:21.432Z +# Page: 基础设置 > 厂商管理 > 厂商查询 + +page: vendor-query +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/factory + +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_vendorQuery: + description: "厂商查询" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "厂商查询" + playwright: "page.getByRole('menuitem', { name: '厂商查询' })" + + menu_vendorQual: + description: "供应商资质管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商资质管理" + playwright: "page.getByRole('menuitem', { name: '供应商资质管理' })" + + menu_authFirst: + description: "授权变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更初审" + playwright: "page.getByRole('menuitem', { name: '授权变更初审' })" + + menu_certFirst: + description: "注册证变更初审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更初审" + playwright: "page.getByRole('menuitem', { name: '注册证变更初审' })" + + menu_certFinal: + description: "注册证变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证变更终审" + playwright: "page.getByRole('menuitem', { name: '注册证变更终审' })" + + menu_authFinal: + description: "授权变更终审" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "授权变更终审" + playwright: "page.getByRole('menuitem', { name: '授权变更终审' })" + + menu_certDiff: + description: "注册证差异对比" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "注册证差异对比" + playwright: "page.getByRole('menuitem', { name: '注册证差异对比' })" + + menu_evalScheme: + description: "评价方案维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价方案维护" + playwright: "page.getByRole('menuitem', { name: '评价方案维护' })" + + menu_evalIndicator: + description: "评价指标维护" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "评价指标维护" + playwright: "page.getByRole('menuitem', { name: '评价指标维护' })" + + menu_vendorHoliday: + description: "供应商节假日管理" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "供应商节假日管理" + playwright: "page.getByRole('menuitem', { name: '供应商节假日管理' })" + + vendorQuery: + description: "厂商查询" + element_type: tab + section: "main::main" + strategy: text + value: "厂商查询" + playwright: "page.getByText('厂商查询')" + + tab_vendorQuery: + description: "厂商查询" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/factory" + playwright: "page.locator('#rc-tabs-0-tab-/basic/mfrs/paltform/mfrsName/factory')" + + mfrsNameInput: + description: "厂商名称" + element_type: text + section: "main::main" + strategy: placeholder + value: "厂商名称" + playwright: "page.getByPlaceholder('厂商名称')" + + tableDensity: + description: "column-height" + element_type: span + section: "main::main" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + # --- main::pagination --- + pagination_el_e585b1343430e69da13132333435e280a2e280a2e280a2393530e69da12fe9a1b5e8b7b3e887b3e9a1b5: + description: "共 440 条12345•••950 条/页跳至页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 440 条12345•••950 条/页跳至页" + playwright: "page.getByText('共 440 条12345•••950 条/页跳至页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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_page_2: + description: "2" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "2" + playwright: "page.getByText('2')" + + pagination_page_3: + description: "3" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "3" + playwright: "page.getByText('3')" + + pagination_page_4: + description: "4" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "4" + playwright: "page.getByText('4')" + + pagination_page_5: + description: "5" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "5" + playwright: "page.getByText('5')" + + pagination_el_e59091e5908e35e9a1b5: + description: "向后 5 页" + element_type: li + section: "main::pagination" + strategy: text + value: "向后 5 页" + playwright: "page.getByText('向后 5 页')" + + pagination_page_9: + description: "9" + element_type: pagination_item + section: "main::pagination" + strategy: text + value: "9" + playwright: "page.getByText('9')" + + pagination_nextPageBtn: + description: "下一页" + element_type: li + section: "main::pagination" + strategy: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" diff --git a/locators/spd/vendor-shared.locator.ts b/locators/spd/vendor-shared.locator.ts new file mode 100644 index 0000000..d158787 --- /dev/null +++ b/locators/spd/vendor-shared.locator.ts @@ -0,0 +1,46 @@ +import type { Locator, Page } from '@playwright/test'; + +/** 基础设置 · 厂商管理。叶子菜单是 .ant-menu-item,不是 role=menuitem。 */ +export class VendorSharedLocators { + readonly menuBasic: Locator; + readonly vendorSubmenuTitle: Locator; + readonly tableHeader: Locator; + readonly empty: Locator; + readonly paginationTotal: Locator; + readonly nextPage: Locator; + readonly prevPage: Locator; + readonly formError: Locator; + + constructor(protected readonly page: Page) { + this.menuBasic = page.getByRole('menuitem', { name: '基础设置' }); + this.vendorSubmenuTitle = 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.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(); + } + + dialog(): Locator { + return this.page.locator('.ant-modal-wrap .ant-modal, .ant-drawer-open').last(); + } +} diff --git a/locators/spd/warehouse-probe.locator.ts b/locators/spd/warehouse-probe.locator.ts new file mode 100644 index 0000000..f55e611 --- /dev/null +++ b/locators/spd/warehouse-probe.locator.ts @@ -0,0 +1,40 @@ +import type { Locator, Page } from '@playwright/test'; + +import { WhFlowSharedLocators } from './wh-flow-shared.locator'; + +/** 探针绑定。选择器来自 locators/spd/warehouse-probe.locator.yaml */ +export class WarehouseProbeLocators extends WhFlowSharedLocators { + readonly queryBtn: Locator; + readonly resetBtn: Locator; + readonly bindBtn: Locator; + readonly modalCancel: Locator; + + constructor(page: Page) { + super(page); + this.queryBtn = page.getByRole('button', { name: /查\s*询/ }); + this.resetBtn = page.getByRole('button', { name: /重\s*置/ }); + this.bindBtn = page.getByRole('button', { name: '绑定货位' }); + this.modalCancel = page.getByRole('button', { name: /取\s*消/ }); + } + + probeIdInput(): Locator { + return this.page.locator('.ant-form-item').filter({ hasText: '探针ID' }).locator('input').first(); + } + + hostCodeInput(): Locator { + return this.page.locator('.ant-form-item').filter({ hasText: '主机编码' }).locator('input').first(); + } + + bindSelect(): Locator { + return this.page.locator('.ant-form-item').filter({ hasText: '是否绑定货位' }).locator('.ant-select').first(); + } + + bindModal(): Locator { + return this.page.locator('.ant-modal:visible').filter({ hasText: /货位绑定探针/ }).first(); + } + + confirmCountBtn(root?: Locator): Locator { + const scope = root ?? this.page; + return scope.getByRole('button', { name: /确\s*定/ }).first(); + } +} diff --git a/locators/spd/warehouse-probe.locator.yaml b/locators/spd/warehouse-probe.locator.yaml new file mode 100644 index 0000000..a5ba02d --- /dev/null +++ b/locators/spd/warehouse-probe.locator.yaml @@ -0,0 +1,461 @@ +# Auto-generated from warehouse-probe-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/storehouse/storehouse/probe +# Scanned: 2026-10-20T09:05:17.123Z +# Page: 基础设置 > 库房管理 > 探针绑定 +# Note: filter dropdown was opened during scan + +page: warehouse-probe +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/storehouse/storehouse/probe + +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_el_e5ba93e58cbae8aebee7bdae: + description: "库区设置" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "库区设置" + playwright: "page.getByRole('menuitem', { name: '库区设置' })" + + menu_el_e68ea2e99288e7bb91e5ae9a: + description: "探针绑定" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "探针绑定" + playwright: "page.getByRole('menuitem', { name: '探针绑定' })" + + menu_el_e58d8ee6b885e9809ae98193: + description: "华清通道" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "华清通道" + playwright: "page.getByRole('menuitem', { name: '华清通道' })" + + el_e68ea2e99288e7bb91e5ae9a: + description: "探针绑定" + element_type: tab + section: "main::main" + strategy: text + value: "探针绑定" + playwright: "page.getByText('探针绑定')" + + tab_el_e68ea2e99288e7bb91e5ae9a: + description: "探针绑定" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/storehouse/storehouse/probe" + playwright: "page.locator('#rc-tabs-0-tab-/basic/storehouse/storehouse/probe')" + + el_e68ea2e992884944Input: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + el_e68ea2e992884944Input_2: + description: "请输入" + element_type: text + section: "main::main" + strategy: id + value: "form_item_cname" + playwright: "page.locator('#form_item_cname')" + + el_e698afe590a6e7bb91e5ae9ae8b4a7e4bd8dSelect: + 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: '重 置' })" + + tableDensity: + description: "column-height" + element_type: span + section: "main::main" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + 8Btn: + description: "28" + element_type: button + section: "main::main" + strategy: role + role: button + name: "28" + playwright: "page.getByRole('button', { name: '28' })" + + el_e7bb91e5ae9ae8b4a7e4bd8dBtn: + description: "绑定货位" + element_type: button + section: "main::main" + strategy: role + role: button + name: "绑定货位" + playwright: "page.getByRole('button', { name: '绑定货位' })" + + Btn: + description: "3" + element_type: button + section: "main::main" + strategy: role + role: button + name: "3" + playwright: "page.getByRole('button', { name: '3' })" + + # --- main::pagination --- + pagination_el_e585b13131e69da1313530e69da12fe9a1b5: + description: "共 11 条150 条/页" + element_type: ul + section: "main::pagination" + strategy: text + value: "共 11 条150 条/页" + playwright: "page.getByText('共 11 条150 条/页')" + + pagination_prevPageBtn: + description: "上一页" + element_type: li + section: "main::pagination" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + 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: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pagination_pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::pagination" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + # --- main::modal:货位绑定探针 --- + el_e5bd93e5898de5ba93e688bfSelect: + description: "千泉山院区中心库房" + element_type: select + section: "main::modal:货位绑定探针" + strategy: label_text + value: "当前库房" + playwright: "page.getByText('当前库房', { exact: true })" + + el_e5bd93e5898de5ba93e688bfInput: + description: "当前库房" + element_type: search + section: "main::modal:货位绑定探针" + strategy: id + value: "form_item_deptId" + playwright: "page.locator('#form_item_deptId')" + + el_e5bd93e5898de5ba93e58cbaSelect: + description: "当前库区" + element_type: select + section: "main::modal:货位绑定探针" + strategy: label_text + value: "当前库区" + playwright: "page.getByText('当前库区', { exact: true })" + + el_e5bd93e5898de5ba93e58cbaInput: + description: "当前库区" + element_type: search + section: "main::modal:货位绑定探针" + strategy: id + value: "form_item_areaCode" + playwright: "page.locator('#form_item_areaCode')" + + el_e8b4a7e4bd8de69fa5e8afa2Input: + description: "请输入" + element_type: text + section: "main::modal:货位绑定探针" + strategy: id + value: "form_item_shelfNo" + playwright: "page.locator('#form_item_shelfNo')" + + el_e8b4a7e4bd8de78ab6e68081Select: + description: "货位状态" + element_type: select + section: "main::modal:货位绑定探针" + strategy: label_text + value: "货位状态" + playwright: "page.getByText('货位状态', { exact: true })" + + el_e9bb98e8aea4e8b4a7e4bd8dSelect: + description: "默认货位" + element_type: select + section: "main::modal:货位绑定探针" + strategy: label_text + value: "默认货位" + playwright: "page.getByText('默认货位', { exact: true })" + + el_e585b132363236e69da13132333435e280a2e280a2e280a235333530e69da12fe9a1b5e8b7b3e887b3e9a1b5: + description: "共 2626 条12345•••5350 条/页跳至页" + element_type: ul + section: "main::modal:货位绑定探针" + strategy: text + value: "共 2626 条12345•••5350 条/页跳至页" + playwright: "page.getByText('共 2626 条12345•••5350 条/页跳至页')" + + prevPageBtn: + description: "上一页" + element_type: li + section: "main::modal:货位绑定探针" + strategy: text + value: "上一页" + playwright: "page.getByText('上一页')" + + page_1: + description: "1" + element_type: pagination_item + section: "main::modal:货位绑定探针" + strategy: css + value: ".ant-pagination-item-active" + playwright: "page.locator('.ant-pagination-item-active')" + + page_2: + description: "2" + element_type: pagination_item + section: "main::modal:货位绑定探针" + strategy: text + value: "2" + playwright: "page.getByText('2')" + + page_3: + description: "3" + element_type: pagination_item + section: "main::modal:货位绑定探针" + strategy: text + value: "3" + playwright: "page.getByText('3')" + + page_4: + description: "4" + element_type: pagination_item + section: "main::modal:货位绑定探针" + strategy: text + value: "4" + playwright: "page.getByText('4')" + + page_5: + description: "5" + element_type: pagination_item + section: "main::modal:货位绑定探针" + strategy: text + value: "5" + playwright: "page.getByText('5')" + + el_e59091e5908e35e9a1b5: + description: "向后 5 页" + element_type: li + section: "main::modal:货位绑定探针" + strategy: text + value: "向后 5 页" + playwright: "page.getByText('向后 5 页')" + + page_53: + description: "53" + element_type: pagination_item + section: "main::modal:货位绑定探针" + strategy: text + value: "53" + playwright: "page.getByText('53')" + + nextPageBtn: + description: "下一页" + element_type: li + section: "main::modal:货位绑定探针" + strategy: text + value: "下一页" + playwright: "page.getByText('下一页')" + + pageSizeSelect: + description: "50 条/页" + element_type: select + section: "main::modal:货位绑定探针" + strategy: text + value: "50 条/页" + playwright: "page.getByText('50 条/页')" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:货位绑定探针" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + confirmBtn: + description: "确定(0)" + element_type: button + section: "main::modal:货位绑定探针" + strategy: role + role: button + name: "确定(0)" + playwright: "page.getByRole('button', { name: '确定(0)' })" diff --git a/locators/spd/warehouse-zone.locator.ts b/locators/spd/warehouse-zone.locator.ts new file mode 100644 index 0000000..4cac2c3 --- /dev/null +++ b/locators/spd/warehouse-zone.locator.ts @@ -0,0 +1,39 @@ +import type { Locator, Page } from '@playwright/test'; + +import { WhFlowSharedLocators } from './wh-flow-shared.locator'; + +/** 库区设置。选择器来自 locators/spd/warehouse-zone.locator.yaml */ +export class WarehouseZoneLocators extends WhFlowSharedLocators { + readonly warehouseNameInput: Locator; + readonly zoneSearch: Locator; + readonly syncBtn: Locator; + readonly addBtn: Locator; + readonly tree: Locator; + readonly shelfNo: Locator; + readonly shelfName: Locator; + readonly address: Locator; + readonly cancelBtn: Locator; + readonly confirmBtn: Locator; + + constructor(page: Page) { + super(page); + this.warehouseNameInput = page.getByPlaceholder('库房名称'); + this.zoneSearch = page.getByPlaceholder('库区编号/名称'); + this.syncBtn = page.getByRole('button', { name: '同步' }); + this.addBtn = page.getByRole('button', { name: /新\s*增/ }); + this.tree = page.locator('.ant-tree').first(); + this.shelfNo = page.locator('#basic_shelfNo'); + this.shelfName = page.locator('#basic_shelfName'); + this.address = page.locator('#basic_address'); + this.cancelBtn = page.getByRole('button', { name: /取\s*消/ }); + this.confirmBtn = page.getByRole('button', { name: /确\s*定/ }); + } + + treeTitles(): Locator { + return this.tree.locator('.ant-tree-title'); + } + + selectedTreeTitle(): Locator { + return this.tree.locator('.ant-tree-treenode-selected .ant-tree-title').first(); + } +} diff --git a/locators/spd/warehouse-zone.locator.yaml b/locators/spd/warehouse-zone.locator.yaml new file mode 100644 index 0000000..54a155a --- /dev/null +++ b/locators/spd/warehouse-zone.locator.yaml @@ -0,0 +1,591 @@ +# Auto-generated from warehouse-zone-elements.json +# Source: http://spdtest.cmic.com.cn:8080/spd/basic/storehouse/platform/storehouse/area +# Scanned: 2026-10-20T09:08:25.864Z +# Page: 基础设置 > 库房管理 > 库区设置 +# Note: dialog/modal was opened during scan +# Note: edit dialog was opened during scan + +page: warehouse-zone +module: spd-basic +url: http://spdtest.cmic.com.cn:8080/spd/basic/storehouse/platform/storehouse/area + +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_el_e5ba93e58cbae8aebee7bdae: + description: "库区设置" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "库区设置" + playwright: "page.getByRole('menuitem', { name: '库区设置' })" + + menu_el_e68ea2e99288e7bb91e5ae9a: + description: "探针绑定" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "探针绑定" + playwright: "page.getByRole('menuitem', { name: '探针绑定' })" + + menu_el_e58d8ee6b885e9809ae98193: + description: "华清通道" + element_type: li + section: "main::main" + strategy: role + role: menuitem + name: "华清通道" + playwright: "page.getByRole('menuitem', { name: '华清通道' })" + + el_e5ba93e58cbae8aebee7bdae: + description: "库区设置" + element_type: tab + section: "main::main" + strategy: text + value: "库区设置" + playwright: "page.getByText('库区设置')" + + tab_el_e5ba93e58cbae8aebee7bdae: + description: "库区设置" + element_type: div + section: "main::main" + strategy: id + value: "rc-tabs-0-tab-/basic/storehouse/platform/storehouse/area" + playwright: "page.locator('#rc-tabs-0-tab-/basic/storehouse/platform/storehouse/area')" + + el_e5908ce6ada5Btn: + description: "同步" + element_type: button + section: "main::main" + strategy: role + role: button + name: "同步" + playwright: "page.getByRole('button', { name: '同步' })" + + el_e5ba93e688bfe4bfa1e681afInput: + description: "库房名称" + element_type: text + section: "main::main" + strategy: placeholder + value: "库房名称" + playwright: "page.getByPlaceholder('库房名称')" + + tree_node_e58589e8b0b7: + description: "光谷院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "光谷院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*光谷院区中心库房\\s*$/ })" + + tree_node_e6b4aae5b1b1: + description: "洪山院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "洪山院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*洪山院区中心库房\\s*$/ })" + + tree_node_31323035e999: + description: "1205院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "1205院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*1205院区中心库房\\s*$/ })" + + tree_node_e7bb93e7ae97: + description: "结算院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "结算院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*结算院区中心库房\\s*$/ })" + + tree_node_e59b9ee5bd92: + description: "回归测试院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "回归测试院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*回归测试院区中心库房\\s*$/ })" + + tree_node_e5a4a9e5bf83: + description: "天心阁院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "天心阁院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*天心阁院区中心库房\\s*$/ })" + + tree_node_e58d83e6b389: + description: "千泉山院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "千泉山院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*千泉山院区中心库房\\s*$/ })" + + tree_node_e9a9ace78e8b: + description: "马王堆院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "马王堆院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*马王堆院区中心库房\\s*$/ })" + + tree_node_e4b8bbe999a2: + description: "主院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "主院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*主院区中心库房\\s*$/ })" + + tree_node_e5b2b3e9ba93: + description: "岳麓山院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "岳麓山院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*岳麓山院区中心库房\\s*$/ })" + + tree_node_e695b0e68dae: + description: "数据检查院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "数据检查院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*数据检查院区中心库房\\s*$/ })" + + tree_node_e4b89ce59c9f: + description: "东土城院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "东土城院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*东土城院区中心库房\\s*$/ })" + + tree_node_e699bae883bd: + description: "智能院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "智能院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*智能院区中心库房\\s*$/ })" + + tree_node_e887aae58aa8: + description: "自动采购/请领建议专用院区中心库房" + element_type: tree_title + section: "main::main" + strategy: tree_node + value: "自动采购/请领建议专用院区中心库房" + tree: ".ant-tree" + playwright: "page.locator('.ant-tree .ant-tree-title').filter({ hasText: /^\\s*自动采购/请领建议专用院区中心库房\\s*$/ })" + + addBtn: + description: "新 增" + element_type: button + section: "main::main" + strategy: role + role: button + name: "新 增" + playwright: "page.getByRole('button', { name: '新 增' })" + + addInput: + description: "库区编号/名称" + element_type: text + section: "main::main" + strategy: placeholder + value: "库区编号/名称" + playwright: "page.getByPlaceholder('库区编号/名称')" + + el_e5ba93e688bfe4bfa1e681af: + description: "column-height" + element_type: span + section: "main::main" + strategy: label + value: "column-height" + playwright: "page.getByLabel('column-height')" + + editBtn: + description: "编辑" + element_type: button + section: "main::main" + strategy: role + role: button + name: "编辑" + playwright: "page.getByRole('button', { name: '编辑' })" + + el_e4babae59198e7aea1e79086Btn: + description: "人员管理" + element_type: button + section: "main::main" + strategy: role + role: button + name: "人员管理" + playwright: "page.getByRole('button', { name: '人员管理' })" + + el_e8b4a7e4bd8de7aea1e79086Btn: + description: "货位管理" + element_type: button + section: "main::main" + strategy: role + role: button + name: "货位管理" + playwright: "page.getByRole('button', { name: '货位管理' })" + + disableBtn: + description: "停用" + element_type: button + section: "main::main" + strategy: role + role: button + name: "停用" + playwright: "page.getByRole('button', { name: '停用' })" + + enableBtn: + description: "启用" + element_type: button + section: "main::main" + strategy: role + role: button + name: "启用" + playwright: "page.getByRole('button', { name: '启用' })" + + # --- main::pagination --- + pagination_el_e5ba93e688bfe4bfa1e681af: + description: "共 5 条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_el_e5ba93e688bfe4bfa1e681afBtn: + 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::modal:新增库区 --- + el_e5ba93e58cbae7bc96e58fb7Input: + description: "请输入" + element_type: text + section: "main::modal:新增库区" + strategy: id + value: "basic_shelfNo" + playwright: "page.locator('#basic_shelfNo')" + + storageAreaInput: + description: "请输入" + element_type: text + section: "main::modal:新增库区" + strategy: id + value: "basic_shelfName" + playwright: "page.locator('#basic_shelfName')" + + el_e5ba93e58cbae59cb0e59d80Input: + description: "请输入" + element_type: text + section: "main::modal:新增库区" + strategy: id + value: "basic_address" + playwright: "page.locator('#basic_address')" + + el_e590a6: + description: "否" + element_type: radio + section: "main::modal:新增库区" + strategy: text + value: "否" + playwright: "page.getByText('否')" + + el_e698af: + description: "是" + element_type: radio + section: "main::modal:新增库区" + strategy: text + value: "是" + playwright: "page.getByText('是')" + + el_e88194e7b3bbe4babaSelect: + description: "联系人" + element_type: select + section: "main::modal:新增库区" + strategy: label_text + value: "联系人" + playwright: "page.getByText('联系人', { exact: true })" + + el_e88194e7b3bbe4babaInput: + description: "联系人" + element_type: search + section: "main::modal:新增库区" + strategy: id + value: "basic_linkman" + playwright: "page.locator('#basic_linkman')" + + el_e88194e7b3bbe696b9e5bc8fInput: + description: "请输入" + element_type: text + section: "main::modal:新增库区" + strategy: id + value: "basic_recPhone" + playwright: "page.locator('#basic_recPhone')" + + cancelBtn: + description: "取 消" + element_type: button + section: "main::modal:新增库区" + strategy: role + role: button + name: "取 消" + playwright: "page.getByRole('button', { name: '取 消' })" + + confirmBtn: + description: "确 定" + element_type: button + section: "main::modal:新增库区" + strategy: role + role: button + name: "确 定" + playwright: "page.getByRole('button', { name: '确 定' })" + + # --- main::modal:编辑库区信息 --- + el_e5ba93e58cbae7bc96e58fb7Input_2: + description: "请输入" + element_type: text + section: "main::modal:编辑库区信息" + strategy: id + value: "basic_shelfNo" + playwright: "page.locator('#basic_shelfNo')" + + storageAreaInput_2: + description: "请输入" + element_type: text + section: "main::modal:编辑库区信息" + strategy: id + value: "basic_shelfName" + playwright: "page.locator('#basic_shelfName')" + + el_e5ba93e58cbae59cb0e59d80Input_2: + description: "请输入" + element_type: text + section: "main::modal:编辑库区信息" + strategy: id + value: "basic_address" + playwright: "page.locator('#basic_address')" + + el_e590a6_2: + description: "否" + element_type: radio + section: "main::modal:编辑库区信息" + strategy: text + value: "否" + playwright: "page.getByText('否')" + + el_e698af_2: + description: "是" + element_type: radio + section: "main::modal:编辑库区信息" + strategy: text + value: "是" + playwright: "page.getByText('是')" + + el_e88194e7b3bbe4babaSelect_2: + description: "蒋中心库" + element_type: select + section: "main::modal:编辑库区信息" + strategy: label_text + value: "联系人" + playwright: "page.getByText('联系人', { exact: true })" + + el_e88194e7b3bbe4babaInput_2: + description: "联系人" + element_type: search + section: "main::modal:编辑库区信息" + strategy: id + value: "basic_linkman" + playwright: "page.locator('#basic_linkman')" + + el_e88194e7b3bbe696b9e5bc8fInput_2: + description: "请输入" + element_type: text + section: "main::modal:编辑库区信息" + strategy: id + value: "basic_recPhone" + playwright: "page.locator('#basic_recPhone')" diff --git a/locators/spd/wh-flow-shared.locator.ts b/locators/spd/wh-flow-shared.locator.ts new file mode 100644 index 0000000..2f6defb --- /dev/null +++ b/locators/spd/wh-flow-shared.locator.ts @@ -0,0 +1,44 @@ +import type { Locator, Page } from '@playwright/test'; + +/** 库房管理 / 流程设置共用。叶子菜单是 .ant-menu-item。 */ +export class WhFlowSharedLocators { + readonly menuBasic: Locator; + readonly tableHeader: Locator; + readonly empty: Locator; + readonly paginationTotal: Locator; + readonly nextPage: Locator; + readonly prevPage: Locator; + readonly formError: Locator; + + constructor(protected readonly page: Page) { + this.menuBasic = page.getByRole('menuitem', { name: '基础设置' }); + 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.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'); + } + + groupSubmenuTitle(name: string): Locator { + return this.page.locator('.ant-menu-submenu-title').filter({ hasText: new RegExp(`^\\s*${name}\\s*$`) }); + } + + 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 }); + } + + putPage(): Locator { + return this.page.locator('.put-page').last(); + } + + dialog(): Locator { + return this.page.locator('.ant-modal-wrap .ant-modal:visible, .ant-drawer-open').last(); + } +} diff --git a/package.json b/package.json index 46366a1..3d9d5ba 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,9 @@ "test:page": "node scripts/run-page.mjs", "suites": "node scripts/run-ci.mjs --list", "flake:report": "node scripts/flake-report.mjs", + "failures:report": "node scripts/failure-report.mjs", + "quarantine:sync": "node scripts/sync-quarantine.mjs", + "quarantine:apply": "node scripts/sync-quarantine.mjs --write", "email:summary": "node scripts/ci-email-summary.mjs", "test:quarantine": "node scripts/run-ci.mjs --suite=quarantine", "test:smoke": "playwright test tests/spd/login.spec.ts tests/spd/home.spec.ts --grep @smoke", @@ -84,9 +87,78 @@ "discover:scan-invoice-trace": "node scripts/scan-invoice-trace-batch.mjs", "discover:scan-consume": "node scripts/scan-consume-batch.mjs", "discover:scan-extended": "node scripts/scan-extended-report-batch.mjs", + "discover:scan-admit": "node scripts/scan-admit-batch.mjs", + "discover:scan-basic": "node scripts/scan-basic-batch.mjs", + "discover:scan-vendor": "node scripts/scan-vendor-batch.mjs", + "discover:scan-surgery": "node scripts/scan-surgery-batch.mjs", + "discover:scan-user": "node scripts/scan-user-batch.mjs", + "discover:scan-warehouse": "node scripts/scan-wh-flow-batch.mjs warehouse-zone warehouse-probe", + "discover:scan-flow": "node scripts/scan-wh-flow-batch.mjs flow-def flow-inst", + "discover:scan-wh-flow": "node scripts/scan-wh-flow-batch.mjs", + "discover:yaml-from-json-basic": "node scripts/json-to-locator-yaml.mjs --all-new", + "generate:testpoints-vendor": "python3 scripts/generate-vendor-testpoints.py", + "generate:testpoints-user": "python3 scripts/generate-user-testpoints.py", + "generate:testpoints-warehouse": "python3 scripts/generate-wh-flow-testpoints.py warehouse-zone warehouse-probe", + "generate:testpoints-flow": "python3 scripts/generate-wh-flow-testpoints.py flow-def flow-inst", + "generate:testpoints-wh-flow": "python3 scripts/generate-wh-flow-testpoints.py", + "generate:md-user": "python3 scripts/generate-user-md.py", + "generate:md-wh-flow": "python3 scripts/generate-wh-flow-md.py", + "generate:md-csv-wh-flow": "python3 scripts/generate-wh-flow-md.py", + "test:warehouse-zone": "playwright test tests/spd/warehouse-zone.spec.ts --project=chromium --grep @smoke", + "test:warehouse-zone:all": "playwright test tests/spd/warehouse-zone.spec.ts --project=chromium --workers=1", + "test:warehouse-probe": "playwright test tests/spd/warehouse-probe.spec.ts --project=chromium --grep @smoke", + "test:warehouse-probe:all": "playwright test tests/spd/warehouse-probe.spec.ts --project=chromium --workers=1", + "test:flow-def": "playwright test tests/spd/flow-def.spec.ts --project=chromium --grep @smoke", + "test:flow-def:all": "playwright test tests/spd/flow-def.spec.ts --project=chromium --workers=1", + "test:flow-inst": "playwright test tests/spd/flow-inst.spec.ts --project=chromium --grep @smoke", + "test:flow-inst:all": "playwright test tests/spd/flow-inst.spec.ts --project=chromium --workers=1", + "test:wh-flow:smoke": "playwright test tests/spd/warehouse-zone.spec.ts tests/spd/warehouse-probe.spec.ts tests/spd/flow-def.spec.ts tests/spd/flow-inst.spec.ts --project=chromium --grep @smoke --workers=1", + "test:wh-flow:all": "playwright test tests/spd/warehouse-zone.spec.ts tests/spd/warehouse-probe.spec.ts tests/spd/flow-def.spec.ts tests/spd/flow-inst.spec.ts --project=chromium --workers=1", + "assert:wh-flow-csv-spec": "node scripts/assert-wh-flow-csv-spec-sync.mjs", + "assert:wh-flow-isolation": "node scripts/assert-vendor-no-reuse-open.mjs", + "generate:md-csv-user": "python3 scripts/generate-user-md.py", + "test:basic-user": "playwright test tests/spd/basic-user.spec.ts --project=chromium --grep @smoke", + "test:basic-user:all": "playwright test tests/spd/basic-user.spec.ts --project=chromium --workers=1", + "test:basic-user-union": "playwright test tests/spd/basic-user-union.spec.ts --project=chromium --grep @smoke", + "test:basic-user-union:all": "playwright test tests/spd/basic-user-union.spec.ts --project=chromium --workers=1", + "test:basic-role": "playwright test tests/spd/basic-role.spec.ts --project=chromium --grep @smoke", + "test:basic-role:all": "playwright test tests/spd/basic-role.spec.ts --project=chromium --workers=1", + "test:user:smoke": "playwright test tests/spd/basic-user.spec.ts tests/spd/basic-user-union.spec.ts tests/spd/basic-role.spec.ts --project=chromium --grep @smoke --workers=1", + "test:user:all": "playwright test tests/spd/basic-user.spec.ts tests/spd/basic-user-union.spec.ts tests/spd/basic-role.spec.ts --project=chromium --workers=1", + "assert:user-csv-spec": "node scripts/assert-user-csv-spec-sync.mjs", + "assert:basic-csv-spec": "node scripts/assert-basic-csv-spec-sync.mjs", + "test:vendor-query": "playwright test tests/spd/vendor-query.spec.ts --project=chromium --grep @smoke", + "test:vendor-query:all": "playwright test tests/spd/vendor-query.spec.ts --project=chromium --workers=1", + "test:vendor-qual": "playwright test tests/spd/vendor-qual.spec.ts --project=chromium --grep @smoke", + "test:vendor-qual:all": "playwright test tests/spd/vendor-qual.spec.ts --project=chromium --workers=1", + "test:vendor:smoke": "playwright test tests/spd/vendor-query.spec.ts tests/spd/vendor-qual.spec.ts tests/spd/vendor-audit.spec.ts tests/spd/vendor-cert-diff.spec.ts tests/spd/vendor-eval-scheme.spec.ts tests/spd/vendor-eval-indicator.spec.ts tests/spd/vendor-holiday.spec.ts --project=chromium --grep @smoke --workers=1", + "test:vendor-audit:smoke": "playwright test tests/spd/vendor-audit.spec.ts --project=chromium --grep @smoke --workers=1", + "test:vendor-audit:all": "playwright test tests/spd/vendor-audit.spec.ts --project=chromium --workers=1", + "test:vendor-cert-diff": "playwright test tests/spd/vendor-cert-diff.spec.ts --project=chromium --grep @smoke", + "test:vendor-cert-diff:all": "playwright test tests/spd/vendor-cert-diff.spec.ts --project=chromium --workers=1", + "test:vendor-eval-scheme": "playwright test tests/spd/vendor-eval-scheme.spec.ts --project=chromium --grep @smoke", + "test:vendor-eval-scheme:all": "playwright test tests/spd/vendor-eval-scheme.spec.ts --project=chromium --workers=1", + "test:vendor-eval-indicator": "playwright test tests/spd/vendor-eval-indicator.spec.ts --project=chromium --grep @smoke", + "test:vendor-eval-indicator:all": "playwright test tests/spd/vendor-eval-indicator.spec.ts --project=chromium --workers=1", + "test:vendor-holiday": "playwright test tests/spd/vendor-holiday.spec.ts --project=chromium --grep @smoke", + "test:vendor-holiday:all": "playwright test tests/spd/vendor-holiday.spec.ts --project=chromium --workers=1", + "generate:md-csv-vendor": "python3 scripts/generate-vendor-md-csv.py", + "assert:vendor-csv-spec": "node scripts/assert-vendor-csv-spec-sync.mjs", + "assert:vendor-isolation": "node scripts/assert-vendor-no-reuse-open.mjs", + "test:surgery-base-pkg": "playwright test tests/spd/surgery-base-pkg.spec.ts --project=chromium --grep @smoke", + "test:surgery-base-pkg:all": "playwright test tests/spd/surgery-base-pkg.spec.ts --project=chromium --workers=1", + "test:surgery-oper-type": "playwright test tests/spd/surgery-oper-type.spec.ts --project=chromium --grep @smoke", + "test:surgery-oper-type:all": "playwright test tests/spd/surgery-oper-type.spec.ts --project=chromium --workers=1", + "test:surgery-std-pkg": "playwright test tests/spd/surgery-std-pkg.spec.ts --project=chromium --grep @smoke", + "test:surgery-std-pkg:all": "playwright test tests/spd/surgery-std-pkg.spec.ts --project=chromium --workers=1", + "test:surgery:smoke": "playwright test tests/spd/surgery-base-pkg.spec.ts tests/spd/surgery-oper-type.spec.ts tests/spd/surgery-std-pkg.spec.ts --project=chromium --grep @smoke --workers=1", + "test:surgery:all": "playwright test tests/spd/surgery-base-pkg.spec.ts tests/spd/surgery-oper-type.spec.ts tests/spd/surgery-std-pkg.spec.ts --project=chromium --workers=1", + "assert:surgery-csv-spec": "node scripts/assert-surgery-csv-spec-sync.mjs", + "test:vendor:all": "playwright test tests/spd/vendor-query.spec.ts tests/spd/vendor-qual.spec.ts tests/spd/vendor-audit.spec.ts tests/spd/vendor-cert-diff.spec.ts tests/spd/vendor-eval-scheme.spec.ts tests/spd/vendor-eval-indicator.spec.ts tests/spd/vendor-holiday.spec.ts --project=chromium --workers=1", "discover:yaml-from-json": "node scripts/json-to-locator-yaml.mjs --all-extended", "update:ui-specs-extended": "python3 scripts/update-extended-ui-specs.py", "update:ui-specs-consume": "python3 scripts/update-consume-ui-specs.py", + "sync:ui-specs-user": "python3 scripts/sync-docs-ui-specs.py --only USER-001,USER-002,USER-003", "generate:testpoints-invoice-trace": "python3 scripts/generate-invoice-trace-testpoints.py", "generate:testpoints-consume": "python3 scripts/generate-consume-testpoints.py", "generate:testpoints-extended": "python3 scripts/generate-extended-testpoints.py", diff --git a/pages/spd/admit-material-list.page.ts b/pages/spd/admit-material-list.page.ts new file mode 100644 index 0000000..3521c0a --- /dev/null +++ b/pages/spd/admit-material-list.page.ts @@ -0,0 +1,166 @@ +import { expect, type Page } from '@playwright/test'; + +import { admitData } from '../../data/admit.data'; +import { AdmitSharedPage } from './admit-shared.page'; + +/** 物料准入单列表。详情里的「通过」只取消,不确认审批。 */ +export class AdmitMaterialListPage extends AdmitSharedPage { + constructor(page: Page) { + super(page, { + path: admitData.materialListPath, + pathPart: 'productOrder/zrlist', + menu: admitData.menuMaterialList, + forbidden: ['supplyApply/', 'accessKind/', 'productApply/'], + }); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + expect(this.page.url()).toContain('zrlist'); + await expect(this.tab('已通过')).toBeVisible(); + } + + async filterByOrderOrFallback(): Promise { + await this.query(); + await this.queryByFirstAvailable([ + { column: '准入单号', filter: '准入单号', kind: 'input' }, + { column: '注册证号', filter: '注册证号', kind: 'input' }, + { column: '产品名称', filter: '产品名称', kind: 'input' }, + { column: '器械注册人', filter: '器械注册人', kind: 'input' }, + ]); + } + + async resetFilters(): Promise { + await this.query(); + const filled = await this.fillFirstVisibleFilter(); + await this.reset(); + if (filled) await this.expectInputCleared(filled); + await this.expectListOrEmpty(); + } + + async mockEmpty(): Promise { + const cert = this.textInput('注册证号'); + if (await cert.isVisible().catch(() => false)) { + await this.fillFilter('注册证号', admitData.mockCert); + } else { + await this.fillFilter('产品名称', admitData.mockGoods); + } + await this.query(); + await this.expectMockEmpty(); + } + + async expectCoreColumns(): Promise { + await this.query(); + await this.expectHeaders(['准入类型', '准入单号', '进院供应商', '产品名称', '注册证号', '耗材类型', '产品条数', '状态', '操作']); + await expect(this.rowAction(await this.firstRow(), '详情')).toBeVisible(); + } + + async switchPassed(): Promise { + await this.clickTabAndMatchCount('已通过'); + await expect(this.tab('已通过')).toHaveClass(/ant-tabs-tab-active/); + } + + async detailAndBack(): Promise { + await this.query(); + const row = await this.firstRow(); + const order = await this.tryCell(row, '准入单号'); + await this.openPutFromRow(row, '详情'); + if (order) await expect(this.put()).toContainText(order.slice(0, 8)); + await expect(this.put().getByText('审批进程').first()).toBeVisible(); + await this.leaveWithoutCommit(); + } + + async detailCountMatches(): Promise { + await this.query(); + const row = await this.firstRow(); + const countText = (await this.cellText(row, '产品条数')).replace(/\s/g, ''); + await this.openPutFromRow(row, '详情'); + await expect(this.put()).toContainText(countText); + await this.leaveWithoutCommit(); + } + + async openApproval(): Promise { + await this.query(); + await this.openPutFromRow(await this.firstRow(), '详情'); + await this.openDetailSection('审批进程'); + await this.leaveWithoutCommit(); + } + + async exportDetail(): Promise { + await this.query(); + await this.openPutFromRow(await this.firstRow(), '详情'); + const exp = this.button('导出', this.put()).first(); + if (!(await exp.isVisible().catch(() => false))) { + await this.leaveWithoutCommit(); + await this.skipWhenDataInsufficient('详情无导出'); + } + await this.click(exp, '导出'); + await this.assertPageHealthy('详情导出'); + expect(this.page.url()).not.toMatch(/\/login/); + await expect(this.put().getByRole('button', { name: /返\s*回/ }).first()).toBeVisible(); + await this.leaveWithoutCommit(); + } + + async detailSearchMock(): Promise { + await this.query(); + await this.openPutFromRow(await this.firstRow(), '详情'); + const input = this.putInput('产品名称'); + const target = (await input.isVisible().catch(() => false)) ? input : this.put().locator('input').first(); + await this.fill(target, admitData.mockGoods, '详情搜索 mock'); + const query = this.button('查询', this.put()).first(); + if (await query.isVisible().catch(() => false)) await this.click(query, '详情查询'); + await this.waitForTableReady(); + const empty = await this.put().getByText('暂无数据').first().isVisible().catch(() => false); + const rows = await this.put().locator('.ant-table-tbody > tr.ant-table-row').count(); + expect(empty || rows === 0, '详情无匹配应空态').toBeTruthy(); + await this.leaveWithoutCommit(); + } + + async passedNotConfirm(): Promise { + await this.skipIfTabEmpty('已通过'); + const row = await this.firstRow(); + const order = await this.tryCell(row, '准入单号'); + await this.openPutFromRow(row, '详情'); + const pass = this.button('通过', this.put()).first(); + const visible = await pass.isVisible().catch(() => false); + const disabled = visible && ((await pass.isDisabled().catch(() => false)) || ((await pass.getAttribute('class')) ?? '').includes('disabled')); + if (!visible || disabled) { + await this.leaveWithoutCommit(); + } else { + await this.click(pass, '通过'); + const confirm = this.page.locator('.ant-modal-confirm:visible, .ant-modal:visible').last(); + if (await confirm.isVisible().catch(() => false)) { + await this.click(confirm.getByRole('button', { name: /取\s*消/ }).first(), '取消通过'); + } + await this.leaveWithoutCommit(); + } + await this.clickTab('已通过'); + if (order) await expect(this.page.getByText(order.slice(0, 10)).first()).toBeVisible(); + } + + private async fillFirstVisibleFilter(): Promise { + for (const label of ['注册证号', '产品名称', '器械注册人']) { + if (await this.textInput(label).isVisible().catch(() => false)) { + await this.fillFilter(label, '样'); + return label; + } + } + return ''; + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '制单日期', kind: 'dateRange', value: '样本' }, + { label: '销售商', kind: 'text', value: '样本' }, + { label: '耗材类型', kind: 'select', value: '样本' }, + { label: '器械注册人', kind: 'text', value: '样本' }, + { label: '注册证号', kind: 'text', value: '样本' }, + { label: '供应商', kind: 'text', value: '样本' }, + ], + { scope }, + ); + } +} diff --git a/pages/spd/admit-material.page.ts b/pages/spd/admit-material.page.ts new file mode 100644 index 0000000..f5e8d59 --- /dev/null +++ b/pages/spd/admit-material.page.ts @@ -0,0 +1,215 @@ +import { expect, type Page } from '@playwright/test'; + +import { admitData } from '../../data/admit.data'; +import { AdmitSharedPage } from './admit-shared.page'; + +/** 物料准入管理。写操作只取消,不确认生成/作废/关联。 */ +export class AdmitMaterialPage extends AdmitSharedPage { + constructor(page: Page) { + super(page, { + path: admitData.materialPath, + pathPart: 'productApply/ZRlist', + menu: admitData.menuMaterial, + forbidden: ['productOrder/', 'supplyApply/', 'accessKind/'], + }); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + expect(this.page.url()).toContain('ZRlist'); + for (const name of ['关联产品', '生成申请单', '一键生成', '作废']) { + await expect(this.button(name).first(), name).toBeVisible(); + } + await expect(this.tab('待申请')).toBeVisible(); + } + + async filterByProductName(): Promise { + await this.query(); + const name = await this.sampleCell('产品名称'); + await this.fillFilter('产品名称', name.slice(0, 40)); + await this.query(); + await this.expectRowsContain('产品名称', name); + } + + async filterByGoodsType(): Promise { + await this.query(); + const type = await this.sampleCell('耗材类型'); + await this.chooseSelect('耗材类型', type); + await this.query(); + await this.expectRowsContain('耗材类型', type); + } + + async filterByNameAndType(): Promise { + await this.query(); + const name = await this.sampleCell('产品名称'); + const type = await this.sampleCell('耗材类型'); + await this.fillFilter('产品名称', name.slice(0, 40)); + await this.chooseSelect('耗材类型', type); + await this.query(); + await this.expectRowsContain('产品名称', name); + await this.expectRowsContain('耗材类型', type); + } + + async resetFilters(): Promise { + await this.query(); + const name = await this.sampleCell('产品名称'); + await this.fillFilter('产品名称', name.slice(0, 20)); + await this.reset(); + await this.expectInputCleared('产品名称'); + await this.expectListOrEmpty(); + } + + async collapseKeepsName(): Promise { + await this.query(); + const name = await this.sampleCell('产品名称'); + await this.collapseKeeps('产品名称', name.slice(0, 20)); + } + + async mockEmptyName(): Promise { + await this.fillFilter('产品名称', admitData.mockGoods); + await this.query(); + await this.expectMockEmpty(); + } + + async expectCoreColumns(): Promise { + await this.query(); + await this.expectHeaders([ + '准入类型', + '产品编号', + '产品名称', + '规格型号', + '规格', + '型号', + '器械注册人', + '单位', + '耗材类型', + '默认进院供应商', + '操作', + ]); + const row = await this.firstRow(); + await expect(this.rowAction(row, '详情')).toBeVisible(); + } + + async switchPending(): Promise { + await this.clickTab('待申请'); + await expect(this.tab('待申请')).toHaveClass(/ant-tabs-tab-active/); + await this.expectListOrEmpty(); + } + + async pendingCountMatches(): Promise { + await this.clickTabAndMatchCount('待申请'); + } + + async generateNeedsSelection(): Promise { + await this.expectNeedsSelection('生成申请单'); + } + + async detailAndBack(): Promise { + await this.query(); + const row = await this.firstRow(); + const name = await this.cellText(row, '产品名称'); + await this.openPutFromRow(row, '详情'); + await expect(this.put()).toContainText(name.slice(0, 8)); + await this.leaveWithoutCommit(); + await expect(this.button('查询').first()).toBeVisible(); + } + + async detailReadonly(): Promise { + await this.query(); + await this.openPutFromRow(await this.firstRow(), '详情'); + const save = this.button('保存', this.put()); + expect(await save.count(), '详情不应提供保存').toBe(0); + const nameInput = this.putInput('产品名称'); + if (await nameInput.isVisible().catch(() => false)) { + await expect(nameInput).toBeDisabled(); + } + await this.leaveWithoutCommit(); + } + + async editBackfill(): Promise { + await this.clickTab('待申请'); + const row = await this.skipIfNoAction('编辑', '待申请无编辑'); + const name = await this.cellText(row, '产品名称'); + const spec = await this.tryCell(row, '规格型号'); + await this.openPutFromRow(row, '编辑'); + await expect(this.putInput('产品名称')).toHaveValue(name); + if (spec && (await this.putInput('规格型号').isVisible().catch(() => false))) { + await expect(this.putInput('规格型号')).toHaveValue(spec); + } + await this.leaveWithoutCommit(); + } + + async editCancel(): Promise { + await this.clickTab('待申请'); + const row = await this.skipIfNoAction('编辑', '待申请无编辑'); + const name = await this.cellText(row, '产品名称'); + await this.openPutFromRow(row, '编辑'); + const input = this.putInput('产品名称'); + await this.fill(input, `${name}-取消`, '临时改名'); + await this.leaveWithoutCommit(); + await this.clickTab('待申请'); + const again = await this.skipIfNoAction('编辑', '待申请无编辑'); + await this.openPutFromRow(again, '编辑'); + await expect(this.putInput('产品名称')).toHaveValue(name); + await this.leaveWithoutCommit(); + } + + async editClearName(): Promise { + await this.clickTab('待申请'); + const row = await this.skipIfNoAction('编辑', '待申请无编辑'); + const name = await this.cellText(row, '产品名称'); + await this.openPutFromRow(row, '编辑'); + await this.fill(this.putInput('产品名称'), '', '清空产品名称'); + await this.saveOnPut(); + await this.expectBlockedAndStay(); + await this.leaveWithoutCommit(); + await this.clickTab('待申请'); + const again = await this.rowWithAction('编辑'); + expect(again, '取消后原行仍应可编辑').toBeTruthy(); + await this.openPutFromRow(again as NonNullable, '编辑'); + await expect(this.putInput('产品名称')).toHaveValue(name); + await this.leaveWithoutCommit(); + } + + async effectiveNoEdit(): Promise { + await this.skipIfTabEmpty('已生效'); + const edit = (await this.listRows()).getByRole('button', { name: /编\s*辑/ }); + expect(await edit.count(), '已生效行不应提供编辑').toBe(0); + } + + async relateCancel(): Promise { + await this.query(); + const total = await this.getTotalCount(); + const supplier = await this.cellText(await this.firstRow(), '默认进院供应商'); + await this.clickToolbarThenCancel('关联产品'); + expect(await this.getTotalCount(), '取消关联后总数不变').toBe(total); + expect(await this.cellText(await this.firstRow(), '默认进院供应商')).toBe(supplier); + } + + async voidCancel(): Promise { + await this.query(); + const voidBefore = await this.readTabCount('已作废'); + const row = await this.checkFirstRow(); + const name = await this.cellText(row, '产品名称'); + await this.clickButton('作废'); + const confirm = this.page.locator('.ant-modal-confirm:visible, .ant-modal:visible').last(); + await expect(confirm, '作废应出现确认框').toBeVisible(); + await expect(confirm.getByRole('button', { name: /取\s*消/ })).toBeVisible(); + await expect(confirm.getByRole('button', { name: /确\s*定/ })).toBeVisible(); + await this.click(confirm.getByRole('button', { name: /取\s*消/ }).first(), '取消作废'); + await expect(this.page.getByText(name.slice(0, 12)).first(), '取消后该行仍在').toBeVisible(); + if (voidBefore !== null) { + expect(await this.readTabCount('已作废'), '已作废计数不变').toBe(voidBefore); + } + } + + async openLog(): Promise { + await this.query(); + const row = await this.skipIfNoAction('日志', '列表无日志入口'); + await this.click(this.rowAction(row, '日志'), '日志'); + const layer = this.page.locator('.ant-modal:visible, .ant-drawer:visible, .put-page:visible').last(); + await expect(layer, '日志层应打开').toBeVisible(); + await this.assertPageHealthy('日志'); + await this.leaveWithoutCommit(); + } +} diff --git a/pages/spd/admit-product-apply.page.ts b/pages/spd/admit-product-apply.page.ts new file mode 100644 index 0000000..7561275 --- /dev/null +++ b/pages/spd/admit-product-apply.page.ts @@ -0,0 +1,173 @@ +import { expect, type Page } from '@playwright/test'; + +import { admitData, uniqProductName } from '../../data/admit.data'; +import { AdmitSharedPage } from './admit-shared.page'; + +/** 产品准入申请。新增与作废只取消,不确认保存、导入或作废。 */ +export class AdmitProductApplyPage extends AdmitSharedPage { + constructor(page: Page) { + super(page, { + path: admitData.productApplyPath, + pathPart: 'productApply/list', + menu: admitData.menuProductApply, + forbidden: ['ZRlist', 'productOrder/', 'supplyApply/', 'accessKind/'], + }); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + expect(this.page.url()).not.toContain('ZRlist'); + for (const name of ['新增产品', 'Excel导入', '生成申请单']) { + await expect(this.button(name).first(), name).toBeVisible(); + } + } + + async filterByProductName(): Promise { + await this.query(); + const name = await this.sampleCell('产品名称'); + await this.fillFilter('产品名称', name.slice(0, 40)); + await this.query(); + await this.expectRowsContain('产品名称', name); + } + + async filterByNameAndType(): Promise { + await this.query(); + const name = await this.sampleCell('产品名称'); + const type = await this.sampleCell('耗材类型'); + await this.fillFilter('产品名称', name.slice(0, 40)); + await this.chooseSelect('耗材类型', type); + await this.query(); + await this.expectRowsContain('产品名称', name); + } + + async resetFilters(): Promise { + await this.query(); + const name = await this.sampleCell('产品名称'); + await this.fillFilter('产品名称', name.slice(0, 20)); + await this.reset(); + await this.expectInputCleared('产品名称'); + await this.expectListOrEmpty(); + } + + async mockEmpty(): Promise { + await this.fillFilter('产品名称', admitData.mockGoods); + await this.query(); + await this.expectMockEmpty(); + } + + async expectCoreColumns(): Promise { + await this.query(); + await this.expectHeaders(['准入单号', '产品编号', '产品名称', '规格型号', '器械注册人', '单位', '操作']); + } + + async switchPending(): Promise { + await this.clickTab('待申请'); + await expect(this.tab('待申请')).toHaveClass(/ant-tabs-tab-active/); + await this.expectListOrEmpty(); + } + + async goPage2(): Promise { + await this.query(); + await this.goToPage2('产品名称'); + } + + async jumpPage2(): Promise { + await this.query(); + await this.jumpToPage(2); + } + + async openAddAndBack(): Promise { + const total = await this.getTotalCount(); + await this.clickButton('新增产品'); + await expect(this.put(), '新增产品页').toBeVisible(); + for (const label of ['产品名称', '器械注册人', '规格型号']) { + await expect(this.put().getByText(label).first(), label).toBeVisible(); + } + await this.leaveWithoutCommit(); + expect(await this.getTotalCount()).toBe(total); + } + + async addCancel(): Promise { + const total = await this.getTotalCount(); + const name = uniqProductName(); + await this.clickButton('新增产品'); + await expect(this.put()).toBeVisible(); + await this.fill(this.putInput('产品名称'), name, '填写新产品名称'); + await this.leaveWithoutCommit(); + await this.fillFilter('产品名称', name); + await this.query(); + await this.expectMockEmpty(); + await this.reset(); + expect(await this.getTotalCount(), '取消后总数不变').toBe(total); + } + + async excelCancel(): Promise { + const total = await this.getTotalCount(); + await this.clickButton('Excel导入'); + const layer = this.page.locator('.ant-modal:visible, .put-page:visible, .ant-drawer:visible').last(); + await expect(layer, 'Excel 导入层').toBeVisible(); + await this.leaveWithoutCommit(); + expect(await this.getTotalCount()).toBe(total); + } + + async emptyNameBlocked(): Promise { + const total = await this.getTotalCount(); + await this.clickButton('新增产品'); + await expect(this.put()).toBeVisible(); + await this.fill(this.putInput('产品名称'), '', '产品名称保持空'); + await this.saveOnPut(); + await this.expectBlockedAndStay(); + await this.leaveWithoutCommit(); + expect(await this.getTotalCount(), '空名称不应落库').toBe(total); + } + + async detailAndBack(): Promise { + await this.query(); + const row = await this.firstRow(); + const name = await this.cellText(row, '产品名称'); + await this.openPutFromRow(row, '详情'); + await expect(this.put()).toContainText(name.slice(0, 8)); + await this.leaveWithoutCommit(); + } + + async editBackfill(): Promise { + await this.clickTab('待申请'); + const row = await this.skipIfNoAction('编辑', '待申请无编辑'); + const name = await this.cellText(row, '产品名称'); + await this.openPutFromRow(row, '编辑'); + await expect(this.putInput('产品名称')).toHaveValue(name); + await this.leaveWithoutCommit(); + } + + async editCancel(): Promise { + await this.clickTab('待申请'); + const row = await this.skipIfNoAction('编辑', '待申请无编辑'); + const name = await this.cellText(row, '产品名称'); + await this.openPutFromRow(row, '编辑'); + await this.fill(this.putInput('产品名称'), `${name}-取消`, '临时改名'); + await this.leaveWithoutCommit(); + await this.clickTab('待申请'); + const again = await this.skipIfNoAction('编辑', '待申请无编辑'); + await this.openPutFromRow(again, '编辑'); + await expect(this.putInput('产品名称')).toHaveValue(name); + await this.leaveWithoutCommit(); + } + + async voidCancel(): Promise { + await this.query(); + const voidBefore = await this.readTabCount('已作废'); + await this.checkFirstRow(); + const name = await this.cellText(await this.firstRow(), '产品名称'); + await this.clickButton('作废'); + const confirm = this.page.locator('.ant-modal-confirm:visible, .ant-modal:visible').last(); + await expect(confirm).toBeVisible(); + await this.click(confirm.getByRole('button', { name: /取\s*消/ }).first(), '取消作废'); + await expect(this.page.getByText(name.slice(0, 12)).first()).toBeVisible(); + if (voidBefore !== null) expect(await this.readTabCount('已作废')).toBe(voidBefore); + } + + async passedNoEdit(): Promise { + await this.skipIfTabEmpty('已通过'); + expect(await (await this.listRows()).getByRole('button', { name: /编\s*辑/ }).count(), '已通过不应提供编辑').toBe(0); + } +} diff --git a/pages/spd/admit-product-list.page.ts b/pages/spd/admit-product-list.page.ts new file mode 100644 index 0000000..3adfd3f --- /dev/null +++ b/pages/spd/admit-product-list.page.ts @@ -0,0 +1,143 @@ +import { expect, type Page } from '@playwright/test'; + +import { admitData } from '../../data/admit.data'; +import { AdmitSharedPage } from './admit-shared.page'; + +/** 产品准入单列表。与物料单列表 URL 分离;通过只取消。 */ +export class AdmitProductListPage extends AdmitSharedPage { + constructor(page: Page) { + super(page, { + path: admitData.productListPath, + pathPart: 'productOrder/list', + menu: admitData.menuProductList, + forbidden: ['zrlist', 'productApply/', 'supplyApply/', 'accessKind/'], + }); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + expect(this.page.url()).not.toContain('zrlist'); + await expect(this.tab('已通过')).toBeVisible(); + } + + async expectNotMaterialUrl(): Promise { + expect(this.page.url()).toContain('productOrder/list'); + expect(this.page.url()).not.toContain('zrlist'); + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '制单日期', kind: 'dateRange', value: '样本' }, + { label: '销售商', kind: 'text', value: '样本' }, + { label: '耗材类型', kind: 'select', value: '样本' }, + { label: '器械注册人', kind: 'text', value: '样本' }, + { label: '注册证号', kind: 'text', value: '样本' }, + { label: '供应商', kind: 'text', value: '样本' }, + ], + { scope }, + ); + } + + async filterByProductOrFallback(): Promise { + await this.query(); + await this.queryByFirstAvailable([ + { column: '产品名称', filter: '产品名称', kind: 'input' }, + { column: '注册证号', filter: '注册证号', kind: 'input' }, + { column: '器械注册人', filter: '器械注册人', kind: 'input' }, + ]); + } + + async resetFilters(): Promise { + await this.query(); + const label = (await this.textInput('产品名称').isVisible().catch(() => false)) ? '产品名称' : '注册证号'; + await this.fillFilter(label, '样'); + await this.reset(); + await this.expectInputCleared(label); + await this.expectListOrEmpty(); + } + + async mockEmpty(): Promise { + if (await this.textInput('产品名称').isVisible().catch(() => false)) { + await this.fillFilter('产品名称', admitData.mockGoods); + } else { + await this.fillFilter('注册证号', admitData.mockCert); + } + await this.query(); + await this.expectMockEmpty(); + } + + async expectCoreColumns(): Promise { + await this.query(); + await this.expectHeaders(['准入单号', '进院供应商', '产品名称', '注册证号', '耗材类型', '产品条数', '状态', '备注', '附件', '操作']); + } + + async switchPassed(): Promise { + await this.clickTabAndMatchCount('已通过'); + await expect(this.tab('已通过')).toHaveClass(/ant-tabs-tab-active/); + } + + async detailAndBack(): Promise { + await this.query(); + const row = await this.firstRow(); + const order = await this.tryCell(row, '准入单号'); + await this.openPutFromRow(row, '详情'); + if (order) await expect(this.put()).toContainText(order.slice(0, 8)); + await this.leaveWithoutCommit(); + } + + async detailCountMatches(): Promise { + await this.query(); + const row = await this.firstRow(); + const countText = (await this.cellText(row, '产品条数')).replace(/\s/g, ''); + await this.openPutFromRow(row, '详情'); + await expect(this.put()).toContainText(countText); + await this.leaveWithoutCommit(); + } + + async openBillLog(): Promise { + await this.query(); + await this.openPutFromRow(await this.firstRow(), '详情'); + await this.openDetailSection('单据日志'); + await this.leaveWithoutCommit(); + } + + async detailSearchMock(): Promise { + await this.query(); + await this.openPutFromRow(await this.firstRow(), '详情'); + const input = this.putInput('产品名称'); + const target = (await input.isVisible().catch(() => false)) ? input : this.put().locator('input').first(); + await this.fill(target, admitData.mockGoods, '详情搜索 mock'); + const query = this.button('查询', this.put()).first(); + if (await query.isVisible().catch(() => false)) await this.click(query, '详情查询'); + await this.waitForTableReady(); + const empty = await this.put().getByText('暂无数据').first().isVisible().catch(() => false); + const rows = await this.put().locator('.ant-table-tbody > tr.ant-table-row').count(); + expect(empty || rows === 0, '详情无匹配应空态').toBeTruthy(); + await this.leaveWithoutCommit(); + } + + async passedNotConfirm(): Promise { + await this.skipIfTabEmpty('已通过'); + const row = await this.firstRow(); + const order = await this.tryCell(row, '准入单号'); + await this.openPutFromRow(row, '详情'); + const pass = this.button('通过', this.put()).first(); + const visible = await pass.isVisible().catch(() => false); + const disabled = visible && ((await pass.isDisabled().catch(() => false)) || ((await pass.getAttribute('class')) ?? '').includes('disabled')); + if (!visible || disabled) { + await this.leaveWithoutCommit(); + } else { + await this.click(pass, '通过'); + const confirm = this.page.locator('.ant-modal-confirm:visible, .ant-modal:visible').last(); + if (await confirm.isVisible().catch(() => false)) { + await this.click(confirm.getByRole('button', { name: /取\s*消/ }).first(), '取消通过'); + } + await this.leaveWithoutCommit(); + } + await this.clickTab('已通过'); + if (order) await expect(this.page.getByText(order.slice(0, 10)).first()).toBeVisible(); + } +} diff --git a/pages/spd/admit-shared.page.ts b/pages/spd/admit-shared.page.ts new file mode 100644 index 0000000..22d80a4 --- /dev/null +++ b/pages/spd/admit-shared.page.ts @@ -0,0 +1,520 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { env } from '../../config/env.config'; +import { AdmitSharedLocators } from '../../locators/spd/admit-shared.locator'; +import { BasePage } from '../../core/base.page'; +import { skipIfInsufficientForPage2 } from './helpers/pagination-page2.helper'; + +export type AdmitMeta = { + path: string; + /** URL pathname 中用于区分兄弟页的片段,如 productApply/ZRlist */ + pathPart: string; + menu: string; + /** 进入后 URL 不应包含的片段 */ + forbidden?: string[]; +}; + +const DANGER_CONFIRM = /作废|通过|提交|分配|删除|生成/; + +/** 准入管理六页共用:无 iframe、侧栏叶子菜单、表格、Tab、详情层只取消不提交。 */ +export class AdmitSharedPage extends BasePage { + readonly locators: AdmitSharedLocators; + + constructor( + page: Page, + protected readonly meta: AdmitMeta, + ) { + super(page); + this.locators = new AdmitSharedLocators(page); + } + + async open(): Promise { + await this.goto(this.meta.path); + await this.waitSpinGone(); + await this.waitSettle(400); + await this.assertPageHealthy(`打开 ${this.meta.path}`); + expect(this.page.url(), 'storageState 失效回到登录页').not.toMatch(/\/login/); + } + + async openFromMenu(): Promise { + await this.goto(env.homePath); + await this.waitSpinGone(); + await this.click(this.locators.menuBasic, '展开基础设置'); + const title = this.locators.admitSubmenuTitle.first(); + const submenu = this.page.locator('.ant-menu-submenu').filter({ has: title }).first(); + const opened = await submenu + .evaluate((el) => el.classList.contains('ant-menu-submenu-open')) + .catch(() => false); + if (!opened) await this.click(title, '展开准入管理'); + await this.click(this.locators.menuLeaf(this.meta.menu), `进入${this.meta.menu}`); + await this.page.waitForURL((url) => url.pathname.includes(this.meta.pathPart), { timeout: 20_000 }); + await this.waitSpinGone(); + await this.waitSettle(400); + await this.assertPageHealthy(`菜单进入${this.meta.menu}`); + } + + async expectOnPage(): Promise { + expect(this.page.url(), `URL 应含 ${this.meta.pathPart}`).toContain(this.meta.pathPart); + for (const part of this.meta.forbidden ?? []) { + expect(this.page.url(), `不应进入 ${part}`).not.toContain(part); + } + await expect(this.locators.button('查询').first(), '查询按钮').toBeVisible(); + expect((await this.headerTexts()).length, '列表表头').toBeGreaterThan(0); + } + + button(name: string, root?: Locator): Locator { + return this.locators.button(name, root ?? this.page); + } + + async clickButton(name: string, root?: Locator): Promise { + await this.click(this.button(name, root).first(), name); + } + + async query(): Promise { + await this.clickButton('查询'); + await this.waitForTableReady(); + } + + async reset(): Promise { + await this.clickButton('重置'); + await this.waitForTableReady(); + } + + textInput(label: string, root?: Locator): Locator { + const scope = root ?? this.page.locator('body'); + const byRole = scope.getByRole('textbox', { name: label }).first(); + return byRole.or(scope.locator('.ant-form-item').filter({ hasText: label }).locator('input').first()); + } + + formSelect(label: string, root?: Locator): Locator { + const scope = root ?? this.page.locator('body'); + return scope.locator('.ant-form-item').filter({ hasText: label }).locator('.ant-select').first(); + } + + async fillFilter(label: string, value: string): Promise { + const input = this.textInput(label); + await expect(input, `筛选「${label}」`).toBeVisible(); + await this.fill(input, value, `填写${label}`); + } + + async chooseSelect(label: string, optionText: string, root?: Locator): Promise { + const select = this.formSelect(label, root); + await this.click(select, `打开${label}`); + const dropdown = this.page.locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden)').last(); + const keyword = optionText.replace(/\s/g, '').slice(0, 20); + const search = dropdown.locator('input').first(); + if (await search.isVisible().catch(() => false)) { + await search.fill(keyword); + await this.waitSettle(200); + } + const option = dropdown.locator('.ant-select-item-option').filter({ hasText: keyword }).first(); + await this.click(option, `选择${keyword}`); + await this.waitSettle(200); + } + + async selectFirstOption(label: string, root?: Locator): Promise { + const select = this.formSelect(label, root); + if (!(await select.isVisible().catch(() => false))) return false; + await this.click(select, `打开${label}`); + const option = this.page + .locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden) .ant-select-item-option') + .first(); + if (!(await option.isVisible().catch(() => false))) return false; + await this.click(option, `选择${label}首项`); + await this.waitSettle(200); + return true; + } + + tab(name: string): Locator { + return this.locators.tab(name); + } + + async readTabCount(name: string): Promise { + const text = ((await this.tab(name).innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + const matched = text.match(/\((\d+)\)/) ?? text.match(/(\d+)$/); + return matched ? Number(matched[1]) : null; + } + + async clickTab(name: string): Promise { + await this.click(this.tab(name), `切换${name}`); + await this.waitForTableReady(); + } + + put(): Locator { + return this.locators.putPage(); + } + + async headerCells(): Promise { + const groups = this.page.locator('.ant-table-thead'); + const count = await groups.count(); + let best = groups.first().locator('th'); + let bestLen = 0; + for (let i = 0; i < count; i += 1) { + const cells = groups.nth(i).locator('th'); + const len = await cells.count(); + if (len > bestLen) { + bestLen = len; + best = cells; + } + } + return best; + } + + async headerTexts(): Promise { + return (await (await this.headerCells()).allTextContents()).map((item) => item.replace(/\s/g, '')); + } + + async expectHeaders(names: string[]): Promise { + const text = (await this.headerTexts()).join(' '); + for (const name of names) { + expect(text, `表头应含「${name}」`).toContain(name.replace(/\s/g, '')); + } + } + + async headerIndex(name: string): Promise { + const texts = await this.headerTexts(); + const index = texts.findIndex((item) => item.includes(name.replace(/\s/g, ''))); + expect(index, `找不到列「${name}」`).toBeGreaterThanOrEqual(0); + return index; + } + + async listRows(): Promise { + const bodies = this.page.locator('.ant-table-tbody'); + const count = await bodies.count(); + let best = this.page.locator('.ant-table-tbody > tr.ant-table-row:not(.ant-table-measure-row)'); + let bestWidth = -1; + for (let i = 0; i < count; i += 1) { + const body = bodies.nth(i); + const insidePut = await body.evaluate((el) => Boolean(el.closest('.put-page'))).catch(() => false); + if (insidePut) continue; + const row = body.locator('> tr.ant-table-row:not(.ant-table-measure-row)').first(); + const width = await row.locator('td').count().catch(() => 0); + if (width > bestWidth) { + bestWidth = width; + best = body.locator('> tr.ant-table-row:not(.ant-table-measure-row)'); + } + } + return best; + } + + async getDataRowCount(): Promise { + return (await this.listRows()).count(); + } + + async getTotalCount(): Promise { + const text = (await this.locators.paginationTotal.textContent().catch(() => '')) ?? ''; + const match = text.match(/共\s*(\d+)\s*条/); + return match ? Number(match[1]) : 0; + } + + async waitForTableReady(): Promise { + await this.waitSpinGone(); + await this.waitSettle(200); + await this.assertPageHealthy('列表加载'); + } + + async cellText(row: Locator, header: string): Promise { + const index = await this.headerIndex(header); + return ((await row.locator('td').nth(index).innerText()) ?? '').trim(); + } + + async tryCell(row: Locator, header: string): Promise { + try { + return (await this.cellText(row, header)).trim(); + } catch { + return ''; + } + } + + async firstRow(): Promise { + const row = (await this.listRows()).first(); + await expect(row, '列表至少应有 1 条数据').toBeVisible(); + return row; + } + + async expectListOrEmpty(): Promise { + await this.waitForTableReady(); + const rows = await this.getDataRowCount(); + if (rows > 0) { + expect(await this.getTotalCount(), '有数据时分页总数应 ≥ 1').toBeGreaterThanOrEqual(1); + return; + } + const emptyVisible = await this.locators.empty.isVisible().catch(() => false); + const emptyText = await this.page.getByText('暂无数据').first().isVisible().catch(() => false); + expect(emptyVisible || emptyText || (await this.getTotalCount()) === 0, '无数据时空态应明确').toBeTruthy(); + } + + async expectMockEmpty(): Promise { + await this.waitForTableReady(); + const rows = await this.getDataRowCount(); + const total = await this.getTotalCount(); + const empty = await this.page.getByText('暂无数据').first().isVisible().catch(() => false); + expect(rows === 0 || empty, '无匹配应空态').toBeTruthy(); + expect(total === 0 || rows === 0, '分页总数应为 0').toBeTruthy(); + } + + async expectRowsContain(header: string, keyword: string): Promise { + const needle = keyword.replace(/\s/g, '').slice(0, 12); + const rows = await this.listRows(); + const count = await rows.count(); + expect(count, `「${header}」查询应有结果`).toBeGreaterThan(0); + const limit = Math.min(count, 5); + for (let i = 0; i < limit; i += 1) { + const text = (await this.cellText(rows.nth(i), header)).replace(/\s/g, ''); + expect(text, `第 ${i + 1} 行${header}应包含关键字`).toContain(needle); + } + } + + async sampleCell(header: string): Promise { + const rows = await this.listRows(); + const count = Math.min(await rows.count(), 8); + for (let i = 0; i < count; i += 1) { + const text = (await this.tryCell(rows.nth(i), header)).replace(/\s/g, ''); + if (text && text !== '--' && text !== '-') return text; + } + await this.skipWhenDataInsufficient(`首屏「${header}」为空`); + return ''; + } + + /** 制单日期保持默认,只用样本列去点已有筛选项。 */ + async queryByFirstAvailable( + pairs: Array<{ column: string; filter: string; kind: 'input' | 'select' }>, + ): Promise { + const row = await this.firstRow(); + for (const pair of pairs) { + const value = (await this.tryCell(row, pair.column)).trim(); + if (!value || value === '--' || value === '-') continue; + if (pair.kind === 'input' && (await this.textInput(pair.filter).isVisible().catch(() => false))) { + await this.fillFilter(pair.filter, value.slice(0, 40)); + await this.query(); + await this.expectRowsContain(pair.column, value); + return; + } + if (pair.kind === 'select' && (await this.formSelect(pair.filter).isVisible().catch(() => false))) { + await this.chooseSelect(pair.filter, value); + await this.query(); + await this.expectRowsContain(pair.column, value); + return; + } + } + await this.guardedSkip('筛选区没有与首行样本对应的条件'); + } + + async collapseKeeps(label: string, value: string): Promise { + const expand = this.button('展开').first(); + if (await expand.isVisible().catch(() => false)) { + await this.click(expand, '展开筛选'); + } + await this.fillFilter(label, value); + const collapse = this.button('收起').first(); + await expect(collapse, '收起按钮').toBeVisible(); + await this.click(collapse, '收起'); + await this.click(this.button('展开').first(), '再次展开'); + await expect(this.textInput(label)).toHaveValue(value); + } + + async expectInputCleared(label: string): Promise { + const input = this.textInput(label); + if (await input.isVisible().catch(() => false)) { + await expect(input).toHaveValue(''); + } + } + + async clickTabAndMatchCount(name: string): Promise { + const marked = await this.readTabCount(name); + await this.clickTab(name); + const total = await this.getTotalCount(); + if (marked !== null) { + expect(total, `${name} Tab 计数应等于分页总数`).toBe(marked); + } + return marked; + } + + async goToPage2(identityHeader: string): Promise { + await skipIfInsufficientForPage2({ + ensureHealthyBeforeSkip: (reason) => this.ensureHealthyBeforeSkip(reason), + waitForTableReady: () => this.waitForTableReady(), + getDataRowCount: () => this.getDataRowCount(), + hasPaginationItem: async (pageNum) => + this.page.locator(`.ant-pagination-item-${pageNum}`).first().isVisible().catch(() => false), + }); + const first = await this.cellText(await this.firstRow(), identityHeader); + const item = this.page.locator('.ant-pagination-item-2').first(); + await this.click(item, '翻到第 2 页'); + await this.waitForTableReady(); + const active = ((await this.page.locator('.ant-pagination-item-active').innerText()) ?? '').trim(); + expect(active).toBe('2'); + const next = await this.cellText(await this.firstRow(), identityHeader); + expect(next, '第 2 页首行应与第 1 页不同').not.toBe(first); + } + + async jumpToPage(pageNum: number): Promise { + await skipIfInsufficientForPage2({ + ensureHealthyBeforeSkip: (reason) => this.ensureHealthyBeforeSkip(reason), + waitForTableReady: () => this.waitForTableReady(), + getDataRowCount: () => this.getDataRowCount(), + hasPaginationItem: async (n) => this.page.locator(`.ant-pagination-item-${n}`).first().isVisible().catch(() => false), + }); + if (!(await this.locators.pageJumpInput.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('无跳至输入框或仅 1 页'); + } + await this.fill(this.locators.pageJumpInput, String(pageNum), '跳至指定页'); + await this.page.keyboard.press('Enter'); + await this.waitForTableReady(); + const active = ((await this.page.locator('.ant-pagination-item-active').innerText()) ?? '').trim(); + expect(active).toBe(String(pageNum)); + } + + async expectPrevDisabled(): Promise { + await expect(this.locators.prevPage, '第 1 页上一页应不可用').toHaveClass(/ant-pagination-disabled/); + } + + rowAction(row: Locator, name: string): Locator { + const compact = name.replace(/\s/g, ''); + const pattern = new RegExp(compact.split('').join('\\s*')); + return row.getByRole('button', { name: pattern }).or(row.getByText(pattern)).first(); + } + + async rowWithAction(name: string): Promise { + const rows = await this.listRows(); + const count = Math.min(await rows.count(), 15); + for (let i = 0; i < count; i += 1) { + const row = rows.nth(i); + if (await this.rowAction(row, name).isVisible().catch(() => false)) return row; + } + return null; + } + + async skipIfNoAction(name: string, detail: string): Promise { + const row = await this.rowWithAction(name); + if (!row) await this.skipWhenDataInsufficient(detail); + return row as Locator; + } + + async skipIfTabEmpty(name: string): Promise { + const marked = await this.readTabCount(name); + await this.clickTab(name); + const rows = await this.getDataRowCount(); + if ((marked === 0 || marked === null) && rows === 0) { + await this.skipWhenDataInsufficient(`${name} Tab 无数据`); + } + } + + async openPutFromRow(row: Locator, action: string): Promise { + await this.click(this.rowAction(row, action), action); + await expect(this.put(), `${action}应打开详情或编辑层`).toBeVisible({ timeout: 15_000 }); + await this.waitSpinGone(); + await this.assertPageHealthy(action); + } + + async leaveWithoutCommit(): Promise { + for (let i = 0; i < 4; i += 1) { + const confirm = this.page.locator('.ant-modal-confirm:visible, .ant-popconfirm:visible, .ant-modal:visible').last(); + if (await confirm.isVisible().catch(() => false)) { + const text = (await confirm.innerText().catch(() => '')) ?? ''; + const cancel = confirm.getByRole('button', { name: /取\s*消/ }).first(); + if (DANGER_CONFIRM.test(text)) { + if (await cancel.isVisible().catch(() => false)) { + await this.click(cancel, '取消确认'); + await this.waitSettle(200); + continue; + } + } + const dismiss = confirm.getByRole('button', { name: /取\s*消|关\s*闭/ }).first(); + if (await dismiss.isVisible().catch(() => false)) { + await this.click(dismiss, '关闭弹层'); + await this.waitSettle(200); + continue; + } + } + const put = this.page.locator('.put-page:visible').last(); + if (!(await put.isVisible().catch(() => false))) return; + const cancel = put.getByRole('button', { name: /取\s*消/ }).first(); + const back = put.getByRole('button', { name: /返\s*回/ }).first(); + if (await cancel.isVisible().catch(() => false)) { + await this.click(cancel, '取消'); + } else if (await back.isVisible().catch(() => false)) { + await this.click(back, '返回'); + } else { + return; + } + await this.waitSettle(300); + } + await expect(this.page.locator('.put-page:visible'), '应离开详情或编辑层').toHaveCount(0); + } + + async expectBlockedAndStay(): Promise { + const hint = this.page.getByText(/必填|请输入|请选择|不能为空|不可为空|请先/).first(); + await expect(this.locators.formError.first().or(hint), '应出现必填或非法提示').toBeVisible({ timeout: 8_000 }); + const stayed = + (await this.page.locator('.put-page:visible').count()) > 0 || + (await this.page.locator('.ant-modal:visible').count()) > 0; + expect(stayed, '拦截后页面不应关闭').toBeTruthy(); + } + + async clearChecks(): Promise { + for (let i = 0; i < 8; i += 1) { + const checked = this.page.locator('.ant-table-tbody .ant-checkbox-checked').first(); + if (!(await checked.isVisible().catch(() => false))) return; + await checked.click(); + await this.waitSettle(100); + } + } + + async checkFirstRow(): Promise { + const row = await this.firstRow(); + const box = row.locator('.ant-checkbox').first(); + await this.click(box, '勾选首行'); + return row; + } + + /** 未勾选:按钮禁用,或点击后只出现提示 / 可取消的确认,不点确定。 */ + async expectNeedsSelection(name: string): Promise { + await this.clearChecks(); + const total = await this.getTotalCount(); + const button = this.button(name).first(); + await expect(button, name).toBeVisible(); + if (await button.isDisabled().catch(() => false)) return; + const className = (await button.getAttribute('class')) ?? ''; + if (className.includes('ant-btn-disabled')) return; + await this.click(button, name); + const hint = this.page.getByText(/请选择|请勾选|至少选择|未选择/).first(); + const confirm = this.page.locator('.ant-modal-confirm:visible, .ant-modal:visible').last(); + const hinted = await hint.isVisible().catch(() => false); + const confirmed = await confirm.isVisible().catch(() => false); + expect(hinted || confirmed, `${name}未勾选时应不可用或提示`).toBeTruthy(); + if (confirmed) { + const cancel = confirm.getByRole('button', { name: /取\s*消/ }).first(); + if (await cancel.isVisible().catch(() => false)) await this.click(cancel, '取消'); + } + expect(await this.getTotalCount(), '未勾选不应新增单据').toBe(total); + } + + async clickToolbarThenCancel(name: string): Promise { + await this.clickButton(name); + const layer = this.page.locator('.put-page:visible, .ant-modal:visible, .ant-drawer:visible').last(); + await expect(layer, `${name}应打开操作层`).toBeVisible({ timeout: 10_000 }); + await this.leaveWithoutCommit(); + } + + async openDetailSection(name: string): Promise { + const target = this.put().getByRole('button', { name: new RegExp(name) }).or(this.put().getByText(name)).first(); + await this.click(target, name); + await this.waitSettle(300); + await this.assertPageHealthy(name); + await expect(this.put(), '详情层仍在').toBeVisible(); + } + + putInput(label: string): Locator { + return this.textInput(label, this.put()); + } + + async saveOnPut(): Promise { + const save = this.button('保存', this.put()).first(); + if (await save.isVisible().catch(() => false)) { + await this.click(save, '保存'); + return; + } + await this.click(this.button('提交', this.put()).first(), '提交'); + } +} diff --git a/pages/spd/admit-supply.page.ts b/pages/spd/admit-supply.page.ts new file mode 100644 index 0000000..1d30b93 --- /dev/null +++ b/pages/spd/admit-supply.page.ts @@ -0,0 +1,176 @@ +import { expect, type Page } from '@playwright/test'; + +import { admitData, uniqRemark } from '../../data/admit.data'; +import { AdmitSharedPage } from './admit-shared.page'; + +/** 供货目录申请。提交与批量提交只取消,不确认。 */ +export class AdmitSupplyPage extends AdmitSharedPage { + constructor(page: Page) { + super(page, { + path: admitData.supplyPath, + pathPart: 'supplyApply/list', + menu: admitData.menuSupply, + forbidden: ['productApply/', 'productOrder/', 'accessKind/'], + }); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + await expect(this.button('新增').first()).toBeVisible(); + await expect(this.button('批量提交').first()).toBeVisible(); + } + + async filterBySupplier(): Promise { + await this.query(); + const supplier = await this.sampleCell('进院供应商'); + await this.chooseSelect('进院供应商', supplier); + await this.query(); + await this.expectRowsContain('进院供应商', supplier); + } + + async resetFilters(): Promise { + await this.query(); + const supplier = await this.sampleCell('进院供应商'); + await this.chooseSelect('进院供应商', supplier); + await this.reset(); + await this.expectListOrEmpty(); + } + + async mockEmpty(): Promise { + if (await this.textInput('变更单号').isVisible().catch(() => false)) { + await this.fillFilter('变更单号', admitData.mockOrder); + } else { + await this.fillFilter('产品名称', admitData.mockGoods); + } + await this.query(); + await this.expectMockEmpty(); + } + + async expectCoreColumns(): Promise { + await this.query(); + await this.expectHeaders(['申请单号', '申请人', '申请时间', '进院供应商', '二级供应商', '业务模式', '是否默认', '备注', '操作']); + } + + async switchPending(): Promise { + await this.clickTabAndMatchCount('待提交'); + await expect(this.tab('待提交')).toHaveClass(/ant-tabs-tab-active/); + } + + async openAddFields(): Promise { + await this.clickButton('新增'); + await expect(this.put()).toBeVisible(); + for (const label of ['进院供应商', '二级供应商', '业务模式', '是否默认', '备注']) { + await expect(this.put().getByText(label).first(), label).toBeVisible(); + } + await this.leaveWithoutCommit(); + } + + async addCancel(): Promise { + const total = await this.getTotalCount(); + const remark = uniqRemark(); + await this.clickButton('新增'); + await this.fill(this.putInput('备注'), remark, '填写备注'); + await this.leaveWithoutCommit(); + expect(await this.getTotalCount(), '未提交总数不变').toBe(total); + await expect(this.page.getByText(remark)).toHaveCount(0); + } + + async switchModeWithoutSubmit(): Promise { + const total = await this.getTotalCount(); + await this.clickButton('新增'); + await this.selectFirstOption('业务模式', this.put()); + const no = this.put().getByText('否', { exact: true }).first(); + if (await no.isVisible().catch(() => false)) await this.click(no, '切换是否默认'); + else await this.selectFirstOption('是否默认', this.put()); + await this.leaveWithoutCommit(); + expect(await this.getTotalCount()).toBe(total); + } + + async editBackfill(): Promise { + await this.clickTab('待提交'); + const row = await this.skipIfNoAction('编辑', '待提交无编辑'); + const supplier = await this.cellText(row, '进院供应商'); + await this.openPutFromRow(row, '编辑'); + await expect(this.put()).toContainText(supplier.slice(0, 6)); + await this.leaveWithoutCommit(); + } + + async editCancel(): Promise { + await this.clickTab('待提交'); + const row = await this.skipIfNoAction('编辑', '待提交无编辑'); + await this.openPutFromRow(row, '编辑'); + const input = this.putInput('备注'); + const original = await input.inputValue().catch(() => ''); + await this.fill(input, uniqRemark(), '临时备注'); + await this.leaveWithoutCommit(); + await this.clickTab('待提交'); + const again = await this.skipIfNoAction('编辑', '待提交无编辑'); + await this.openPutFromRow(again, '编辑'); + await expect(this.putInput('备注')).toHaveValue(original); + await this.leaveWithoutCommit(); + } + + async emptySupplierBlocked(): Promise { + const total = await this.getTotalCount(); + await this.clickButton('新增'); + await this.clickButton('提交', this.put()); + await this.expectBlockedAndStay(); + await this.leaveWithoutCommit(); + expect(await this.getTotalCount()).toBe(total); + } + + async noProductBlocked(): Promise { + const total = await this.getTotalCount(); + await this.clickButton('新增'); + await this.selectFirstOption('进院供应商', this.put()); + await this.selectFirstOption('二级供应商', this.put()); + await this.selectFirstOption('业务模式', this.put()); + await this.clickButton('提交', this.put()); + await this.expectBlockedAndStay(); + await this.leaveWithoutCommit(); + expect(await this.getTotalCount()).toBe(total); + } + + async batchNeedsSelection(): Promise { + await this.expectNeedsSelection('批量提交'); + } + + async batchCancel(): Promise { + await this.skipIfTabEmpty('待提交'); + const row = await this.checkFirstRow(); + const order = await this.tryCell(row, '申请单号'); + await this.clickButton('批量提交'); + const confirm = this.page.locator('.ant-modal-confirm:visible, .ant-modal:visible, .put-page:visible').last(); + await expect(confirm).toBeVisible(); + const cancel = confirm.getByRole('button', { name: /取\s*消/ }).first(); + await this.click(cancel, '取消批量提交'); + await this.clickTab('待提交'); + if (order) await expect(this.page.getByText(order.slice(0, 10)).first()).toBeVisible(); + } + + async detailAndBack(): Promise { + await this.query(); + const row = await this.firstRow(); + const order = await this.tryCell(row, '申请单号'); + await this.openPutFromRow(row, '详情'); + if (order) await expect(this.put()).toContainText(order.slice(0, 8)); + await expect(this.put().getByText('审批进程').first()).toBeVisible(); + await this.leaveWithoutCommit(); + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '制单日期', kind: 'dateRange', value: '样本' }, + { label: '销售商', kind: 'select', value: '样本' }, + { label: '供应商', kind: 'select', value: '样本' }, + { label: '变更单号', kind: 'text', value: '样本' }, + { label: '物料名称', kind: 'text', value: '样本' }, + { label: '规格型号', kind: 'text', value: '样本' }, + ], + { scope }, + ); + } +} diff --git a/pages/spd/admit-task.page.ts b/pages/spd/admit-task.page.ts new file mode 100644 index 0000000..8bb953a --- /dev/null +++ b/pages/spd/admit-task.page.ts @@ -0,0 +1,147 @@ +import { expect, type Page } from '@playwright/test'; + +import { admitData } from '../../data/admit.data'; +import { AdmitSharedPage } from './admit-shared.page'; + +/** 准入任务分配。分配只取消,不确认。 */ +export class AdmitTaskPage extends AdmitSharedPage { + constructor(page: Page) { + super(page, { + path: admitData.taskPath, + pathPart: 'accessKind/list', + menu: admitData.menuTask, + forbidden: ['productApply/', 'productOrder/', 'supplyApply/'], + }); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + await expect(this.tab('待分类')).toBeVisible(); + await expect(this.tab('已分类')).toBeVisible(); + await expect(this.button('批量分配').first()).toBeVisible(); + } + + async filterByProductName(): Promise { + await this.query(); + const name = await this.sampleCell('产品名称'); + await this.fillFilter('产品名称', name.slice(0, 40)); + await this.query(); + await this.expectRowsContain('产品名称', name); + } + + async switchTabs(): Promise { + await this.clickTabAndMatchCount('待分类'); + await this.clickTabAndMatchCount('已分类'); + await this.clickTab('待分类'); + await expect(this.tab('待分类')).toHaveClass(/ant-tabs-tab-active/); + } + + async resetFilters(): Promise { + await this.query(); + const name = await this.sampleCell('产品名称'); + await this.fillFilter('产品名称', name.slice(0, 20)); + await this.reset(); + await this.expectInputCleared('产品名称'); + await this.expectListOrEmpty(); + } + + async mockEmpty(): Promise { + await this.fillFilter('产品名称', admitData.mockGoods); + await this.query(); + await this.expectMockEmpty(); + } + + async expectCoreColumns(): Promise { + await this.clickTab('待分类'); + await this.expectHeaders(['准入类型', '产品编号', '产品名称', '规格型号', '规格', '型号', '器械注册人', '操作']); + if ((await this.getDataRowCount()) > 0) { + await expect(this.rowAction(await this.firstRow(), '分配类型')).toBeVisible(); + } + } + + async goPage2(): Promise { + await this.query(); + await this.goToPage2('产品名称'); + } + + async jumpPage2(): Promise { + await this.query(); + await this.jumpToPage(2); + } + + async prevDisabled(): Promise { + await this.query(); + await this.expectPrevDisabled(); + } + + async assignCancel(): Promise { + await this.clickTab('待分类'); + const before = await this.readTabCount('待分类'); + const row = await this.skipIfNoAction('分配类型', '待分类无分配类型'); + const name = await this.cellText(row, '产品名称'); + await this.click(this.rowAction(row, '分配类型'), '分配类型'); + const layer = this.page.locator('.put-page:visible, .ant-modal:visible, .ant-drawer:visible').last(); + await expect(layer, '分配层应打开').toBeVisible(); + await this.leaveWithoutCommit(); + await this.clickTab('待分类'); + await expect(this.page.getByText(name.slice(0, 12)).first()).toBeVisible(); + if (before !== null) expect(await this.readTabCount('待分类')).toBe(before); + } + + async batchNeedsSelection(): Promise { + await this.clickTab('待分类'); + await this.expectNeedsSelection('批量分配'); + } + + async batchCancel(): Promise { + await this.clickTab('待分类'); + if ((await this.getDataRowCount()) === 0) await this.skipWhenDataInsufficient('待分类无数据'); + const before = await this.readTabCount('待分类'); + const row = await this.checkFirstRow(); + const name = await this.cellText(row, '产品名称'); + await this.clickButton('批量分配'); + const layer = this.page.locator('.put-page:visible, .ant-modal:visible, .ant-drawer:visible').last(); + await expect(layer).toBeVisible(); + await this.leaveWithoutCommit(); + await this.clickTab('待分类'); + await expect(this.page.getByText(name.slice(0, 12)).first()).toBeVisible(); + if (before !== null) expect(await this.readTabCount('待分类')).toBe(before); + } + + async classifiedNoAssign(): Promise { + await this.skipIfTabEmpty('已分类'); + const assign = (await this.listRows()).getByRole('button', { name: /分配类型/ }); + if ((await assign.count()) === 0) return; + const before = await this.readTabCount('已分类'); + await this.click(assign.first(), '已分类分配类型'); + const hint = this.page.getByText(/不可|不允许|不能|已分类/).first(); + const layer = this.page.locator('.ant-modal:visible, .put-page:visible').last(); + expect( + (await hint.isVisible().catch(() => false)) || (await layer.isVisible().catch(() => false)), + '已分类再分配应被拒绝或需取消', + ).toBeTruthy(); + await this.leaveWithoutCommit(); + if (before !== null) expect(await this.readTabCount('已分类')).toBe(before); + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '新增日期', kind: 'dateRange', value: '样本' }, + { label: '操作日期', kind: 'dateRange', value: '样本' }, + { label: '销售商', kind: 'text', value: '样本' }, + { label: '物料名称', kind: 'text', value: '样本' }, + { label: '注册证号', kind: 'text', value: '样本' }, + { label: '操作人', kind: 'text', value: '样本' }, + { label: '器械注册人', kind: 'text', value: '样本' }, + { label: '耗材类型', kind: 'select', value: '样本' }, + { label: '规格型号', kind: 'text', value: '样本' }, + { label: '准入类型', kind: 'select', value: '样本' }, + { label: '准入状态', kind: 'select', value: '样本' }, + ], + { scope }, + ); + } +} diff --git a/pages/spd/basic-branch.page.ts b/pages/spd/basic-branch.page.ts new file mode 100644 index 0000000..84b1222 --- /dev/null +++ b/pages/spd/basic-branch.page.ts @@ -0,0 +1,232 @@ +import { expect, type Page } from '@playwright/test'; + +import { basicHospitalData, uniqCampusName } from '../../data/basic-hospital.data'; +import { BasicBranchLocators } from '../../locators/spd/basic-branch.locator'; +import { BasicHospitalSharedPage } from './basic-shared.page'; + +export class BasicBranchPage extends BasicHospitalSharedPage { + readonly locators: BasicBranchLocators; + + constructor(page: Page) { + super(page); + this.locators = new BasicBranchLocators(page); + } + + async open(): Promise { + await this.openPath(basicHospitalData.branchPath); + } + + async resetSessionForReuse(): Promise { + await this.closeDialogIfOpen(); + if (await this.locators.campusNameInput.isVisible().catch(() => false)) { + await this.locators.campusNameInput.fill(''); + await this.page.keyboard.press('Enter').catch(() => undefined); + await this.waitForTableReady(); + } + } + + async expectEntered(): Promise { + await this.expectUrlContains('/spd/basic/hospital/paltform/area'); + await this.expectNotOnSiblingPages(['/paltform/notice', '/paltform/info', '/balance/month']); + await expect(this.locators.addBtn).toBeVisible(); + await expect(this.locators.campusNameInput).toBeVisible(); + await expect(this.shared.tableHeader.first()).toBeVisible(); + await expect(this.page.getByRole('button', { name: /查\s*询/ })).toHaveCount(0); + await expect(this.page.getByRole('button', { name: /重\s*置/ })).toHaveCount(0); + } + + async extractSample(): Promise<{ sampleCampusName: string; sampleCampusId: string; sampleCampusAddress: string }> { + const row = await this.firstRow(); + return { + sampleCampusName: await this.cellText(row, '院区名称'), + sampleCampusId: await this.cellText(row, '院区编号'), + sampleCampusAddress: await this.cellText(row, '院区地址'), + }; + } + + async searchByName(name: string): Promise { + await this.fill(this.locators.campusNameInput, name, '填写院区名称'); + await this.page.keyboard.press('Enter'); + await this.waitForTableReady(); + } + + async expectNameResults(keyword: string): Promise { + const count = await this.getDataRowCount(); + expect(count).toBeGreaterThan(0); + for (let i = 0; i < Math.min(count, 5); i += 1) { + const text = await this.cellText(this.dataRows().nth(i), '院区名称'); + expect(text).toContain(keyword.slice(0, Math.min(keyword.length, 6))); + } + } + + async expectCoreColumns(): Promise { + await this.expectHeaders(['序号', '院区编号', '院区名称', '院区地址', '状态', '启用云仓', '操作']); + await expect((await this.firstRow()).getByRole('button', { name: /编\s*辑/ })).toBeVisible(); + } + + async clearSearch(): Promise { + const filtered = await this.getTotalCount(); + await this.locators.campusNameInput.fill(''); + await this.page.keyboard.press('Enter'); + await this.waitForTableReady(); + const restored = await this.getTotalCount(); + expect(restored, '清空后条数应不少于过滤后').toBeGreaterThanOrEqual(filtered); + } + + async expectMockEmpty(): Promise { + const rows = await this.getDataRowCount(); + const empty = await this.page.getByText('暂无数据').first().isVisible().catch(() => false); + expect(rows === 0 || empty).toBeTruthy(); + expect(await this.getTotalCount()).toBe(0); + } + + async openAddDialog(): Promise { + await this.click(this.locators.addBtn, '新 增'); + await expect(this.shared.dialog).toContainText('新增院区'); + await expect(this.locators.addName).toBeVisible(); + await expect(this.dialogFormItem('所在地区')).toBeVisible(); + await expect(this.locators.addAddress).toBeVisible(); + await expect(this.shared.dialog.getByText('主院区')).toBeVisible(); + await expect(this.shared.dialog.getByText('停用')).toBeVisible(); + await expect(this.shared.dialog.getByText('启用').first()).toBeVisible(); + await expect(this.locators.editId).toHaveCount(0); + await expect(this.shared.dialog.getByText('启用云仓')).toHaveCount(0); + } + + async addAndCancel(): Promise { + const total = await this.getTotalCount(); + const name = uniqCampusName(); + await this.openAddDialog(); + await this.fill(this.locators.addName, name, '填写院区名称'); + await this.click(this.shared.dialog.getByRole('button', { name: /取\s*消/ }), '取 消'); + await expect(this.shared.dialog).toBeHidden({ timeout: 8_000 }); + expect(await this.getTotalCount()).toBe(total); + await this.searchByName(name); + await this.expectMockEmpty(); + await this.locators.campusNameInput.fill(''); + await this.page.keyboard.press('Enter'); + await this.waitForTableReady(); + } + + async addSuccessAndCleanup(): Promise { + const name = uniqCampusName(); + await this.openAddDialog(); + await this.fill(this.locators.addName, name, '填写院区名称'); + await this.pickCascaderFirstPath(); + await this.fill(this.locators.addAddress, '自动化测试地址', '填写详细地址'); + await this.dialogFormItem('主院区').getByText('否', { exact: true }).click(); + await this.dialogFormItem('状态').getByText('启用', { exact: true }).click(); + await this.click(this.shared.dialog.getByRole('button', { name: /确\s*定/ }), '确 定'); + await expect(this.shared.dialog).toBeHidden({ timeout: 15_000 }); + await this.searchByName(name); + expect(await this.cellText(await this.firstRow(), '院区名称')).toContain(name); + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + await expect(this.shared.dialog).toBeVisible(); + const disable = this.dialogFormItem('状态').getByText(/禁用|停用/).first(); + if (await disable.isVisible().catch(() => false)) { + await disable.click(); + await this.click(this.shared.dialog.getByRole('button', { name: /确\s*定/ }), '停用测试院区'); + await this.waitSpinGone(); + } else { + await this.closeDialogIfOpen(); + } + } + + async addEmptyName(): Promise { + await this.openAddDialog(); + await this.pickCascaderFirstPath(); + await this.fill(this.locators.addAddress, basicHospitalData.mockAddr, '填写地址'); + await this.click(this.shared.dialog.getByRole('button', { name: /确\s*定/ }), '确 定'); + await this.expectFormError(); + await expect(this.shared.dialog).toBeVisible(); + await this.closeDialogIfOpen(); + } + + async addEmptyRegion(): Promise { + await this.openAddDialog(); + await this.fill(this.locators.addName, basicHospitalData.mockCampusNoRegion, '填写名称'); + await this.fill(this.locators.addAddress, basicHospitalData.mockAddr, '填写地址'); + await this.click(this.shared.dialog.getByRole('button', { name: /确\s*定/ }), '确 定'); + await this.expectFormError(); + await expect(this.shared.dialog).toBeVisible(); + await this.closeDialogIfOpen(); + } + + async addEmptyAddress(): Promise { + await this.openAddDialog(); + await this.fill(this.locators.addName, basicHospitalData.mockCampusNoAddr, '填写名称'); + await this.pickCascaderFirstPath(); + await this.click(this.shared.dialog.getByRole('button', { name: /确\s*定/ }), '确 定'); + await this.expectFormError(); + await expect(this.shared.dialog).toBeVisible(); + await this.closeDialogIfOpen(); + } + + async openEditAndExpectBackfill(): Promise { + const sample = await this.extractSample(); + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + await expect(this.shared.dialog).toContainText('编辑院区'); + expect(await this.locators.editId.inputValue()).toBe(sample.sampleCampusId); + expect(await this.locators.editName.inputValue()).toBe(sample.sampleCampusName); + expect(await this.locators.editAddress.inputValue()).toContain(sample.sampleCampusAddress.slice(0, 4)); + await expect(this.shared.dialog.getByText('启用云仓')).toBeVisible(); + await this.closeDialogIfOpen(); + } + + async expectIdNotChangeable(): Promise { + const sample = await this.extractSample(); + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + await expect(this.locators.editId).toBeVisible(); + expect(await this.locators.editId.inputValue()).toBe(sample.sampleCampusId); + const disabled = await this.locators.editId.isDisabled().catch(() => false); + const readonly = await this.locators.editId.getAttribute('readonly'); + if (!disabled && readonly === null) { + await this.fill(this.locators.editId, basicHospitalData.mockCampusId, '尝试改编号'); + await this.click(this.shared.dialog.getByRole('button', { name: /确\s*定/ }), '确 定'); + await this.waitSpinGone(); + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + expect(await this.locators.editId.inputValue()).toBe(sample.sampleCampusId); + } + await this.closeDialogIfOpen(); + } + + async editAndCancel(): Promise { + const sample = await this.extractSample(); + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + await this.fill(this.locators.editName, uniqCampusName(), '改名称'); + await this.click(this.shared.dialog.getByRole('button', { name: /取\s*消/ }), '取 消'); + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + expect(await this.locators.editName.inputValue()).toBe(sample.sampleCampusName); + await this.closeDialogIfOpen(); + } + + async expectEditStatusLabels(): Promise { + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + await expect(this.shared.dialog.getByText('禁用')).toBeVisible(); + await expect(this.shared.dialog.getByText('启用').first()).toBeVisible(); + await expect(this.dialogFormItem('状态').getByText('停用')).toHaveCount(0); + await this.closeDialogIfOpen(); + } + + async expectCloudWarehouse(): Promise { + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + await expect(this.dialogFormItem('启用云仓').getByText('是', { exact: true })).toBeVisible(); + await expect(this.dialogFormItem('启用云仓').getByText('否', { exact: true })).toBeVisible(); + await this.closeDialogIfOpen(); + } + + async editClearRequired(): Promise { + const sample = await this.extractSample(); + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + await this.locators.editName.fill(''); + await this.click(this.shared.dialog.getByRole('button', { name: /确\s*定/ }), '确 定'); + await this.expectFormError(); + await expect(this.shared.dialog).toBeVisible(); + await this.closeDialogIfOpen(); + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + expect(await this.locators.editName.inputValue()).toBe(sample.sampleCampusName); + await this.closeDialogIfOpen(); + } +} + +export const BASIC_BRANCH_PATH = basicHospitalData.branchPath; diff --git a/pages/spd/basic-finance-month.page.ts b/pages/spd/basic-finance-month.page.ts new file mode 100644 index 0000000..ccc202e --- /dev/null +++ b/pages/spd/basic-finance-month.page.ts @@ -0,0 +1,224 @@ +import { expect, type Locator, type Page, test } from '@playwright/test'; + +import { basicHospitalData } from '../../data/basic-hospital.data'; +import { BasicFinanceMonthLocators } from '../../locators/spd/basic-finance-month.locator'; +import { BasicHospitalSharedPage } from './basic-shared.page'; + +export type FinanceSample = { + sampleFinanceMonth: string; + sampleStartDate: string; + sampleEndDate: string; + sampleAutoSettleDate: string; +}; + +export class BasicFinanceMonthPage extends BasicHospitalSharedPage { + readonly locators: BasicFinanceMonthLocators; + + constructor(page: Page) { + super(page); + this.locators = new BasicFinanceMonthLocators(page); + } + + async open(): Promise { + await this.openPath(basicHospitalData.financeMonthPath); + } + + async search(): Promise { + await this.click(this.locators.searchBtn, '查 询'); + await this.waitForTableReady(); + } + + async resetSessionForReuse(options?: { search?: boolean }): Promise { + if (await this.locators.cancelBtn.isVisible().catch(() => false)) { + await this.click(this.locators.cancelBtn, '取消行内编辑'); + } + if (await this.locators.resetBtn.isVisible().catch(() => false)) { + await this.click(this.locators.resetBtn, '重 置'); + await this.waitForTableReady(); + } + if (options?.search) await this.search(); + } + + async expectEntered(): Promise { + await this.expectUrlContains('/spd/basic/hospital/balance/month'); + await this.expectNotOnSiblingPages(['/paltform/notice', '/paltform/info', '/paltform/area']); + await expect(this.locators.settleMonth).toBeVisible(); + await expect(this.locators.searchBtn).toBeVisible(); + await expect(this.locators.resetBtn).toBeVisible(); + await expect(this.shared.tableHeader.first()).toBeVisible(); + } + + currentMonthRow(): Locator { + return this.dataRows().filter({ hasText: '当前月份' }).first(); + } + + historyRows(): Locator { + return this.dataRows().filter({ hasText: '历史月份' }); + } + + async extractSample(): Promise { + await this.search(); + const row = (await this.currentMonthRow().isVisible().catch(() => false)) + ? this.currentMonthRow() + : await this.firstRow(); + return { + sampleFinanceMonth: await this.cellText(row, '财务月份'), + sampleStartDate: await this.cellText(row, '开始日期'), + sampleEndDate: await this.cellText(row, '结束日期'), + sampleAutoSettleDate: await this.cellText(row, '自动结算日期'), + }; + } + + async queryByMonth(month: string): Promise { + await this.click(this.locators.settleMonth, '打开财务月份'); + const panel = this.page.locator('.ant-picker-dropdown:visible').last(); + const opt = panel.getByText(month, { exact: true }).first(); + if (await opt.isVisible().catch(() => false)) { + await this.click(opt, `选择 ${month}`); + } else { + await this.locators.settleMonth.fill(month); + await this.page.keyboard.press('Enter'); + } + await this.search(); + } + + async expectMonthResults(month: string): Promise { + const count = await this.getDataRowCount(); + expect(count).toBeGreaterThan(0); + for (let i = 0; i < Math.min(count, 5); i += 1) { + const text = await this.cellText(this.dataRows().nth(i), '财务月份'); + expect(text).toContain(month); + } + } + + async resetFilters(): Promise { + await this.click(this.locators.resetBtn, '重 置'); + await this.waitForTableReady(); + } + + async expectCoreColumns(): Promise { + await this.expectHeaders(['财务月份', '开始日期', '结束日期', '自动结算日期', '状态', '操作']); + } + + async expectOnlyCurrentEditable(): Promise { + const current = this.currentMonthRow(); + await expect(current, '应存在当前月份行').toBeVisible(); + await expect(current.getByRole('button', { name: /修\s*改/ })).toBeVisible(); + const history = this.historyRows(); + const historyCount = await history.count(); + for (let i = 0; i < historyCount; i += 1) { + await expect(history.nth(i).getByRole('button', { name: /修\s*改/ })).toHaveCount(0); + } + } + + async expectAtMostOneCurrent(): Promise { + const count = await this.dataRows().filter({ hasText: '当前月份' }).count(); + expect(count, '当前月份至多一行').toBeLessThanOrEqual(1); + } + + async expectHistoryNotEditable(): Promise { + const history = this.historyRows(); + if ((await history.count()) === 0) { + test.skip(true, '无历史月份'); + } + await history.first().click({ trial: true }).catch(() => undefined); + await expect(this.locators.saveBtn).toBeHidden(); + await expect(this.locators.autoSettleDateInput).toBeHidden(); + } + + async enterInlineEdit(): Promise { + const row = this.currentMonthRow(); + await expect(row).toBeVisible(); + await this.click(row.getByRole('button', { name: /修\s*改/ }), '修 改'); + await expect(this.locators.saveBtn).toBeVisible(); + await expect(this.locators.cancelBtn).toBeVisible(); + await expect(this.locators.autoSettleDateInput).toBeVisible(); + } + + async expectStartEndReadonly(): Promise { + await this.enterInlineEdit(); + const row = this.currentMonthRow(); + const startInput = row.locator('td').nth(await this.headerIndex('开始日期')).locator('input'); + const endInput = row.locator('td').nth(await this.headerIndex('结束日期')).locator('input'); + if (await startInput.count()) await expect(startInput.first()).toBeDisabled(); + if (await endInput.count()) await expect(endInput.first()).toBeDisabled(); + await expect(this.locators.autoSettleDateInput).toBeEditable(); + await this.click(this.locators.cancelBtn, '取 消'); + } + + async pickAltDateInRange(start: string, end: string, current: string): Promise { + const startDay = Number(start.slice(-2)); + const endDay = Number(end.slice(-2)); + const curDay = Number(current.slice(-2)); + let next = curDay === startDay ? Math.min(endDay, startDay + 1) : startDay; + if (next === curDay) next = Math.min(endDay, curDay + 1); + return `${start.slice(0, 8)}${String(next).padStart(2, '0')}`; + } + + async fillAutoSettle(date: string): Promise { + await this.locators.autoSettleDateInput.click(); + await this.locators.autoSettleDateInput.fill(date); + await this.page.keyboard.press('Enter'); + await this.waitSettle(200); + } + + async cancelWithoutSave(): Promise { + const sample = await this.extractSample(); + await this.enterInlineEdit(); + const alt = await this.pickAltDateInRange(sample.sampleStartDate, sample.sampleEndDate, sample.sampleAutoSettleDate); + await this.fillAutoSettle(alt); + await this.click(this.locators.cancelBtn, '取 消'); + const shown = await this.cellText(this.currentMonthRow(), '自动结算日期'); + expect(shown).toContain(sample.sampleAutoSettleDate); + } + + async saveAndRestore(): Promise { + const sample = await this.extractSample(); + await this.enterInlineEdit(); + const alt = await this.pickAltDateInRange(sample.sampleStartDate, sample.sampleEndDate, sample.sampleAutoSettleDate); + await this.fillAutoSettle(alt); + await this.click(this.locators.saveBtn, '保 存'); + await this.waitSpinGone(); + await this.search(); + expect(await this.cellText(this.currentMonthRow(), '自动结算日期')).toContain(alt); + await this.enterInlineEdit(); + await this.fillAutoSettle(sample.sampleAutoSettleDate); + await this.click(this.locators.saveBtn, '恢复自动结算日'); + await this.waitSpinGone(); + } + + async saveEmptyDate(): Promise { + const sample = await this.extractSample(); + await this.enterInlineEdit(); + await this.locators.autoSettleDateInput.fill(''); + await this.page.keyboard.press('Escape').catch(() => undefined); + await this.click(this.locators.saveBtn, '保 存'); + await this.expectFormError(); + const shown = await this.cellText(this.currentMonthRow(), '自动结算日期'); + expect(shown.includes(sample.sampleAutoSettleDate) || shown.length > 0).toBeTruthy(); + if (await this.locators.cancelBtn.isVisible().catch(() => false)) { + await this.click(this.locators.cancelBtn, '取 消'); + } + } + + async saveOutOfRange(): Promise { + const sample = await this.extractSample(); + await this.enterInlineEdit(); + try { + await this.fillAutoSettle(basicHospitalData.mockOutOfRangeDate); + await this.click(this.locators.saveBtn, '保 存'); + await this.expectFormError(); + const shown = await this.cellText(this.currentMonthRow(), '自动结算日期'); + expect(shown).not.toContain('1999'); + } catch { + test.skip(true, '日期控件无法选出区间外日期'); + } finally { + if (await this.locators.cancelBtn.isVisible().catch(() => false)) { + await this.click(this.locators.cancelBtn, '取 消'); + } + } + expect(await this.cellText(this.currentMonthRow(), '自动结算日期')).toContain(sample.sampleAutoSettleDate); + } +} + +export const BASIC_FINANCE_MONTH_PATH = basicHospitalData.financeMonthPath; diff --git a/pages/spd/basic-hospital-info.page.ts b/pages/spd/basic-hospital-info.page.ts new file mode 100644 index 0000000..8c15d9e --- /dev/null +++ b/pages/spd/basic-hospital-info.page.ts @@ -0,0 +1,113 @@ +import { expect, type Page, test } from '@playwright/test'; + +import { basicHospitalData, uniqLinkman } from '../../data/basic-hospital.data'; +import { BasicHospitalInfoLocators } from '../../locators/spd/basic-hospital-info.locator'; +import { BasicHospitalSharedPage } from './basic-shared.page'; + +export class BasicHospitalInfoPage extends BasicHospitalSharedPage { + readonly locators: BasicHospitalInfoLocators; + + constructor(page: Page) { + super(page); + this.locators = new BasicHospitalInfoLocators(page); + } + + async open(): Promise { + await this.openPath(basicHospitalData.hospitalInfoPath); + } + + async expectEntered(): Promise { + await this.expectUrlContains('/spd/basic/hospital/paltform/info'); + await this.expectNotOnSiblingPages(['/paltform/notice', '/paltform/area', '/balance/month']); + await expect(this.locators.editBtn, '查看态编辑').toBeVisible(); + await expect(this.page.getByText('医院编码').first()).toBeVisible(); + await expect(this.page.getByText('医院名称').first()).toBeVisible(); + await expect(this.page.getByText(/地址/).first()).toBeVisible(); + } + + async expectViewReadonly(): Promise { + await expect(this.locators.saveBtn).toBeHidden(); + await expect(this.locators.cancelBtn).toBeHidden(); + await this.expectSystemFieldsReadonly(); + } + + async expectSystemFieldsReadonly(): Promise { + for (const label of ['医院编码', '医院名称']) { + const input = this.formItem(label).locator('input:not([type="hidden"])'); + if (await input.count()) { + await expect(input.first(), `${label} 应只读`).toBeDisabled(); + } + } + } + + async previewIfPresent(): Promise { + const preview = this.locators.previewLink.first(); + if (!(await preview.isVisible().catch(() => false))) { + test.skip(true, '均无预览入口'); + } + await this.click(preview, '预览文件'); + const previewLayer = this.page.locator('.ant-image-preview, .ant-modal, .ant-image').first(); + await expect(previewLayer.or(this.page.locator('img').first()), '预览应打开').toBeVisible({ timeout: 8_000 }); + await this.page.keyboard.press('Escape').catch(() => undefined); + } + + async enterEdit(): Promise { + await this.click(this.locators.editBtn, '编 辑'); + await expect(this.locators.saveBtn).toBeVisible(); + await expect(this.locators.cancelBtn).toBeVisible(); + await expect(this.locators.linkman).toBeEditable(); + await expect(this.locators.contactWay).toBeEditable(); + await expect(this.locators.shortName).toBeEditable(); + } + + async readLinkman(): Promise { + if (await this.locators.linkman.isVisible().catch(() => false)) { + return this.locators.linkman.inputValue(); + } + return ((await this.formItem('联系人').innerText()) ?? '').replace(/联系人\*?/, '').trim(); + } + + async cancelWithoutSave(): Promise { + await this.enterEdit(); + const original = await this.readLinkman(); + await this.fill(this.locators.linkman, uniqLinkman(), '改联系人'); + await this.click(this.locators.cancelBtn, '取 消'); + await expect(this.locators.saveBtn).toBeHidden(); + const shown = await this.readLinkman(); + expect(shown).toContain(original); + } + + async saveAndRestore(): Promise { + await this.enterEdit(); + const original = await this.readLinkman(); + const next = uniqLinkman(); + await this.fill(this.locators.linkman, next, '改联系人'); + await this.click(this.locators.saveBtn, '保 存'); + await this.waitSpinGone(); + await this.open(); + await this.enterEdit(); + expect(await this.readLinkman()).toBe(next); + await this.fill(this.locators.linkman, original, '恢复联系人'); + await this.click(this.locators.saveBtn, '保 存恢复'); + await this.waitSpinGone(); + } + + async saveEmptyField(field: 'linkman' | 'contactWay' | 'shortName'): Promise { + await this.enterEdit(); + const locator = this.locators[field]; + await locator.fill(''); + await this.click(this.locators.saveBtn, '保 存'); + await this.expectFormError(); + await expect(this.locators.saveBtn, '仍停留编辑态').toBeVisible(); + await this.click(this.locators.cancelBtn, '取 消'); + } + + async expectUploadInputs(): Promise { + await this.enterEdit(); + await expect(this.locators.loginBgUpload).toHaveCount(1); + await expect(this.page.locator('input[type="file"]')).toHaveCount(2); + await this.click(this.locators.cancelBtn, '取 消'); + } +} + +export const BASIC_HOSPITAL_INFO_PATH = basicHospitalData.hospitalInfoPath; diff --git a/pages/spd/basic-notice.page.ts b/pages/spd/basic-notice.page.ts new file mode 100644 index 0000000..f171507 --- /dev/null +++ b/pages/spd/basic-notice.page.ts @@ -0,0 +1,242 @@ +import { expect, type Page } from '@playwright/test'; + +import { + basicHospitalData, + uniqNoticeTitle, +} from '../../data/basic-hospital.data'; +import { BasicNoticeLocators } from '../../locators/spd/basic-notice.locator'; +import { skipIfInsufficientForPage2 } from './helpers/pagination-page2.helper'; +import { BasicHospitalSharedPage } from './basic-shared.page'; + +export class BasicNoticePage extends BasicHospitalSharedPage { + readonly locators: BasicNoticeLocators; + + constructor(page: Page) { + super(page); + this.locators = new BasicNoticeLocators(page); + } + + async open(): Promise { + await this.openPath(basicHospitalData.noticePath); + } + + async search(): Promise { + await this.click(this.locators.searchBtn, '查 询'); + await this.waitForTableReady(); + } + + async resetSessionForReuse(options?: { search?: boolean }): Promise { + await this.closeDialogIfOpen(); + if (await this.locators.resetBtn.isVisible().catch(() => false)) { + await this.click(this.locators.resetBtn, '重 置'); + await this.waitForTableReady(); + } + if (options?.search) await this.search(); + } + + async expectEntered(): Promise { + await this.expectUrlContains('/spd/basic/hospital/paltform/notice'); + await this.expectNotOnSiblingPages(['/paltform/info', '/paltform/area', '/balance/month']); + await expect(this.locators.noticeTitle, '筛选区公告标题').toBeVisible(); + await expect(this.locators.addBtn, '新增按钮').toBeVisible(); + await expect(this.shared.tableHeader.first(), '列表表头').toBeVisible(); + } + + async extractSample(): Promise<{ sampleNoticeTitle: string; samplePublishScope: string }> { + await this.search(); + const row = await this.firstRow(); + return { + sampleNoticeTitle: await this.cellText(row, '公告标题'), + samplePublishScope: await this.cellText(row, '发布范围'), + }; + } + + async queryByTitle(title: string): Promise { + await this.fill(this.locators.noticeTitle, title, '填写公告标题'); + await this.search(); + } + + async expectTitleResultsMatch(keyword: string): Promise { + const count = await this.getDataRowCount(); + expect(count, '标题查询应有结果').toBeGreaterThan(0); + for (let i = 0; i < Math.min(count, 5); i += 1) { + const text = await this.cellText(this.dataRows().nth(i), '公告标题'); + expect(text, `第 ${i + 1} 行标题应包含关键字`).toContain(keyword.slice(0, Math.min(keyword.length, 8))); + } + } + + async queryByScope(scope: string): Promise { + await this.selectDropdown(this.locators.publishScopeSelect, scope); + await this.search(); + } + + async expectScopeResultsMatch(scope: string): Promise { + if (scope === '全部') return; + const count = await this.getDataRowCount(); + expect(count, '范围查询应有结果或空态可接受').toBeGreaterThanOrEqual(0); + for (let i = 0; i < Math.min(count, 5); i += 1) { + const text = await this.cellText(this.dataRows().nth(i), '发布范围'); + expect(text, `第 ${i + 1} 行发布范围`).toContain(scope); + } + } + + async queryByTitleAndScope(title: string, scope: string): Promise { + await this.fill(this.locators.noticeTitle, title, '填写公告标题'); + if (scope && scope !== '全部') { + await this.selectDropdown(this.locators.publishScopeSelect, scope); + } + await this.search(); + } + + async resetFilters(): Promise { + await this.click(this.locators.resetBtn, '重 置'); + await this.waitForTableReady(); + await expect(this.locators.noticeTitle).toHaveValue(''); + } + + async expectMockEmpty(): Promise { + await this.waitForTableReady(); + const rows = await this.getDataRowCount(); + const total = await this.getTotalCount(); + const empty = await this.page.getByText('暂无数据').first().isVisible().catch(() => false); + expect(rows === 0 || empty, '无匹配应空态').toBeTruthy(); + expect(total === 0 || rows === 0, '分页总数应为 0').toBeTruthy(); + } + + async expectCoreColumns(): Promise { + await this.expectHeaders(['公告编号', '公告标题', '发布范围', '发布科室', '发布人', '操作']); + const row = await this.firstRow(); + await expect(row.getByRole('button', { name: /编\s*辑/ })).toBeVisible(); + await expect(row.getByRole('button', { name: /删\s*除/ })).toBeVisible(); + } + + async goToPage2(): Promise { + await skipIfInsufficientForPage2({ + ensureHealthyBeforeSkip: (reason) => this.ensureHealthyBeforeSkip(reason), + waitForTableReady: () => this.waitForTableReady(), + getDataRowCount: () => this.getDataRowCount(), + hasPaginationItem: (n) => this.hasPaginationItem(n), + }); + const firstNo = await this.cellText(await this.firstRow(), '公告编号'); + await this.clickPaginationPage(2); + await this.waitForTableReady(); + const active = ((await this.page.locator('.ant-pagination-item-active').innerText()) ?? '').trim(); + expect(active).toBe('2'); + const nextNo = await this.cellText(await this.firstRow(), '公告编号'); + expect(nextNo, '第 2 页首行应与第 1 页不同').not.toBe(firstNo); + } + + async jumpToPage(pageNum: number): Promise { + await skipIfInsufficientForPage2({ + ensureHealthyBeforeSkip: (reason) => this.ensureHealthyBeforeSkip(reason), + waitForTableReady: () => this.waitForTableReady(), + getDataRowCount: () => this.getDataRowCount(), + hasPaginationItem: (n) => this.hasPaginationItem(n), + }); + if (!(await this.locators.pageJumpInput.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('无 jumper 或仅 1 页'); + } + await this.fill(this.locators.pageJumpInput, String(pageNum), '跳至指定页'); + await this.page.keyboard.press('Enter'); + await this.waitForTableReady(); + const active = ((await this.page.locator('.ant-pagination-item-active').innerText()) ?? '').trim(); + expect(active).toBe(String(pageNum)); + } + + async openAddDialog(): Promise { + await this.click(this.locators.addBtn, '新 增'); + await expect(this.shared.dialog).toBeVisible(); + await expect(this.shared.dialog).toContainText('新增公告'); + await expect(this.locators.modalTitleInput).toBeVisible(); + await expect(this.shared.dialog.getByText('发布范围')).toBeVisible(); + await expect(this.shared.dialog.getByText('置顶')).toBeVisible(); + await expect(this.shared.dialog.getByText('截止日期')).toBeVisible(); + await expect(this.locators.richTextEditor).toBeVisible(); + await expect(this.shared.dialog.getByRole('button', { name: /取\s*消/ })).toBeVisible(); + await expect(this.shared.dialog.getByRole('button', { name: /发\s*布/ })).toBeVisible(); + } + + async addAndCancel(title: string): Promise { + await this.openAddDialog(); + await this.fill(this.locators.modalTitleInput, title, '填写新增标题'); + await this.locators.richTextEditor.fill('自动化取消用例正文'); + await this.click(this.shared.dialog.getByRole('button', { name: /取\s*消/ }), '取 消'); + await expect(this.shared.dialog).toBeHidden({ timeout: 8_000 }); + await this.queryByTitle(title); + await this.expectMockEmpty(); + } + + async publishAndCleanup(): Promise { + const title = uniqNoticeTitle(); + await this.openAddDialog(); + await this.fill(this.locators.modalTitleInput, title, '填写新增标题'); + await this.shared.dialog.getByText('中心库', { exact: true }).click(); + await this.locators.richTextEditor.fill('自动化发布用例正文'); + await this.click(this.shared.dialog.getByRole('button', { name: /发\s*布/ }), '发 布'); + await expect(this.shared.dialog).toBeHidden({ timeout: 15_000 }); + await this.queryByTitle(title); + const row = await this.firstRow(); + expect(await this.cellText(row, '公告标题')).toContain(title); + expect(await this.cellText(row, '发布范围')).toContain('中心库'); + await row.getByRole('button', { name: /删\s*除/ }).click(); + const confirm = this.page.getByRole('button', { name: /确\s*定/ }).last(); + if (await confirm.isVisible().catch(() => false)) { + await this.click(confirm, '确定删除测试公告'); + } + await this.waitForTableReady(); + } + + async publishWithEmptyTitle(): Promise { + await this.openAddDialog(); + await this.locators.richTextEditor.fill(basicHospitalData.mockNoticeBody); + await this.click(this.shared.dialog.getByRole('button', { name: /发\s*布/ }), '发 布'); + await this.expectFormError(); + await expect(this.shared.dialog).toBeVisible(); + await this.closeDialogIfOpen(); + } + + async publishWithEmptyContent(): Promise { + await this.openAddDialog(); + await this.fill(this.locators.modalTitleInput, basicHospitalData.mockEmptyContentTitle, '填写 mock 标题'); + await this.locators.richTextEditor.fill(''); + await this.click(this.shared.dialog.getByRole('button', { name: /发\s*布/ }), '发 布'); + await this.expectFormError(); + await expect(this.shared.dialog).toBeVisible(); + await this.closeDialogIfOpen(); + } + + async openEditAndExpectBackfill(): Promise { + const sample = await this.extractSample(); + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + await expect(this.shared.dialog).toContainText('编辑公告'); + const value = await this.locators.modalTitleInput.inputValue(); + expect(value.replace(/\s/g, '')).toBe(sample.sampleNoticeTitle.replace(/\s/g, '')); + await this.closeDialogIfOpen(); + } + + async editAndCancel(): Promise { + const sample = await this.extractSample(); + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + await expect(this.shared.dialog).toBeVisible(); + const uniq = uniqNoticeTitle(); + await this.fill(this.locators.modalTitleInput, uniq, '改标题'); + await this.click(this.shared.dialog.getByRole('button', { name: /取\s*消/ }), '取 消'); + await (await this.firstRow()).getByRole('button', { name: /编\s*辑/ }).click(); + expect(await this.locators.modalTitleInput.inputValue()).toBe(sample.sampleNoticeTitle); + await this.closeDialogIfOpen(); + } + + async deleteConfirmAndCancel(): Promise { + const sample = await this.extractSample(); + await (await this.firstRow()).getByRole('button', { name: /删\s*除/ }).click(); + const box = this.page.locator('.ant-modal, .ant-popconfirm, .ant-modal-confirm').filter({ hasText: /提示/ }).last(); + await expect(box).toBeVisible(); + await expect(box.getByRole('button', { name: /取\s*消/ })).toBeVisible(); + await expect(box.getByRole('button', { name: /确\s*定/ })).toBeVisible(); + await this.click(box.getByRole('button', { name: /取\s*消/ }), '取消删除'); + await this.queryByTitle(sample.sampleNoticeTitle); + expect(await this.getDataRowCount()).toBeGreaterThan(0); + } +} + +export const BASIC_NOTICE_PATH = basicHospitalData.noticePath; diff --git a/pages/spd/basic-role.page.ts b/pages/spd/basic-role.page.ts new file mode 100644 index 0000000..1249a52 --- /dev/null +++ b/pages/spd/basic-role.page.ts @@ -0,0 +1,217 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { userData } from '../../data/user.data'; +import { BasicRoleLocators } from '../../locators/spd/basic-role.locator'; +import { BasicUserSharedPage } from './basic-user-shared.page'; + +/** 角色管理:左右分栏 + 权限树;断言在 tests/spd/basic-role.spec.ts。 */ +export class BasicRolePage extends BasicUserSharedPage { + override readonly locators: BasicRoleLocators; + + constructor(page: Page) { + super(page, { + path: userData.rolePath, + pathPart: '/basic/user/platform/role', + menu: userData.menuRole, + forbidden: ['personCom', '/platform/person'], + }); + this.locators = new BasicRoleLocators(page); + } + + override async waitContentReady(): Promise { + await this.locators.saveBtn.or(this.locators.roleTree).first().waitFor({ state: 'visible', timeout: 20_000 }).catch(() => undefined); + await this.waitSpinGone(); + } + + leftRoleItems(): Locator { + return this.page.locator( + '.ant-layout-sider .ant-table-tbody tr.ant-table-row, .ant-layout-sider .ant-list-item, .ant-layout-sider .ant-menu-item, [class*="role"] .ant-table-row', + ); + } + + async leftRoleNames(): Promise { + const items = this.leftRoleItems(); + const count = await items.count(); + if (count > 0) { + const names: string[] = []; + for (let i = 0; i < Math.min(count, 30); i += 1) { + const text = ((await items.nth(i).innerText()) ?? '').split('\n')[0].trim(); + if (text) names.push(text); + } + if (names.length) return names; + } + const fallback = this.page.locator('.ant-table-tbody tr.ant-table-row td').first(); + if (await fallback.isVisible().catch(() => false)) { + return (await this.page.locator('.ant-table-tbody tr.ant-table-row td:first-child').allTextContents()) + .map((t) => t.trim()) + .filter(Boolean); + } + return []; + } + + async sampleRoleName(): Promise { + const names = await this.leftRoleNames(); + const found = names.find((n) => n && n !== '角色'); + if (!found) { + await this.skipWhenDataInsufficient('左栏无角色'); + return ''; + } + return found; + } + + async selectRole(name: string): Promise { + const item = this.leftRoleItems().filter({ hasText: name }).first(); + if (await item.isVisible().catch(() => false)) { + await this.click(item, `选中角色 ${name}`); + } else { + await this.click(this.page.getByText(name, { exact: true }).first(), `选中角色 ${name}`); + } + await this.waitSpinGone(); + await this.waitSettle(400); + } + + async searchRole(keyword: string): Promise { + const input = this.locators.roleSearch; + if (!(await input.isVisible().catch(() => false))) { + const fallback = this.page.locator('input').first(); + await this.fill(fallback, keyword, '角色名称搜索'); + } else { + await this.fill(input, keyword, '角色名称搜索'); + } + await this.page.keyboard.press('Enter').catch(() => undefined); + await this.waitSettle(400); + } + + async clearRoleSearch(): Promise { + const input = this.locators.roleSearch.or(this.page.locator('input').first()); + await input.fill(''); + await this.page.keyboard.press('Enter').catch(() => undefined); + await this.waitSettle(400); + } + + async isRoleSelected(name: string): Promise { + const item = this.leftRoleItems().filter({ hasText: name }).first(); + const cls = ((await item.getAttribute('class').catch(() => '')) ?? '').toLowerCase(); + const aria = (await item.getAttribute('aria-selected').catch(() => '')) ?? ''; + if (/selected|active/.test(cls) || aria === 'true') return true; + const selected = this.page.locator('.ant-table-row-selected, .ant-menu-item-selected, [aria-selected="true"]'); + return (await selected.filter({ hasText: name }).count()) > 0; + } + + async checkedSignature(): Promise { + const nodes = this.locators.roleTree.locator('.ant-tree-treenode'); + const limit = Math.min(await nodes.count(), 40); + const parts: string[] = []; + for (let i = 0; i < limit; i += 1) { + const title = ((await nodes.nth(i).locator('.ant-tree-title').innerText().catch(() => '')) ?? '').trim(); + if (!title) continue; + const checked = (await nodes.nth(i).locator('.ant-tree-checkbox-checked').count()) > 0; + parts.push(`${title}:${checked ? 1 : 0}`); + } + return parts.join('|'); + } + + async expandTreeNode(title: string): Promise { + const node = this.locators.roleTree + .locator('.ant-tree-treenode') + .filter({ has: this.page.locator('.ant-tree-title').filter({ hasText: new RegExp(`^\\s*${title}\\s*$`) }) }) + .first(); + if (!(await node.isVisible().catch(() => false))) return; + const switcher = node.locator('.ant-tree-switcher').first(); + const closed = await switcher + .evaluate((el) => el.classList.contains('ant-tree-switcher_close')) + .catch(() => false); + if (closed) await this.click(switcher, `展开 ${title}`); + await this.waitSettle(200); + } + + async isTreeChecked(title: string): Promise { + const box = this.locators.treeCheckbox(title); + const cls = (await box.getAttribute('class').catch(() => '')) ?? ''; + return cls.includes('ant-tree-checkbox-checked'); + } + + async isTreeDisabled(title: string): Promise { + const box = this.locators.treeCheckbox(title); + const cls = (await box.getAttribute('class').catch(() => '')) ?? ''; + return cls.includes('ant-tree-checkbox-disabled'); + } + + async checkModule(moduleName = '厂商管理'): Promise { + const box = this.locators.treeCheckbox(moduleName); + if (!(await box.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient(`权限树无「${moduleName}」节点`); + return; + } + if (await this.isTreeDisabled(moduleName)) { + await this.skipWhenDataInsufficient(`「${moduleName}」勾选被锁定`); + return; + } + await this.click(box, `勾选 ${moduleName}`); + await this.waitSettle(300); + } + + async refreshTree(): Promise { + await this.click(this.locators.refreshBtn, '刷 新'); + await this.waitSpinGone(); + await this.waitSettle(300); + } + + async deleteEntry(roleName: string): Promise { + const row = this.leftRoleItems().filter({ hasText: roleName }).first(); + return row + .getByRole('button', { name: /删/ }) + .or(row.locator('.anticon-delete, [aria-label*="delete"], [aria-label*="删"]')) + .first(); + } + + async openDeleteConfirm(): Promise<{ roleName: string; confirm: Locator } | null> { + const names = await this.leftRoleNames(); + const target = names.find((n) => n && !/管理员/.test(n)) ?? names[0]; + if (!target) { + await this.skipWhenDataInsufficient('无可删除角色'); + return null; + } + await this.selectRole(target); + const del = await this.deleteEntry(target); + if (!(await del.isVisible().catch(() => false))) { + const globalDel = this.page.locator('.anticon-delete, [aria-label*="delete"]').first(); + if (!(await globalDel.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('无删除入口'); + return null; + } + await this.click(globalDel, '删除'); + } else { + await this.click(del, '删除'); + } + const confirm = this.page + .locator('.ant-modal-confirm, .ant-popconfirm, .ant-modal') + .filter({ hasText: /提示|删除|确认/ }) + .last(); + await expect(confirm, '删除确认框').toBeVisible({ timeout: 8_000 }); + return { roleName: target, confirm }; + } + + async openAddRoleForm(): Promise<{ before: number; opened: boolean }> { + const add = this.locators.addRoleBtn.or(this.button('新增').first()); + if (!(await add.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('新增角色入口不可见'); + return { before: 0, opened: false }; + } + const before = (await this.leftRoleNames()).length; + await this.click(add, '新增角色'); + await this.waitSettle(400); + const layer = this.dialog().or(this.page.locator('.ant-modal:visible, .ant-drawer-open')); + const opened = await layer.first().isVisible().catch(() => false); + return { before, opened }; + } + + async cancelAddRole(): Promise { + const layer = this.dialog().or(this.page.locator('.ant-modal:visible')); + if (await layer.first().isVisible().catch(() => false)) { + await this.closeDialog(); + return; + } + await this.page.keyboard.press('Escape'); + } +} diff --git a/pages/spd/basic-shared.page.ts b/pages/spd/basic-shared.page.ts new file mode 100644 index 0000000..bee38cb --- /dev/null +++ b/pages/spd/basic-shared.page.ts @@ -0,0 +1,173 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { env } from '../../config/env.config'; +import { BasePage } from '../../core/base.page'; +import { basicHospitalData } from '../../data/basic-hospital.data'; +import { BasicSharedLocators } from '../../locators/spd/basic-shared.locator'; + +/** 医院资料页共用:无 iframe、菜单进入、表格/分页/弹窗。 */ +export class BasicHospitalSharedPage extends BasePage { + protected readonly shared: BasicSharedLocators; + + constructor(page: Page) { + super(page); + this.shared = new BasicSharedLocators(page); + } + + async openPath(path: string): Promise { + await this.goto(path); + await this.waitSpinGone(); + await this.waitSettle(400); + await this.assertPageHealthy(`打开 ${path}`); + expect(this.page.url(), 'storageState 失效回到登录页').not.toMatch(/\/login/); + } + + async openViaMenu(menuName: string, pathPart: string): Promise { + await this.goto(env.homePath); + await this.waitSpinGone(); + await this.click(this.shared.menuBasic, '展开基础设置'); + const hospital = this.shared.menuHospital; + if (await hospital.isVisible().catch(() => false)) { + await this.click(hospital, '展开医院资料'); + } + await this.click(this.shared.menuItem(menuName), `进入${menuName}`); + await this.page.waitForURL((url) => url.pathname.includes(pathPart), { timeout: 20_000 }); + await this.waitSpinGone(); + await this.waitSettle(400); + } + + async expectUrlContains(part: string): Promise { + expect(this.page.url(), `URL 应含 ${part}`).toContain(part); + } + + async expectNotOnSiblingPages(forbidden: string[]): Promise { + const url = this.page.url(); + for (const part of forbidden) { + expect(url, `不应进入 ${part}`).not.toContain(part); + } + } + + dataRows(): Locator { + return this.shared.dataRows; + } + + async getDataRowCount(): Promise { + return this.dataRows().count(); + } + + async getTotalCount(): Promise { + const text = (await this.shared.paginationTotal.textContent().catch(() => '')) ?? ''; + const match = text.match(/共\s*(\d+)\s*条/); + return match ? Number(match[1]) : 0; + } + + async waitForTableReady(): Promise { + await this.waitSpinGone(); + await this.waitSettle(200); + await this.assertPageHealthy('列表加载'); + } + + async expectListOrEmpty(): Promise { + await this.waitForTableReady(); + const rows = await this.getDataRowCount(); + if (rows > 0) { + expect(await this.getTotalCount(), '有数据时分页总数应 ≥ 1').toBeGreaterThanOrEqual(1); + return; + } + const emptyVisible = await this.shared.empty.isVisible().catch(() => false); + const emptyText = await this.page.getByText('暂无数据').first().isVisible().catch(() => false); + expect(emptyVisible || emptyText || (await this.getTotalCount()) === 0, '无数据时空态应明确').toBeTruthy(); + } + + async expectHeaders(names: string[]): Promise { + const text = ((await this.shared.tableHeader.allTextContents()) ?? []).join(' '); + for (const name of names) { + expect(text, `表头应含「${name}」`).toContain(name); + } + } + + async headerIndex(name: string): Promise { + const headers = await this.shared.tableHeader.allTextContents(); + const index = headers.findIndex((item) => item.replace(/\s/g, '').includes(name.replace(/\s/g, ''))); + expect(index, `找不到列「${name}」`).toBeGreaterThanOrEqual(0); + return index; + } + + async cellText(row: Locator, header: string): Promise { + const index = await this.headerIndex(header); + return ((await row.locator('td').nth(index).innerText()) ?? '').trim(); + } + + async firstRow(): Promise { + const row = this.dataRows().first(); + await expect(row, '列表至少应有 1 条数据').toBeVisible(); + return row; + } + + async expectFormError(): Promise { + const error = this.shared.formError.first(); + const toast = this.page.locator('.ant-message-error, .ant-notification-notice').first(); + await expect(error.or(toast), '应出现必填或非法提示').toBeVisible({ timeout: 8_000 }); + } + + async closeDialogIfOpen(): Promise { + const dialog = this.shared.dialog; + if (!(await dialog.isVisible().catch(() => false))) return; + const cancel = dialog.getByRole('button', { name: /取\s*消/ }).first(); + if (await cancel.isVisible().catch(() => false)) { + await this.click(cancel, '关闭弹窗'); + } else { + await this.page.keyboard.press('Escape'); + } + await expect(dialog).toBeHidden({ timeout: 8_000 }).catch(() => undefined); + } + + async selectDropdown(select: Locator, optionText: string): Promise { + await this.click(select, '打开下拉'); + const option = this.page.locator('.ant-select-dropdown:visible .ant-select-item-option').filter({ hasText: optionText }).first(); + await this.click(option, `选择 ${optionText}`); + await this.waitSettle(200); + } + + async clickPaginationPage(pageNum: number): Promise { + const item = this.page.locator(`.ant-pagination-item-${pageNum}`).first(); + if (await item.isVisible().catch(() => false)) { + await this.click(item, `翻到第 ${pageNum} 页`); + return; + } + await this.click(this.shared.nextPage, '下一页'); + } + + async hasPaginationItem(pageNum: number): Promise { + return this.page.locator(`.ant-pagination-item-${pageNum}`).first().isVisible().catch(() => false); + } + + formItem(label: string): Locator { + return this.page.locator('.ant-form-item').filter({ hasText: label }).first(); + } + + dialogFormItem(label: string): Locator { + return this.shared.dialog.locator('.ant-form-item').filter({ hasText: label }).first(); + } + + async pickCascaderFirstPath(): Promise { + const selector = this.shared.dialog.locator('.ant-cascader, .ant-select').filter({ has: this.page.getByText('请选择') }).first(); + const fallback = this.dialogFormItem('所在地区').locator('.ant-select, .ant-cascader').first(); + const target = (await selector.count()) ? selector : fallback; + await this.click(target, '打开所在地区'); + for (let i = 0; i < 3; i += 1) { + const item = this.page.locator('.ant-cascader-menu:visible .ant-cascader-menu-item:not(.ant-cascader-menu-item-disabled)').first(); + if (!(await item.isVisible().catch(() => false))) break; + await this.click(item, `选择地区第 ${i + 1} 级`); + await this.waitSettle(150); + } + } + + uniqStamp(prefix: string): string { + return `${prefix}-${Date.now()}`; + } + + get hospitalData() { + return basicHospitalData; + } +} diff --git a/pages/spd/basic-user-shared.page.ts b/pages/spd/basic-user-shared.page.ts new file mode 100644 index 0000000..cc636e2 --- /dev/null +++ b/pages/spd/basic-user-shared.page.ts @@ -0,0 +1,297 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { env } from '../../config/env.config'; +import { BasePage } from '../../core/base.page'; +import { BasicUserSharedLocators } from '../../locators/spd/basic-user-shared.locator'; +import { skipIfInsufficientForPage2 } from './helpers/pagination-page2.helper'; + +export type UserMeta = { + path: string; + pathPart: string; + menu: string; + forbidden?: string[]; +}; + +export type UserListSnapshot = { + rows: number; + total: number; + emptyVisible: boolean; +}; + +/** 用户管理三页共用:无 iframe、侧栏叶子菜单、表格/弹窗。 */ +export class BasicUserSharedPage extends BasePage { + readonly locators: BasicUserSharedLocators; + + constructor( + page: Page, + protected readonly meta: UserMeta, + ) { + super(page); + this.locators = new BasicUserSharedLocators(page); + } + + get pagePath(): string { + return this.meta.path; + } + + async open(): Promise { + await this.goto(this.meta.path); + await this.waitSpinGone(); + await this.waitSettle(400); + await this.waitContentReady(); + await this.assertPageHealthy(`打开 ${this.meta.path}`); + expect(this.page.url(), 'storageState 失效回到登录页').not.toMatch(/\/login/); + } + + async openFromMenu(): Promise { + await this.goto(env.homePath); + await this.waitSpinGone(); + await this.click(this.locators.menuBasic, '展开基础设置'); + const title = this.locators.userSubmenuTitle.first(); + const submenu = this.page.locator('.ant-menu-submenu').filter({ has: title }).first(); + const opened = await submenu + .evaluate((el) => el.classList.contains('ant-menu-submenu-open')) + .catch(() => false); + if (!opened) await this.click(title, '展开用户管理'); + await this.click(this.locators.menuLeaf(this.meta.menu), `进入${this.meta.menu}`); + await this.page.waitForURL((url) => { + const pathname = url.pathname; + if (!pathname.includes(this.meta.pathPart)) return false; + return !(this.meta.forbidden ?? []).some((token) => pathname.includes(token)); + }, { timeout: 20_000 }); + await this.waitSpinGone(); + await this.waitSettle(600); + await this.waitContentReady(); + await this.assertPageHealthy(`菜单进入${this.meta.menu}`); + } + + async waitContentReady(): Promise { + const markers = this.page + .locator('.ant-table-thead th, .ant-empty, .menu-role-tree, .ant-tree, [placeholder="姓名、登录账号"]') + .first(); + await markers.waitFor({ state: 'visible', timeout: 20_000 }).catch(() => undefined); + await this.waitForTableReady(); + } + + async waitForTableReady(): Promise { + await this.waitSpinGone(); + await this.waitSettle(200); + await this.assertPageHealthy('列表加载'); + } + + button(name: string, root?: Locator): Locator { + return this.locators.button(name, root ?? this.page); + } + + async clickButton(name: string, root?: Locator): Promise { + await this.click(this.button(name, root).first(), name); + } + + async query(): Promise { + await this.clickButton('查询'); + await this.waitForTableReady(); + } + + async reset(): Promise { + await this.clickButton('重置'); + await this.waitForTableReady(); + } + + dialog(): Locator { + return this.locators.dialog; + } + + async headerCells(): Promise { + await this.page + .locator('.ant-table-thead th') + .first() + .waitFor({ state: 'attached', timeout: 12_000 }) + .catch(() => undefined); + return this.page.locator('.ant-table-thead').first().locator('th'); + } + + async headerTexts(): Promise { + return (await (await this.headerCells()).allTextContents()).map((item) => item.replace(/\s/g, '')); + } + + async headerIndex(name: string): Promise { + const texts = await this.headerTexts(); + const needle = name.replace(/\s/g, ''); + const exact = texts.findIndex((item) => item === needle); + if (exact >= 0) return exact; + const index = texts.findIndex((item) => item.includes(needle)); + expect(index, `找不到列「${name}」`).toBeGreaterThanOrEqual(0); + return index; + } + + async listRows(): Promise { + return this.page.locator('.ant-table-tbody > tr.ant-table-row:not(.ant-table-measure-row)'); + } + + async getDataRowCount(): Promise { + return (await this.listRows()).count(); + } + + async getTotalCount(): Promise { + const text = (await this.locators.paginationTotal.textContent().catch(() => '')) ?? ''; + const match = text.match(/共\s*(\d+)\s*条/); + return match ? Number(match[1]) : 0; + } + + async cellText(row: Locator, header: string): Promise { + const index = await this.headerIndex(header); + return ((await row.locator('td').nth(index).innerText()) ?? '').trim(); + } + + async tryCell(row: Locator, header: string): Promise { + try { + return (await this.cellText(row, header)).trim(); + } catch { + return ''; + } + } + + async firstRow(): Promise { + const row = (await this.listRows()).first(); + await expect(row, '列表至少应有 1 条数据').toBeVisible(); + return row; + } + + async snapshotList(): Promise { + await this.waitForTableReady(); + const rows = await this.getDataRowCount(); + const total = await this.getTotalCount(); + const emptyVisible = + (await this.locators.empty.isVisible().catch(() => false)) || + (await this.page.getByText('暂无数据').first().isVisible().catch(() => false)); + return { rows, total, emptyVisible }; + } + + async sampleCell(header: string): Promise { + const rows = await this.listRows(); + const count = Math.min(await rows.count(), 12); + for (let i = 0; i < count; i += 1) { + const text = (await this.tryCell(rows.nth(i), header)).replace(/\s/g, ''); + if (text && text !== '--' && text !== '-') return text; + } + await this.skipWhenDataInsufficient(`首屏「${header}」为空`); + return ''; + } + + async rowsMatchKeyword(header: string, keyword: string, sample = 5): Promise { + const needle = keyword.replace(/\s/g, '').slice(0, 12); + const rows = await this.listRows(); + const count = await rows.count(); + if (count === 0) return false; + const limit = Math.min(count, sample); + for (let i = 0; i < limit; i += 1) { + const text = (await this.cellText(rows.nth(i), header)).replace(/\s/g, ''); + if (!text.includes(needle)) return false; + } + return true; + } + + async fillName(keyword: string): Promise { + await expect(this.locators.nameInput, '姓名/登录账号').toBeVisible(); + await this.fill(this.locators.nameInput, keyword, '填写姓名或登录账号'); + } + + async selectFormOption(label: string, optionText: string): Promise { + const select = this.locators.formSelect(label); + await expect(select, `筛选「${label}」`).toBeVisible(); + await this.click(select, `打开${label}`); + const compact = optionText.replace(/\s/g, '').slice(0, 12); + const escaped = compact.slice(0, 8).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const option = this.page + .locator('.ant-select-dropdown:visible .ant-select-item-option') + .filter({ hasText: new RegExp(escaped) }) + .first(); + await this.click(option, `选择 ${optionText}`); + await this.waitSettle(200); + } + + async expandAdvanced(): Promise { + await this.click(this.locators.advancedFilter, '高级筛选'); + await this.waitSettle(400); + } + + rowAction(row: Locator, name: string): Locator { + const compact = name.replace(/\s/g, ''); + const pattern = new RegExp(compact.split('').join('\\s*')); + return row.getByRole('button', { name: pattern }).or(row.getByText(pattern)).first(); + } + + async goToPage2(): Promise<{ firstLogin: string; secondLogin: string; active: string }> { + await skipIfInsufficientForPage2({ + ensureHealthyBeforeSkip: (reason) => this.ensureHealthyBeforeSkip(reason), + waitForTableReady: () => this.waitForTableReady(), + getDataRowCount: () => this.getDataRowCount(), + hasPaginationItem: async (pageNum) => + this.page.locator(`.ant-pagination-item-${pageNum}`).first().isVisible().catch(() => false), + }); + const firstLogin = await this.tryCell(await this.firstRow(), '登录账号'); + const item = this.page.locator('.ant-pagination-item-2').first(); + if (await item.isVisible().catch(() => false)) { + await this.click(item, '翻到第 2 页'); + } else { + await this.click(this.locators.nextPage, '下一页'); + } + await this.waitForTableReady(); + const active = ((await this.page.locator('.ant-pagination-item-active').innerText()) ?? '').trim(); + const secondLogin = await this.tryCell(await this.firstRow(), '登录账号'); + return { firstLogin, secondLogin, active }; + } + + async openAddDialog(): Promise { + await this.clickButton('新增'); + const dialog = this.dialog(); + await expect(dialog, '新增弹窗').toBeVisible({ timeout: 12_000 }); + return dialog; + } + + async openRowAction(name: string): Promise { + const row = await this.firstRow(); + await this.click(this.rowAction(row, name), name); + await this.waitSettle(400); + return row; + } + + async closeDialog(): Promise { + const dialog = this.dialog(); + if (!(await dialog.isVisible().catch(() => false))) return; + const cancel = dialog.getByRole('button', { name: /取\s*消|关\s*闭/ }).first(); + if (await cancel.isVisible().catch(() => false)) { + await this.click(cancel, '关闭弹窗'); + } else { + await this.page.keyboard.press('Escape'); + } + await expect(dialog).toBeHidden({ timeout: 8_000 }).catch(() => undefined); + } + + async expectBlockedAndStay(): Promise { + const hint = this.page.getByText(/必填|请输入|请选择|不能为空|不可为空|已存在|重复|占用/).first(); + await expect(this.locators.formError.first().or(hint), '应出现拦截提示').toBeVisible({ timeout: 8_000 }); + const stayed = + (await this.page.locator('.ant-modal:visible').count()) > 0 || + (await this.page.locator('.ant-drawer-open').count()) > 0; + expect(stayed, '拦截后弹窗不应关闭').toBeTruthy(); + } + + async probeUnauthAccess(): Promise<{ onLogin: boolean; hasBusinessRow: boolean }> { + await this.page.goto(this.meta.path, { waitUntil: 'domcontentloaded' }); + await this.page.waitForLoadState('networkidle', { timeout: 8_000 }).catch(() => undefined); + const onLogin = /\/login/i.test(this.page.url()); + const hasBusinessRow = await this.page + .locator('.ant-table-tbody tr.ant-table-row, .menu-role-tree') + .first() + .isVisible() + .catch(() => false); + return { onLogin, hasBusinessRow }; + } + + async fillDialogField(locator: Locator, value: string, label: string): Promise { + if (!(await locator.isVisible().catch(() => false))) return; + await this.fill(locator, value, label); + } + +} diff --git a/pages/spd/basic-user-union.page.ts b/pages/spd/basic-user-union.page.ts new file mode 100644 index 0000000..bc74942 --- /dev/null +++ b/pages/spd/basic-user-union.page.ts @@ -0,0 +1,92 @@ +import { type Page } from '@playwright/test'; + +import { userData, uniqLogin } from '../../data/user.data'; +import { BasicUserUnionLocators } from '../../locators/spd/basic-user-union.locator'; +import { BasicUserPage, type UserSample } from './basic-user.page'; + +export type UnionSample = UserSample & { + sampleHospital: string; + samplePhone: string; +}; + +/** 医共体用户管理:医院维度差异;断言在 tests/spd/basic-user-union.spec.ts。 */ +export class BasicUserUnionPage extends BasicUserPage { + override readonly locators: BasicUserUnionLocators; + + constructor(page: Page) { + super(page, { + path: userData.unionPath, + pathPart: '/basic/user/platform/personCom', + menu: userData.menuUnion, + forbidden: ['/platform/role'], + }); + this.locators = new BasicUserUnionLocators(page); + } + + async extractUnionSample(): Promise { + await this.query(); + const row = await this.firstRow(); + const sampleUserName = await this.cellText(row, '姓名'); + const sampleLoginName = await this.cellText(row, '登录账号'); + const sampleHospital = await this.cellText(row, '所属医院'); + const samplePhone = await this.tryCell(row, '联系方式'); + const sampleDept = await this.tryCell(row, '所属科室'); + const rawRole = await this.tryCell(row, '角色'); + const sampleRole = rawRole.replace(/[((].*$/, '').replace(/…|\.{3}.*/, '').trim(); + return { sampleUserName, sampleLoginName, sampleDept, sampleRole, sampleHospital, samplePhone }; + } + + async currentHospitalLabel(): Promise { + const select = this.locators.formSelect('所属医院'); + return ((await select.innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + } + + async switchHospitalIfPossible(): Promise<{ from: string; to: string } | null> { + const from = await this.currentHospitalLabel(); + await this.click(this.locators.formSelect('所属医院'), '打开所属医院'); + const options = this.page.locator('.ant-select-dropdown:visible .ant-select-item-option'); + const count = await options.count(); + if (count < 2) { + await this.page.keyboard.press('Escape').catch(() => undefined); + await this.skipWhenDataInsufficient('所属医院仅 1 家可选'); + return null; + } + let to = from; + for (let i = 0; i < count; i += 1) { + const text = ((await options.nth(i).innerText()) ?? '').replace(/\s/g, ''); + if (text && text !== from) { + to = text; + await this.click(options.nth(i), `选择医院 ${text}`); + break; + } + } + if (to === from) { + await this.page.keyboard.press('Escape').catch(() => undefined); + await this.skipWhenDataInsufficient('所属医院无可切换选项'); + return null; + } + await this.query(); + return { from, to }; + } + + async queryComboUnion(sample: UnionSample): Promise { + await this.fillName(sample.sampleUserName); + if (sample.sampleHospital) { + await this.selectFormOption('所属医院', sample.sampleHospital).catch(() => undefined); + } + const phoneInput = this.page.getByPlaceholder(/联系方式/).or(this.locators.formItem('联系方式').locator('input')); + if (sample.samplePhone && sample.samplePhone.length >= 4 && (await phoneInput.first().isVisible().catch(() => false))) { + await this.fill(phoneInput.first(), sample.samplePhone, '联系方式'); + } + await this.query(); + } + + async addAndCancelOnHospital(): Promise { + const login = uniqLogin(); + await this.openAddDialog(); + await this.fillAddRequired(login, '医共体取消'); + await this.closeDialog(); + await this.queryByName(login); + return login; + } +} diff --git a/pages/spd/basic-user.page.ts b/pages/spd/basic-user.page.ts new file mode 100644 index 0000000..99bdce9 --- /dev/null +++ b/pages/spd/basic-user.page.ts @@ -0,0 +1,142 @@ +import { expect, type Page } from '@playwright/test'; + +import { userData, uniqLogin } from '../../data/user.data'; +import { BasicUserLocators } from '../../locators/spd/basic-user.locator'; +import { BasicUserSharedPage, type UserMeta } from './basic-user-shared.page'; + +export type UserSample = { + sampleUserName: string; + sampleLoginName: string; + sampleDept: string; + sampleRole: string; +}; + +/** 用户管理:动作与取数;断言在 tests/spd/basic-user.spec.ts。 */ +export class BasicUserPage extends BasicUserSharedPage { + override readonly locators: BasicUserLocators; + + constructor(page: Page, meta?: UserMeta) { + super( + page, + meta ?? { + path: userData.userPath, + pathPart: '/basic/user/platform/person', + menu: userData.menuUser, + forbidden: ['personCom', '/platform/role'], + }, + ); + this.locators = new BasicUserLocators(page); + } + + async extractSample(): Promise { + await this.query(); + const row = await this.firstRow(); + const sampleUserName = await this.cellText(row, '姓名'); + const sampleLoginName = await this.cellText(row, '登录账号'); + const sampleDept = await this.sampleCell('所属科室'); + const rawRole = await this.tryCell(row, '角色'); + const sampleRole = rawRole.replace(/[((].*$/, '').replace(/…|\.{3}.*/, '').trim(); + return { sampleUserName, sampleLoginName, sampleDept, sampleRole }; + } + + async queryByName(keyword: string): Promise { + await this.fillName(keyword); + await this.query(); + } + + async queryByDept(dept: string): Promise { + await this.selectFormOption('科室', dept); + await this.query(); + } + + async queryCombo(sample: UserSample): Promise { + await this.fillName(sample.sampleUserName); + if (sample.sampleDept && sample.sampleDept !== '--') { + await this.selectFormOption('科室', sample.sampleDept); + } + if (sample.sampleRole) { + await this.selectFormOption('角色', sample.sampleRole).catch(() => undefined); + } + await this.query(); + } + + async queryMockEmpty(): Promise { + await this.queryByName(userData.mockUser); + } + + async filterByStatus(status: '启用' | '禁用'): Promise { + await this.expandAdvanced(); + const panel = this.page.locator('.ant-drawer-open, .ant-pro-table-search-query-filter, .advance-filter-panel').first(); + const root = (await panel.isVisible().catch(() => false)) ? panel : this.page; + await this.selectFormOption('状态', status); + const filterBtn = this.locators.button('筛选', root).first(); + if (await filterBtn.isVisible().catch(() => false)) { + await this.click(filterBtn, '筛 选'); + } else { + await this.query(); + } + await this.waitForTableReady(); + } + + async fillAddRequired(login: string, name: string): Promise { + const dialog = this.dialog(); + await this.fillDialogField(this.locators.dialogUserCode(), login, '账号'); + await this.fillDialogField(this.locators.dialogName(), name, '姓名'); + await expect(dialog.getByRole('button', { name: /确\s*定/ })).toBeVisible(); + } + + async addAndCancel(): Promise { + const login = uniqLogin(); + await this.openAddDialog(); + await this.fillAddRequired(login, '自动化取消'); + await this.closeDialog(); + await this.queryByName(login); + return login; + } + + async submitEmptyRequired(): Promise { + await this.openAddDialog(); + await this.click(this.dialog().getByRole('button', { name: /确\s*定/ }), '确 定'); + } + + async submitDuplicateLogin(login: string): Promise { + await this.openAddDialog(); + await this.fillAddRequired(login, '重复账号'); + await this.click(this.dialog().getByRole('button', { name: /确\s*定/ }), '确 定'); + } + + async openEdit(): Promise<{ login: string; name: string }> { + const row = await this.firstRow(); + const login = await this.cellText(row, '登录账号'); + const name = await this.cellText(row, '姓名'); + await this.click(this.rowAction(row, '编辑'), '编辑'); + const dialog = this.dialog(); + await expect(dialog, '编辑用户弹窗').toBeVisible({ timeout: 12_000 }); + return { login, name }; + } + + async editAndCancel(): Promise { + const { name } = await this.openEdit(); + await this.fillDialogField(this.locators.dialogName(), `${name}-tmp`, '临时改姓名'); + await this.closeDialog(); + return name; + } + + async collectRoleFilterOptions(): Promise { + const select = this.locators.formSelect('角色'); + if (!(await select.isVisible().catch(() => false))) return []; + await this.click(select, '打开角色下拉'); + const options = this.page.locator('.ant-select-dropdown:visible .ant-select-item-option'); + const texts = (await options.allTextContents()) + .map((item) => item.replace(/\s/g, '').replace(/[((].*$/, '')) + .filter(Boolean); + await this.page.keyboard.press('Escape').catch(() => undefined); + return texts; + } + + async authCheckedCount(): Promise { + const tree = this.page.locator('.ant-tree, .menu-role-tree, .ant-drawer-open .ant-tree').first(); + if (!(await tree.isVisible().catch(() => false))) return 0; + return tree.locator('.ant-tree-checkbox-checked').count(); + } +} diff --git a/pages/spd/center-report-shared.page.ts b/pages/spd/center-report-shared.page.ts index fcb9f30..8b525a4 100644 --- a/pages/spd/center-report-shared.page.ts +++ b/pages/spd/center-report-shared.page.ts @@ -1603,7 +1603,8 @@ export class CenterReportSharedPage extends BasePage { await executeExportReport({ page: this.page, prepareRowSelection: options?.prepareRowSelection, - clickExport: () => this.click(exportBtn, `点击${tableLabel}导出`), + clickExport: () => + this.click(exportBtn, `点击${tableLabel}导出`, { operation: 'write' }), hasVisibleErrorNotice: () => this.hasVisibleErrorNotice(), assertHealthy: () => this.assertPageHealthy('导出后'), }); @@ -1664,7 +1665,7 @@ export class CenterReportSharedPage extends BasePage { async triggerBatchPrint(): Promise { const dialogPromise = this.page.waitForEvent('dialog', { timeout: 10_000 }).catch(() => null); - await this.click(this.locators.batchPrintBtn, '点击批量打印'); + await this.click(this.locators.batchPrintBtn, '点击批量打印', { operation: 'write' }); const dialog = await dialogPromise; if (dialog) { await dialog.dismiss().catch(() => undefined); @@ -1734,7 +1735,7 @@ export class CenterReportSharedPage extends BasePage { } await expect(printBtn, '打印按钮应可点击').toBeEnabled({ timeout: 10_000 }); const dialogPromise = this.page.waitForEvent('dialog', { timeout: 10_000 }).catch(() => null); - await this.click(printBtn, '点击打 印'); + await this.click(printBtn, '点击打 印', { operation: 'write' }); const dialog = await dialogPromise; if (dialog) { await dialog.dismiss().catch(() => undefined); diff --git a/pages/spd/flow-def.page.ts b/pages/spd/flow-def.page.ts new file mode 100644 index 0000000..94c5b5f --- /dev/null +++ b/pages/spd/flow-def.page.ts @@ -0,0 +1,140 @@ +import { expect, type Page } from '@playwright/test'; + +import { flowData } from '../../data/flow.data'; +import { FlowDefLocators } from '../../locators/spd/flow-def.locator'; +import { WhFlowSharedPage } from './wh-flow-shared.page'; + +/** 审批流程设置:动作与取数;断言在 tests/spd/flow-def.spec.ts。 */ +export class FlowDefPage extends WhFlowSharedPage { + override readonly locators: FlowDefLocators; + + constructor(page: Page) { + super(page, { + path: flowData.defPath, + pathPart: 'workflow/flowSet', + group: flowData.menuGroup, + menu: flowData.menuDef, + forbidden: ['flowCase'], + }); + this.locators = new FlowDefLocators(page); + } + + override async waitContentReady(): Promise { + await this.locators.typeSearch + .or(this.locators.defaultFlow) + .or(this.locators.saveBtn) + .first() + .waitFor({ state: 'visible', timeout: 20_000 }) + .catch(() => undefined); + await this.waitSpinGone(); + } + + async typeNames(): Promise { + const items = this.locators.typeItems(); + const count = await items.count(); + const names: string[] = []; + if (count > 0) { + for (let i = 0; i < Math.min(count, 40); i += 1) { + const text = ((await items.nth(i).innerText()) ?? '').split('\n')[0].trim(); + if (text && !['流程名称', '审批流程设置'].includes(text)) names.push(text); + } + if (names.length) return names; + } + const fallback = this.page.locator('.ant-layout-sider, [class*="left"]').locator('span, div, li'); + const n = Math.min(await fallback.count(), 60); + for (let i = 0; i < n; i += 1) { + const text = ((await fallback.nth(i).innerText()) ?? '').trim(); + if (text && text.length <= 16 && /计划|单|申请|变更|调拨|准入/.test(text)) names.push(text); + } + return [...new Set(names)]; + } + + async selectedTypeName(): Promise { + const selected = this.locators.selectedType(); + if (await selected.isVisible().catch(() => false)) { + return ((await selected.innerText()) ?? '').split('\n')[0].trim(); + } + const names = await this.typeNames(); + return names[0] ?? ''; + } + + async filterTypes(keyword: string): Promise { + await this.fill(this.locators.typeSearch, keyword, '填写流程名称'); + await this.waitSettle(400); + } + + async clearTypeFilter(): Promise { + await this.fill(this.locators.typeSearch, '', '清空流程名称'); + await this.waitSettle(400); + } + + async clickType(name: string): Promise { + const item = this.locators.typeItems().filter({ hasText: name }).first() + .or(this.page.getByText(name, { exact: true }).first()); + await this.click(item, `选中类型 ${name}`); + await this.waitSpinGone(); + await this.waitSettle(400); + } + + async switchOtherType(): Promise { + const current = await this.selectedTypeName(); + const names = await this.typeNames(); + const other = names.find((name) => name && name !== current); + if (!other) { + await this.skipWhenDataInsufficient('业务类型仅 1 个'); + return null; + } + await this.clickType(other); + return other; + } + + async defaultFlowText(): Promise { + const region = this.page.locator('section, div').filter({ hasText: '默认审批流' }).first(); + return ((await region.innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + } + + async customRowCount(): Promise { + return this.getDataRowCount(); + } + + async addFlowRow(): Promise { + await this.click(this.locators.addFlowBtn, '新增流程'); + await this.waitSettle(300); + } + + async clickSave(): Promise { + await this.click(this.locators.saveBtn, '保 存', { operation: 'write' }); + await this.waitSettle(400); + } + + async clickReset(): Promise { + await this.click(this.locators.resetBtn, '重 置'); + await this.waitSpinGone(); + await this.waitSettle(400); + } + + async openFirstCondition(): Promise<{ before: string; opened: boolean }> { + const rows = await this.listRows(); + if ((await rows.count()) === 0) { + await this.skipWhenDataInsufficient('当前类型无自定义流'); + return { before: '', opened: false }; + } + const row = rows.first(); + const before = ((await row.innerText()) ?? '').replace(/\s/g, ''); + await this.click(this.rowAction(row, '编辑'), '条件编辑'); + const dialog = this.locators.dialog(); + const opened = await dialog.isVisible().catch(() => false); + await expect(dialog, '条件配置').toBeVisible({ timeout: 12_000 }); + return { before, opened }; + } + + async cancelCondition(): Promise { + await this.closeOverlay(); + } + + async firstCustomRowText(): Promise { + const rows = await this.listRows(); + if ((await rows.count()) === 0) return ''; + return ((await rows.first().innerText()) ?? '').replace(/\s/g, ''); + } +} diff --git a/pages/spd/flow-inst.page.ts b/pages/spd/flow-inst.page.ts new file mode 100644 index 0000000..611969f --- /dev/null +++ b/pages/spd/flow-inst.page.ts @@ -0,0 +1,127 @@ +import { expect, type Page } from '@playwright/test'; + +import { flowData } from '../../data/flow.data'; +import { FlowInstLocators } from '../../locators/spd/flow-inst.locator'; +import { WhFlowSharedPage } from './wh-flow-shared.page'; + +/** 审批流实例:动作与取数;断言在 tests/spd/flow-inst.spec.ts。 */ +export class FlowInstPage extends WhFlowSharedPage { + override readonly locators: FlowInstLocators; + + constructor(page: Page) { + super(page, { + path: flowData.instPath, + pathPart: 'workflow/flowCase', + group: flowData.menuGroup, + menu: flowData.menuInst, + forbidden: ['flowSet'], + }); + this.locators = new FlowInstLocators(page); + } + + override async waitContentReady(): Promise { + await this.locators.orderNo.or(this.locators.queryBtn).first().waitFor({ state: 'visible', timeout: 20_000 }).catch(() => undefined); + await this.waitSpinGone(); + } + + async fillOrderNo(value: string): Promise { + await this.fill(this.locators.orderNo, value, '填写单据编号'); + } + + async fillSummary(value: string): Promise { + await this.fill(this.locators.summary, value, '填写摘要'); + } + + async queryByOrderNo(orderNo: string): Promise { + await this.fillOrderNo(orderNo); + await this.query(); + } + + async queryByStatus(status: string): Promise { + await this.selectFormOption('审批状态', status); + await this.query(); + } + + async queryByBizType(type: string): Promise { + await this.selectFormOption('业务类型', type); + await this.query(); + } + + async queryCombo(status: string, type: string): Promise { + await this.selectFormOption('审批状态', status); + await this.selectFormOption('业务类型', type); + await this.query(); + } + + async searchMockEmpty(): Promise { + await this.queryByOrderNo(flowData.mockFlow); + } + + async dateRangeValues(): Promise<{ start: string; end: string }> { + return { + start: ((await this.locators.startDate.inputValue().catch(() => '')) ?? '').trim(), + end: ((await this.locators.endDate.inputValue().catch(() => '')) ?? '').trim(), + }; + } + + async sampleSummaryKeyword(): Promise<{ keyword: string; usedMock: boolean }> { + const rows = await this.listRows(); + const count = Math.min(await rows.count(), 10); + for (let i = 0; i < count; i += 1) { + const text = (await this.tryCell(rows.nth(i), '摘要')).replace(/\s/g, ''); + if (text && text !== '--' && text !== '-') { + return { keyword: text.slice(0, Math.min(text.length, 8)), usedMock: false }; + } + } + return { keyword: flowData.mockFlow, usedMock: true }; + } + + async prefixTypeSamples(): Promise> { + const rows = await this.listRows(); + const count = Math.min(await rows.count(), 20); + const found: Array<{ prefix: string; type: string; expected: string }> = []; + const seen = new Set(); + for (let i = 0; i < count; i += 1) { + const orderNo = await this.tryCell(rows.nth(i), '单据编号'); + const type = await this.tryCell(rows.nth(i), '业务类型'); + for (const [prefix, expected] of Object.entries(flowData.prefixType)) { + if (orderNo.startsWith(prefix) && !seen.has(prefix)) { + seen.add(prefix); + found.push({ prefix, type, expected }); + } + } + } + return found; + } + + async openFirstDetail(): Promise<{ orderNo: string; layerText: string }> { + const row = await this.firstRow(); + const orderNo = await this.tryCell(row, '单据编号'); + await this.click(this.rowAction(row, '详情'), '详情'); + const layer = this.overlay().or(this.page.locator('.ant-page-header, .ant-descriptions')); + await expect(layer.first(), '审批详情').toBeVisible({ timeout: 15_000 }); + await this.waitSpinGone(); + const layerText = ((await this.page.locator('body').innerText()) ?? '').replace(/\s/g, ''); + return { orderNo, layerText }; + } + + async closeDetail(): Promise { + const back = this.locators.backBtn.first().or(this.page.getByRole('button', { name: /关\s*闭|返\s*回/ }).first()); + if (await back.isVisible().catch(() => false)) { + await this.click(back, '返回'); + } else { + await this.closeOverlay(); + } + await this.waitForTableReady(); + } + + async hasSaveOnDetail(): Promise { + const save = this.page.getByRole('button', { name: /保\s*存|提交审批|改条件/ }); + if (!(await save.first().isVisible().catch(() => false))) return false; + return save.first().isEnabled().catch(() => false); + } + + async firstOrderNo(): Promise { + return this.tryCell(await this.firstRow(), '单据编号'); + } +} diff --git a/pages/spd/helpers/reuse-open.helper.ts b/pages/spd/helpers/reuse-open.helper.ts index d734351..b29d445 100644 --- a/pages/spd/helpers/reuse-open.helper.ts +++ b/pages/spd/helpers/reuse-open.helper.ts @@ -24,6 +24,10 @@ export type ReuseOpenOptions = { /** * 同页复用 open:beforeAll 只导航一次,beforeEach 复位筛选。 + * + * ⚠️ 仅用于存量报表加速。组内共享同一 BrowserContext/Page,与「每用例独立 context」原则冲突。 + * 厂商 / 准入 / 医院资料等新模块禁止使用;应走 auth.setup + storageState 文件 + 默认 page fixture。 + * * 组内顺序执行,但前序失败不跳过后续(避免 serial 把无关用例整组未跑)。 * 失败后重建会话,避免脏页拖垮下一条。 * 用例内通过 getReport() 取 Page Object,勿使用默认 page fixture。 diff --git a/pages/spd/surgery-base-pkg.page.ts b/pages/spd/surgery-base-pkg.page.ts new file mode 100644 index 0000000..c73c96f --- /dev/null +++ b/pages/spd/surgery-base-pkg.page.ts @@ -0,0 +1,236 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { surgeryData, uniqBasePkgCode, uniqBasePkgName } from '../../data/surgery.data'; +import { SurgerySharedPage, type SurgeryMeta } from './surgery-shared.page'; + +const META: SurgeryMeta = { + path: surgeryData.basePkgPath, + pathPart: surgeryData.basePkgPathPart, + menu: surgeryData.menuBasePkg, + siblings: [ + { name: surgeryData.menuOperType, pathPart: surgeryData.operTypePathPart }, + { name: surgeryData.menuStdPkg, pathPart: surgeryData.stdPkgPathPart }, + ], + forbidden: [surgeryData.operTypePathPart, surgeryData.stdPkgPathPart], +}; + +/** 基础包维护(CSV SURG-BASE-001..022)。 */ +export class SurgeryBasePkgPage extends SurgerySharedPage { + constructor(page: Page) { + super(page, META); + } + + // ---- 定位 ---- + + categorySearch() { + return this.page.getByPlaceholder('请输入分类名称').first(); + } + + categoryTree() { + return this.locators.leftTree; + } + + addCategoryBtn() { + return this.locators.button('新增分类').first(); + } + + addBasePkgBtn() { + return this.locators.button('新增基础包').first(); + } + + productNameInput() { + return this.filterById('#table_search_form_keyword'); + } + + specModelInput() { + return this.filterById('#table_search_form_goodsSpec'); + } + + mfrsNameInput() { + return this.filterById('#table_search_form_goodsMfrsName'); + } + + // ---- 左树分类 ---- + + /** CSV SURG-BASE-001:按分类名称过滤左树。 */ + async searchCategory(keyword: string): Promise { + await this.fill(this.categorySearch(), keyword, `填写分类名称「${keyword}」`); + await this.waitSettle(600); + } + + /** CSV SURG-BASE-003:打开新增分类弹窗。 */ + async openAddCategoryDialog(): Promise { + await this.click(this.addCategoryBtn(), '新增分类'); + await this.expectDialogVisible(); + await this.waitSpinGone(); + } + + categoryNameField(): Locator { + return this.locators.byId('#form_item_kindName'); + } + + // ---- 筛选 ---- + + /** CSV SURG-BASE-006/007/008:按列首行样例筛选。 */ + async searchByProductName(): Promise { + return this.searchByFirstRow('#table_search_form_keyword', '产品名称'); + } + + async searchBySpecModel(): Promise { + return this.searchByFirstRow('#table_search_form_goodsSpec', '规格型号'); + } + + async searchByMfrsName(): Promise { + return this.searchByFirstRow('#table_search_form_goodsMfrsName', '器械注册人'); + } + + /** CSV SURG-BASE-013:mock 反例空态。 */ + async searchMockEmpty(): Promise { + await this.fillFilterById('#table_search_form_keyword', surgeryData.mockBaseKeyword); + await this.query(); + } + + /** CSV SURG-BASE-010:过滤后重置并返回筛选框取值。 */ + async filterThenReset(): Promise<{ before: number; keyword: string; afterValue: string }> { + const keyword = await this.sampleCell('产品名称'); + await this.fillFilterById('#table_search_form_keyword', keyword); + await this.query(); + const before = await this.getTotalCount(); + await this.reset(); + const afterValue = await this.productNameInput().inputValue().catch(() => ''); + return { before, keyword, afterValue }; + } + + // ---- 新增基础包 ---- + + /** CSV SURG-BASE-014:打开新增基础包弹窗。 */ + async openAddBasePkgDialog(): Promise { + await this.click(this.addBasePkgBtn(), '新增基础包'); + await this.expectDialogVisible(); + await this.waitSpinGone(); + } + + pkgNameField(): Locator { + return this.locators.byId('#form_item_pkgName'); + } + + pkgCategoryField(): Locator { + return this.locators.byId('#form_item_kindId'); + } + + pkgCodeField(): Locator { + return this.locators.byId('#form_item_pkgCode'); + } + + remarkField(): Locator { + return this.locators.byId('#form_item_remark'); + } + + /** 填名称/所属分类/包编码(CSV SURG-BASE-016 前置)。 */ + async fillAddForm(opts: { name: string; code: string; withCategory?: boolean }): Promise { + await this.fill(this.pkgNameField(), opts.name, '填写基础包名称'); + if (opts.withCategory !== false) { + await this.selectInDialog('所属分类'); + } + await this.fill(this.pkgCodeField(), opts.code, '填写包编码'); + } + + /** CSV SURG-BASE-015:新增后取消,返回 { total, newName }。 */ + async addThenCancel(): Promise<{ total: number; newName: string }> { + const total = await this.getTotalCount(); + const newName = uniqBasePkgName(); + await this.openAddBasePkgDialog(); + await this.fill(this.pkgNameField(), newName, '填写基础包名称'); + await this.closeDialogIfOpen(); + return { total, newName }; + } + + /** CSV SURG-BASE-016/019:新增成功,返回新增名称。 */ + async addSuccess(opts?: { name?: string; code?: string; doubleConfirm?: boolean }): Promise { + const name = opts?.name ?? uniqBasePkgName(); + const code = opts?.code ?? uniqBasePkgCode(); + await this.openAddBasePkgDialog(); + await this.fillAddForm({ name, code }); + if (opts?.doubleConfirm) { + // 连点两次:第二次可能因弹窗已关而落空,用短超时点击且忽略失败,只验证最终仅落一条 + const confirm = this.dialogButton('确 认').or(this.locators.confirmLikeButton(this.dialog())); + await confirm.click({ timeout: 1_500 }).catch(() => undefined); + await confirm.click({ timeout: 1_500 }).catch(() => undefined); + await this.waitSpinGone(); + } else { + await this.clickConfirm('确 认'); + } + await expect(this.dialog(), '新增成功后弹窗应关闭').toBeHidden({ timeout: 15_000 }); + await this.waitForTableReady(); + return name; + } + + /** CSV SURG-BASE-017/018:仅名称/不选分类的必填拦截。 */ + async addWithMissing(opts: { name?: string; code?: string; withCategory?: boolean }): Promise { + await this.openAddBasePkgDialog(); + if (opts.name) await this.fill(this.pkgNameField(), opts.name, '填写基础包名称'); + if (opts.withCategory) await this.selectInDialog('所属分类'); + if (opts.code) await this.fill(this.pkgCodeField(), opts.code, '填写包编码'); + await this.clickConfirm('确 认'); + } + + /** 新增后按名称检索并断言可见(用例层再断言)。 */ + async searchByName(name: string): Promise { + await this.fillFilterById('#table_search_form_keyword', name); + await this.query(); + } + + /** 后置清理:按名称检索后移除(写操作,二次确认)。 */ + async cleanupByName(name: string): Promise { + await this.searchByName(name); + const row = await this.rowWithAction('移 除'); + if (!row) return; + await this.click(this.rowAction(row, '移 除'), '移 除'); + const confirm = this.dialogButton('确 定').or(this.locators.confirmLikeButton(this.dialog())); + if (await confirm.isVisible().catch(() => false)) { + await this.click(confirm, '确认移除', { operation: 'write' }); + await this.waitForTableReady(); + } else { + await this.page.keyboard.press('Escape').catch(() => undefined); + } + } + + // ---- 行内操作 ---- + + /** CSV SURG-BASE-020:首行编辑并回填,返回首行产品名称。 */ + async openEditFirstRow(): Promise { + const name = await this.tryCell(await this.firstRow(), '产品名称'); + await this.click(this.rowAction(await this.firstRow(), '编 辑'), '编 辑'); + await this.expectDialogVisible(); + await this.waitSpinGone(); + return name; + } + + /** CSV SURG-BASE-021:首行移除并取消,返回 { total, cancelShown }。 */ + async removeFirstRowThenCancel(): Promise<{ total: number; cancelShown: boolean }> { + const total = await this.getTotalCount(); + const row = await this.firstRow(); + await this.click(this.rowAction(row, '移 除'), '移 除'); + const cancel = this.locators.cancelLikeButton(this.dialog()); + const cancelShown = await cancel.isVisible({ timeout: 5_000 }).catch(() => false); + if (cancelShown) await this.closeDialogIfOpen(); + return { total, cancelShown }; + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '物料名称', kind: 'text', value: '样本' }, + { label: '规格型号', kind: 'text', value: '样本' }, + { label: '器械注册人', kind: 'text', value: '样本' }, + { label: '耗材类型', kind: 'select', value: '样本' }, + { label: '是否收费', kind: 'select', value: '样本' }, + ], + { scope }, + ); + } +} + +export const SURGERY_BASE_PKG_PATH = surgeryData.basePkgPath; diff --git a/pages/spd/surgery-oper-type.page.ts b/pages/spd/surgery-oper-type.page.ts new file mode 100644 index 0000000..86dcd80 --- /dev/null +++ b/pages/spd/surgery-oper-type.page.ts @@ -0,0 +1,249 @@ +import type { Locator, Page } from '@playwright/test'; + +import { surgeryData, uniqOperName } from '../../data/surgery.data'; +import { SurgerySharedPage, type SurgeryMeta } from './surgery-shared.page'; + +const META: SurgeryMeta = { + path: surgeryData.operTypePath, + pathPart: surgeryData.operTypePathPart, + menu: surgeryData.menuOperType, + siblings: [ + { name: surgeryData.menuBasePkg, pathPart: surgeryData.basePkgPathPart }, + { name: surgeryData.menuStdPkg, pathPart: surgeryData.stdPkgPathPart }, + ], + forbidden: [surgeryData.basePkgPathPart, surgeryData.stdPkgPathPart], +}; + +/** 术种维护(CSV SURG-TYPE-001..025)。 */ +export class SurgeryOperTypePage extends SurgerySharedPage { + constructor(page: Page) { + super(page, META); + } + + // ---- 定位 ---- + + categorySearch() { + return this.page.getByPlaceholder(/请输入.*术种分类名称|请输入分类名称/).first(); + } + + addBtn() { + return this.locators.button('新 增').or(this.locators.button('新增')).first(); + } + + addOperTypeBtn() { + return this.locators.button('新增术种').first(); + } + + operationNameInput() { + return this.filterById('#table_search_form_operationName'); + } + + diseaseInput() { + return this.filterById('#table_search_form_disease'); + } + + statusTab(name: string): Locator { + return this.page + .locator('.ant-radio-button-wrapper, .ant-tabs-tab, .ant-segmented-item') + .filter({ hasText: new RegExp(name) }) + .first() + .or(this.locators.tab(name)); + } + + // ---- 左树分类 ---- + + async searchCategory(keyword: string): Promise { + await this.fill(this.categorySearch(), keyword, `填写术种分类名称「${keyword}」`); + await this.waitSettle(600); + } + + /** CSV SURG-TYPE-007:打开分类维护弹窗。 */ + async openCategoryDialog(): Promise { + await this.click(this.addBtn(), '新增'); + await this.expectDialogVisible(); + await this.waitSpinGone(); + } + + categoryNameField(): Locator { + return this.locators.byId('#basic_kindName'); + } + + // ---- 筛选 ---- + + /** CSV SURG-TYPE-009:按术种名称筛选。 */ + async searchByOperationName(): Promise { + return this.searchByFirstRow('#table_search_form_operationName', '术种名称'); + } + + /** CSV SURG-TYPE-010:按病种名称筛选。 */ + async searchByDisease(): Promise { + return this.searchByFirstRow('#table_search_form_disease', '病种名称'); + } + + /** CSV SURG-TYPE-014:mock 反例空态。 */ + async searchMockEmpty(): Promise { + await this.fillFilterById('#table_search_form_operationName', surgeryData.mockOperKeyword); + await this.query(); + } + + /** CSV SURG-TYPE-012:左树 + 筛选 + Tab 组合过滤。 */ + async combinedFilter(tabName = surgeryData.statusAll): Promise { + const category = await this.clickFirstTreeNode(); + const keyword = await this.sampleCell('术种名称'); + await this.fillFilterById('#table_search_form_operationName', keyword); + await this.query(); + await this.switchStatusTab(tabName); + return `${category}|${keyword}`; + } + + /** CSV SURG-TYPE-013:过滤后重置,返回筛选框是否清空。 */ + async filterThenReset(): Promise<{ before: number; afterValue: string }> { + const keyword = await this.sampleCell('术种名称'); + await this.fillFilterById('#table_search_form_operationName', keyword); + await this.query(); + const before = await this.getTotalCount(); + await this.reset(); + const afterValue = await this.operationNameInput().inputValue().catch(() => ''); + return { before, afterValue }; + } + + // ---- 状态 Tab ---- + + /** 读取状态 Tab 括号计数(如「停用(3)」)。 */ + async tabCount(name: string): Promise { + const text = ((await this.statusTab(name).innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + const match = text.match(/[((](\d+)[))]/) ?? text.match(/(\d+)/); + return match ? Number(match[1]) : null; + } + + async switchStatusTab(name: string): Promise { + await this.click(this.statusTab(name), `切换状态 Tab「${name}」`); + await this.waitForTableReady(); + } + + /** CSV SURG-TYPE-011:读取各状态 Tab 计数与列表总数。 */ + async statusTabCounts(): Promise<{ + all: number | null; + disabled: number | null; + unclassified: number | null; + }> { + const result = { + all: await this.tabCount(surgeryData.statusAll), + disabled: await this.tabCount(surgeryData.statusDisabled), + unclassified: await this.tabCount(surgeryData.statusUnclassified), + }; + return result; + } + + // ---- 列表与编码(CSV SURG-TYPE-016/017)---- + + async firstIcd10Code(): Promise { + return this.sampleCell('诊断编码(ICD-10)'); + } + + async firstIcd9Code(): Promise { + return this.sampleCell('操作编码(ICD-9-CM-3)'); + } + + async firstIcd10Name(): Promise { + return this.sampleCell('诊断名称(ICD-10)'); + } + + async firstIcd9Name(): Promise { + return this.sampleCell('操作名称(ICD-9-CM-3)'); + } + + // ---- 新增术种 ---- + + /** CSV SURG-TYPE-020:打开新增术种入口。 */ + async openAddOperType(): Promise { + await this.click(this.addOperTypeBtn(), '新增术种'); + await this.waitSpinGone(); + await this.waitSettle(400); + } + + /** CSV SURG-TYPE-021:新增后取消,返回 { total, newName }。 */ + async addThenCancel(): Promise<{ total: number; newName: string }> { + const total = await this.getTotalCount(); + const newName = uniqOperName(); + await this.openAddOperType(); + const nameField = this.dialogFormItem('术种名称').locator('input').first(); + if (await nameField.isVisible().catch(() => false)) { + await this.fill(nameField, newName, '填写术种名称'); + } + await this.closeDialogIfOpen(); + return { total, newName }; + } + + /** CSV SURG-TYPE-022:名称为空的必填拦截(返回弹窗是否仍在、拦截后总数)。 */ + async addWithEmptyName(): Promise<{ dialogStillOpen: boolean; total: number }> { + const total = await this.getTotalCount(); + await this.openAddOperType(); + await this.clickConfirm('保 存'); + const dialogStillOpen = await this.dialog().isVisible().catch(() => false); + return { dialogStillOpen, total }; + } + + // ---- 行内操作 ---- + + /** CSV SURG-TYPE-023:首行编辑并回填,返回首行术种名称。 */ + async openEditFirstRow(): Promise { + const name = await this.tryCell(await this.firstRow(), '术种名称'); + await this.click(this.rowAction(await this.firstRow(), '编 辑'), '编 辑'); + await this.expectDialogVisible(); + await this.waitSpinGone(); + return name; + } + + /** + * CSV SURG-TYPE-024:首行停用并确认。 + * 返回停用前「停用」Tab 计数、是否出现确认框、停用后计数,供用例断言计数 +1。 + */ + async disableFirstRowConfirmed(): Promise<{ + before: number | null; + confirmShown: boolean; + after: number | null; + }> { + const before = await this.tabCount(surgeryData.statusDisabled); + const row = (await this.rowWithAction('停 用')) ?? (await this.rowWithAction('停用')); + if (!row) { + await this.skipWhenDataInsufficient('无「停用」可操作行'); + } + await this.click(this.rowAction(row as Locator, '停 用').or(this.rowAction(row as Locator, '停用')), '停用'); + const confirm = this.dialogButton('确 定').or(this.locators.confirmLikeButton(this.dialog())); + const confirmShown = await confirm.isVisible({ timeout: 5_000 }).catch(() => false); + if (confirmShown) { + await this.click(confirm, '确认停用', { operation: 'write' }); + await this.waitForTableReady(); + } + const after = await this.tabCount(surgeryData.statusDisabled); + return { before, confirmShown, after }; + } + + /** 后置:对目标术种「启用」恢复。 */ + async enableByName(name: string): Promise { + await this.fillFilterById('#table_search_form_operationName', name); + await this.query(); + const row = (await this.rowWithAction('启 用')) ?? (await this.rowWithAction('启用')); + if (!row) return; + await this.click(this.rowAction(row, '启 用').or(this.rowAction(row, '启用')), '启用'); + const confirm = this.dialogButton('确 定').or(this.locators.confirmLikeButton(this.dialog())); + if (await confirm.isVisible({ timeout: 5_000 }).catch(() => false)) { + await this.click(confirm, '确认启用', { operation: 'write' }); + await this.waitForTableReady(); + } + } + + /** CSV SURG-TYPE-025:首行停用后取消,返回 { before, cancelShown }。 */ + async disableFirstRowThenCancel(): Promise<{ before: number | null; cancelShown: boolean }> { + const before = await this.tabCount(surgeryData.statusDisabled); + const row = (await this.rowWithAction('停 用')) ?? (await this.rowWithAction('停用')) ?? (await this.firstRow()); + await this.click(this.rowAction(row, '停 用').or(this.rowAction(row, '停用')).or(this.rowAction(row, '启 用')), '停用'); + const cancel = this.locators.cancelLikeButton(this.dialog()); + const cancelShown = await cancel.isVisible({ timeout: 5_000 }).catch(() => false); + if (cancelShown) await this.closeDialogIfOpen(); + return { before, cancelShown }; + } +} + +export const SURGERY_OPER_TYPE_PATH = surgeryData.operTypePath; diff --git a/pages/spd/surgery-shared.page.ts b/pages/spd/surgery-shared.page.ts new file mode 100644 index 0000000..0ce8722 --- /dev/null +++ b/pages/spd/surgery-shared.page.ts @@ -0,0 +1,487 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { env } from '../../config/env.config'; +import { BasePage } from '../../core/base.page'; +import { SurgerySharedLocators } from '../../locators/spd/surgery-shared.locator'; + +export type SurgeryMeta = { + /** 直达路径 */ + path: string; + /** URL 片段(断言用) */ + pathPart: string; + /** 侧栏叶子菜单名 */ + menu: string; + /** 同组页签其它两页,用于跨页只读跳转 */ + siblings: { name: string; pathPart: string }[]; + /** 不应误入的 URL 片段 */ + forbidden: string[]; +}; + +/** 左树快照(供用例层断言) */ +export type TreeSnapshot = { nodes: number; texts: string[] }; + +/** + * 手术套包设置三页共用:菜单进入、左树、筛选、列表、分页、弹窗。 + * 断言留在 spec,本类只做动作与取数。 + */ +export class SurgerySharedPage extends BasePage { + readonly locators: SurgerySharedLocators; + + constructor( + page: Page, + protected readonly meta: SurgeryMeta, + ) { + super(page); + this.locators = new SurgerySharedLocators(page); + } + + get pagePath(): string { + return this.meta.path; + } + + // ---------- 页面进入 ---------- + + /** 直达页面(含模块健康检查与登录态校验)。 */ + async open(): Promise { + await this.goto(this.meta.path); + await this.waitSpinGone(); + await this.waitSettle(400); + await this.waitContentReady(); + await this.assertPageHealthy(`打开 ${this.meta.path}`); + expect(this.page.url(), 'storageState 失效回到登录页').not.toMatch(/\/login/); + } + + /** 按「基础设置 → 手术套包设置 → 叶子菜单」进入。 */ + async openFromMenu(): Promise { + await this.goto(env.homePath); + await this.waitSpinGone(); + await this.click(this.locators.menuBasic, '展开基础设置'); + const opened = await this.locators.surgerySubmenuTitle + .evaluate((el) => el.closest('.ant-menu-submenu')?.classList.contains('ant-menu-submenu-open') ?? false) + .catch(() => false); + if (!opened) await this.click(this.locators.surgerySubmenuTitle, '展开手术套包设置'); + await this.click(this.locators.menuLeaf(this.meta.menu), `进入${this.meta.menu}`); + await this.page.waitForURL((url) => url.pathname.includes(this.meta.pathPart), { timeout: 20_000 }); + await this.waitSpinGone(); + await this.waitSettle(600); + await this.waitContentReady(); + await this.assertPageHealthy(`菜单进入${this.meta.menu}`); + } + + /** 等左树或主表挂载,避免菜单切入后尚未渲染。 */ + async waitContentReady(): Promise { + const markers = this.page + .locator('.ant-tree, .ant-table-thead th, .ant-table-header th, thead th, .ant-empty') + .first(); + await markers.waitFor({ state: 'visible', timeout: 20_000 }).catch(() => undefined); + await this.waitForTableReady(); + } + + /** 断言已在目标页,且未误入同组其它页。 */ + async expectEntered(): Promise { + expect(this.page.url(), `URL 应含 ${this.meta.pathPart}`).toContain(this.meta.pathPart); + for (const part of this.meta.forbidden) { + expect(this.page.url(), `不应误入 ${part}`).not.toContain(part); + } + await this.waitContentReady(); + const hasTree = await this.locators.leftTree.isVisible().catch(() => false); + const hasHeader = (await this.locators.tableHeader.count()) > 0; + const hasEmpty = await this.page.getByText('暂无数据').first().isVisible().catch(() => false); + expect(hasTree || hasHeader || hasEmpty, '左侧分类树、列表表头或空态至少其一可见').toBeTruthy(); + } + + // ---------- 同组页签 ---------- + + async switchTab(name: string, expectPathPart?: string): Promise { + await this.click(this.locators.tab(name), `点击 Tab「${name}」`); + if (expectPathPart) { + await this.page.waitForURL((url) => url.pathname.includes(expectPathPart), { timeout: 20_000 }).catch(() => undefined); + } + await this.waitSpinGone(); + await this.waitSettle(500); + await this.waitContentReady(); + await this.assertPageHealthy(`切换 Tab「${name}」`); + } + + async expectTabPath(pathPart: string): Promise { + expect(this.page.url(), `Tab 切换后 URL 应含 ${pathPart}`).toContain(pathPart); + } + + /** 未登录直达探测:返回是否被拦截(用例层断言)。 */ + async probeUnauthAccess(): Promise<{ onLogin: boolean; hasBusinessRow: boolean }> { + await this.page.goto(this.meta.path, { waitUntil: 'domcontentloaded' }); + await this.page.waitForLoadState('networkidle', { timeout: 8_000 }).catch(() => undefined); + const onLogin = /\/login/i.test(this.page.url()); + const hasBusinessRow = await this.page + .locator('.ant-table-tbody tr.ant-table-row') + .first() + .isVisible() + .catch(() => false); + return { onLogin, hasBusinessRow }; + } + + // ---------- 左侧分类树 ---------- + + treeNodes(): Locator { + return this.locators.treeNodes(); + } + + async getTreeCount(): Promise { + return this.treeNodes().count(); + } + + /** 左树快照(节点数 + 文本)。 */ + async snapshotTree(): Promise { + await this.waitSettle(200); + const nodes = this.locators.treeNodes(); + const count = await nodes.count(); + const texts: string[] = []; + for (let i = 0; i < count; i += 1) { + texts.push(((await nodes.nth(i).innerText().catch(() => '')) ?? '').replace(/\s/g, '')); + } + return { nodes: count, texts }; + } + + async firstTreeNodeText(): Promise { + const snap = await this.snapshotTree(); + const hit = snap.texts.find((t) => t && t !== '-' && t !== '--'); + if (!hit) { + await this.skipWhenDataInsufficient('左树分类为空'); + } + return hit ?? ''; + } + + /** 读取首个带括号计数的节点:名称与计数(CSV SURG-TYPE-006)。 */ + async firstCountedTreeNode(): Promise<{ name: string; count: number } | null> { + const nodes = this.locators.treeNodes(); + const count = Math.min(await nodes.count(), 30); + for (let i = 0; i < count; i += 1) { + const text = ((await nodes.nth(i).innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + const match = text.match(/^(.*?)[((](\d+)[))]$/); + if (match && match[1]) { + return { name: match[1], count: Number(match[2]) }; + } + } + return null; + } + + async searchTree(keyword: string): Promise { + const input = this.treeSearch(); + await this.fill(input, keyword, `填写分类名称搜索「${keyword}」`); + await input.press('Enter').catch(() => undefined); + await this.waitSettle(600); + } + + async clearTreeSearch(): Promise { + const input = this.treeSearch(); + await input.fill(''); + await input.press('Enter').catch(() => undefined); + await this.waitSettle(600); + } + + /** 树过滤框:按 placeholder 精确优先,回退到左树临近输入。 */ + treeSearch(): Locator { + const byPlaceholder = this.page.getByPlaceholder(/请输入.*分类名称/).first(); + return byPlaceholder.or(this.locators.treeSearchInput); + } + + async clickTreeNode(name: string): Promise { + await this.click(this.locators.treeNode(name), `选择分类「${name}」`); + await this.waitForTableReady(); + } + + /** 选择首个分类节点并返回其名称(用例层断言用)。 */ + async clickFirstTreeNode(): Promise { + const name = await this.firstTreeNodeText(); + await this.clickTreeNode(name); + return name; + } + + // ---------- 右侧筛选 ---------- + + /** CSV 明确给出筛选控件 id 时优先用 id。 */ + filterById(id: string): Locator { + return this.locators.byId(id); + } + + /** 通用筛选:优先 id,回退 placeholder/label。 */ + filterInput(opts: { id?: string; placeholder?: string; label?: string }): Locator { + if (opts.id) { + const byId = this.locators.byId(opts.id); + return byId; + } + if (opts.placeholder) return this.page.getByPlaceholder(opts.placeholder).first(); + const label = opts.label ?? ''; + return this.page + .locator('.ant-form-item') + .filter({ hasText: label }) + .locator('input') + .first(); + } + + async fillFilterById(id: string, keyword: string): Promise { + await this.fill(this.filterById(id), keyword, `填写筛选「${id}」`); + } + + async query(): Promise { + await this.click(this.locators.button('查 询').first(), '查 询'); + await this.waitForTableReady(); + } + + async reset(): Promise { + await this.click(this.locators.button('重 置').first(), '重 置'); + await this.waitForTableReady(); + } + + /** 按首行某列样例值填写筛选并查询;返回关键字(用例层断言用)。 */ + async searchByFirstRow(id: string, header: string): Promise { + const keyword = await this.sampleCell(header); + await this.fillFilterById(id, keyword); + await this.query(); + return keyword; + } + + // ---------- 列表取数 ---------- + + dataRows(): Locator { + return this.page.locator('.ant-table-tbody > tr.ant-table-row:not(.ant-table-measure-row)'); + } + + async getDataRowCount(): Promise { + return this.dataRows().count(); + } + + async getTotalCount(): Promise { + const text = (await this.locators.paginationTotal.textContent().catch(() => '')) ?? ''; + const match = text.match(/共\s*(\d+)\s*条/); + return match ? Number(match[1]) : 0; + } + + async waitForTableReady(): Promise { + await this.waitSpinGone(); + await this.waitSettle(200); + await this.assertPageHealthy('列表加载'); + } + + async headerCells(): Promise { + await this.locators.tableHeader.first().waitFor({ state: 'attached', timeout: 12_000 }).catch(() => undefined); + return this.locators.tableHeader; + } + + async headerTexts(): Promise { + return (await (await this.headerCells()).allTextContents()).map((item) => item.replace(/\s/g, '')); + } + + async headerIndex(name: string): Promise { + const texts = await this.headerTexts(); + const index = texts.findIndex((item) => item.includes(name.replace(/\s/g, ''))); + expect(index, `找不到列「${name}」`).toBeGreaterThanOrEqual(0); + return index; + } + + async cellText(row: Locator, header: string): Promise { + const index = await this.headerIndex(header); + return ((await row.locator('td').nth(index).innerText()) ?? '').trim(); + } + + async tryCell(row: Locator, header: string): Promise { + try { + return (await this.cellText(row, header)).trim(); + } catch { + return ''; + } + } + + async firstRow(): Promise { + const row = this.dataRows().first(); + await expect(row, '列表至少应有 1 条数据').toBeVisible({ timeout: 15_000 }); + return row; + } + + /** 首屏某列样例值(跳过占位),数据不足时 skip。 */ + async sampleCell(header: string): Promise { + const rows = this.dataRows(); + const count = Math.min(await rows.count(), 8); + for (let i = 0; i < count; i += 1) { + const text = (await this.tryCell(rows.nth(i), header)).replace(/\s/g, ''); + if (text && text !== '--' && text !== '-') return text; + } + await this.skipWhenDataInsufficient(`首屏「${header}」为空`); + return ''; + } + + /** 抽样行某列是否均含关键字(用例层断言)。 */ + async rowsMatchKeyword(header: string, keyword: string, sample = 5): Promise { + const needle = keyword.replace(/\s/g, '').slice(0, 12); + const rows = this.dataRows(); + const count = await rows.count(); + if (count === 0) return false; + const limit = Math.min(count, sample); + for (let i = 0; i < limit; i += 1) { + const text = (await this.tryCell(rows.nth(i), header)).replace(/\s/g, ''); + if (!text.includes(needle)) return false; + } + return true; + } + + /** 列表中是否存在包含关键字的行(跨展开/分组)。 */ + async hasRowContaining(keyword: string): Promise { + const rows = this.dataRows(); + const count = await rows.count(); + for (let i = 0; i < count; i += 1) { + const text = ((await rows.nth(i).innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + if (text.includes(keyword.replace(/\s/g, ''))) return true; + } + return false; + } + + /** 列表快照供用例层断言(不在此 expect)。 */ + async snapshotList(): Promise<{ rows: number; total: number; emptyVisible: boolean }> { + await this.waitForTableReady(); + const rows = await this.getDataRowCount(); + const total = await this.getTotalCount(); + const emptyVisible = + (await this.locators.empty.isVisible().catch(() => false)) || + (await this.page.getByText('暂无数据').first().isVisible().catch(() => false)); + return { rows, total, emptyVisible }; + } + + // ---------- 分页 ---------- + + async currentPage(): Promise { + const text = (await this.locators.paginationActive.innerText().catch(() => '')) ?? ''; + const match = text.match(/\d+/); + return match ? Number(match[0]) : 1; + } + + async prevDisabled(): Promise { + const cls = (await this.locators.paginationPrev.getAttribute('class').catch(() => '')) ?? ''; + if (/disabled/.test(cls)) return true; + return this.locators.paginationPrev + .getAttribute('aria-disabled') + .then((v) => v === 'true') + .catch(() => false); + } + + /** 切换每页条数,返回 { total, rowsOnPage }。 */ + async changePageSize(size: number): Promise<{ total: number; rowsOnPage: number }> { + const total = await this.getTotalCount(); + const select = this.page.locator('.ant-pagination-options .ant-select').first(); + await this.click(select, '打开每页条数'); + const option = this.page + .locator('.ant-select-dropdown:visible .ant-select-item-option') + .filter({ hasText: new RegExp(`^\\s*${size}\\s*条\\s*/\\s*页`) }) + .first(); + if (await option.isVisible().catch(() => false)) { + await this.click(option, `选择每页 ${size} 条`); + } else { + await this.page.keyboard.press('Escape'); + await this.skipWhenDataInsufficient(`无每页 ${size} 条选项`); + } + await this.waitForTableReady(); + return { total, rowsOnPage: await this.getDataRowCount() }; + } + + async goToPage2(): Promise { + const item = this.page.locator('.ant-pagination-item-2').first(); + if (await item.isVisible().catch(() => false)) { + await this.click(item, '翻到第 2 页'); + } else if (!(await this.prevDisabled())) { + await this.click(this.locators.paginationNext, '下一页'); + } + await this.waitForTableReady(); + } + + // ---------- 行内操作 ---------- + + rowAction(row: Locator, name: string): Locator { + const compact = name.replace(/\s/g, ''); + const pattern = new RegExp(compact.split('').join('\\s*')); + return row.getByRole('button', { name: pattern }).or(row.getByText(pattern)).first(); + } + + async rowWithAction(name: string): Promise { + const rows = this.dataRows(); + const count = Math.min(await rows.count(), 15); + for (let i = 0; i < count; i += 1) { + const row = rows.nth(i); + if (await this.rowAction(row, name).isVisible().catch(() => false)) return row; + } + return null; + } + + // ---------- 弹窗 ---------- + + dialog(): Locator { + return this.locators.dialog; + } + + dialogButton(name: string): Locator { + return this.locators.button(name, this.dialog()).first(); + } + + dialogFormItem(label: string): Locator { + return this.dialog().locator('.ant-form-item').filter({ hasText: label }).first(); + } + + formItem(label: string): Locator { + return this.page.locator('.ant-form-item').filter({ hasText: label }).first(); + } + + /** 弹窗内下拉选择:按 label 找到表单项中的 ant-select,选择首个可选项。 */ + async selectInDialog(label: string): Promise { + const select = this.dialogFormItem(label).locator('.ant-select').first(); + await this.click(select, `打开「${label}」下拉`); + const option = this.page + .locator('.ant-select-dropdown:visible .ant-select-item-option:not(.ant-select-item-option-disabled)') + .first(); + if (await option.isVisible().catch(() => false)) { + await this.click(option, `选择「${label}」首项`); + } else { + await this.page.keyboard.press('Escape'); + } + await this.waitSettle(200); + } + + /** 弹窗内级联选择首条路径(分类树)。 */ + async pickCascaderFirstPath(): Promise { + const target = this.dialog().locator('.ant-cascader, .ant-select').first(); + await this.click(target, '打开级联选择'); + for (let i = 0; i < 4; i += 1) { + const item = this.page + .locator('.ant-cascader-menu:visible .ant-cascader-menu-item:not(.ant-cascader-menu-item-disabled)') + .first(); + if (!(await item.isVisible().catch(() => false))) break; + await this.click(item, `选择第 ${i + 1} 级`); + await this.waitSettle(150); + } + } + + async expectFormError(): Promise { + const error = this.locators.formError.first(); + await expect(error, '应出现必填或非法提示').toBeVisible({ timeout: 8_000 }); + } + + async expectDialogVisible(): Promise { + await expect(this.dialog(), '弹窗应可见').toBeVisible({ timeout: 15_000 }); + } + + async closeDialogIfOpen(): Promise { + if (!(await this.dialog().isVisible().catch(() => false))) return; + const cancel = this.locators.cancelLikeButton(this.dialog()); + if (await cancel.isVisible().catch(() => false)) { + await this.click(cancel, '关闭弹窗'); + } else { + await this.page.keyboard.press('Escape'); + } + await expect(this.dialog()).toBeHidden({ timeout: 8_000 }).catch(() => undefined); + } + + /** 点确定类按钮(写操作,不盲目重试)。 */ + async clickConfirm(label = '确 认'): Promise { + await this.click(this.dialogButton(label).or(this.locators.confirmLikeButton(this.dialog())), label, { + operation: 'write', + }); + await this.waitSpinGone(); + } +} diff --git a/pages/spd/surgery-std-pkg.page.ts b/pages/spd/surgery-std-pkg.page.ts new file mode 100644 index 0000000..53a54f6 --- /dev/null +++ b/pages/spd/surgery-std-pkg.page.ts @@ -0,0 +1,450 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { surgeryData } from '../../data/surgery.data'; +import { SurgerySharedPage, type SurgeryMeta } from './surgery-shared.page'; + +const META: SurgeryMeta = { + path: surgeryData.stdPkgPath, + pathPart: surgeryData.stdPkgPathPart, + menu: surgeryData.menuStdPkg, + siblings: [ + { name: surgeryData.menuBasePkg, pathPart: surgeryData.basePkgPathPart }, + { name: surgeryData.menuOperType, pathPart: surgeryData.operTypePathPart }, + ], + forbidden: [surgeryData.basePkgPathPart, surgeryData.operTypePathPart], +}; + +/** 标准套包维护(CSV SURG-STD-001..029)。 */ +export class SurgeryStdPkgPage extends SurgerySharedPage { + constructor(page: Page) { + super(page, META); + } + + // ---- 定位 ---- + + categorySearch() { + return this.page.getByPlaceholder('请输入术种分类名称、术种名称、标准套包名称').first(); + } + + addBasePkgBtn() { + return this.locators.button('添加基础包').first(); + } + + addLoosePartBtn() { + return this.locators.button('添加散件').first(); + } + + productNameInput() { + return this.filterById('#table_search_form_keyword'); + } + + specModelInput() { + return this.filterById('#table_search_form_goodsSpec'); + } + + mfrsNameInput() { + return this.filterById('#table_search_form_goodsMfrsName'); + } + + // ---- 左树分类 ---- + + async searchCategory(keyword: string): Promise { + await this.fill(this.categorySearch(), keyword, `填写术种分类名称「${keyword}」`); + await this.waitSettle(600); + } + + // ---- 筛选 ---- + + async searchByProductName(): Promise { + return this.searchByFirstRow('#table_search_form_keyword', '产品名称'); + } + + async searchBySpecModel(): Promise { + return this.searchByFirstRow('#table_search_form_goodsSpec', '规格型号'); + } + + async searchByMfrsName(): Promise { + return this.searchByFirstRow('#table_search_form_goodsMfrsName', '器械注册人'); + } + + /** CSV SURG-STD-015:mock 反例空态。 */ + async searchMockEmpty(): Promise { + await this.fillFilterById('#table_search_form_keyword', surgeryData.mockStdKeyword); + await this.query(); + } + + /** CSV SURG-STD-013:过滤后重置,返回筛选框是否清空。 */ + async filterThenReset(): Promise<{ before: number; afterValue: string }> { + const keyword = await this.sampleCell('产品名称'); + await this.fillFilterById('#table_search_form_keyword', keyword); + await this.query(); + const before = await this.getTotalCount(); + await this.reset(); + const afterValue = await this.productNameInput().inputValue().catch(() => ''); + return { before, afterValue }; + } + + // ---- 包名称分组展开/收起 ---- + + /** 首个可展开的包分组(含 expand 图标的行)。 */ + firstExpandableGroup(): Locator { + return this.page + .locator('.ant-table-tbody tr.ant-table-row') + .filter({ has: this.page.locator('.ant-table-row-expand-icon') }) + .first(); + } + + /** + * CSV SURG-STD-012:读取首个包分组名,展开后取子项行数,再收起。 + * 返回展开/收起时「子项行」定位数与可见性,供用例断言「展开显示、收起隐藏」。 + */ + async toggleFirstGroup(): Promise<{ + groupName: string; + expandedChildCount: number; + collapsedChildCount: number; + expandedChildVisible: boolean; + collapsedChildVisible: boolean; + }> { + const group = this.firstExpandableGroup(); + if (!(await group.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('无可用包分组可展开'); + } + const groupName = ((await group.innerText().catch(() => '')) ?? '').replace(/\s/g, '').slice(0, 20); + const expandIcon = group.locator('.ant-table-row-expand-icon').first(); + await this.click(expandIcon, '展开包分组'); + await this.waitSettle(500); + const expandedChild = this.childRows(); + const expandedChildCount = await expandedChild.count(); + const expandedChildVisible = await expandedChild.first().isVisible().catch(() => false); + await this.click(expandIcon, '收起包分组'); + await this.waitSettle(500); + const collapsedChild = this.childRows(); + const collapsedChildCount = await collapsedChild.count(); + const collapsedChildVisible = await collapsedChild.first().isVisible().catch(() => false); + return { groupName, expandedChildCount, collapsedChildCount, expandedChildVisible, collapsedChildVisible }; + } + + /** 子项行:分组展开后 TR 带 .ant-table-row-level-1(或无 expand-icon 的明细行)。 */ + childRows(): Locator { + return this.page.locator( + '.ant-table-tbody tr.ant-table-row-level-1, .ant-table-tbody tr.ant-table-row:not(:has(.ant-table-row-expand-icon))', + ); + } + + /** 展开首个分组并返回子项行定位(供行内操作)。 */ + async expandFirstGroup(): Promise<{ groupName: string; childRows: Locator }> { + const group = this.firstExpandableGroup(); + if (!(await group.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('无可用包分组可展开'); + } + const groupName = ((await group.innerText().catch(() => '')) ?? '').replace(/\s/g, '').slice(0, 20); + await this.click(group.locator('.ant-table-row-expand-icon').first(), '展开包分组'); + await this.waitSettle(500); + const childRows = this.page.locator('.ant-table-tbody tr.ant-table-row'); + return { groupName, childRows }; + } + + // ---- 添加基础包弹窗 ---- + + /** CSV SURG-STD-016:打开添加基础包弹窗。 */ + async openAddBasePkgDialog(): Promise { + await this.click(this.addBasePkgBtn(), '添加基础包'); + await this.expectDialogVisible(); + await this.waitSpinGone(); + } + + addBasePkgSearchInput(): Locator { + return this.page.getByPlaceholder('请输入分类/基础包名称').first(); + } + + /** 「确定(N)」按钮:返回定位与当前计数。 */ + confirmCountButton(): Locator { + return this.dialog().getByRole('button').filter({ hasText: /确定\s*[((]?\s*\d*\s*[))]?/ }).first(); + } + + async confirmCount(): Promise { + const text = ((await this.confirmCountButton().innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + const match = text.match(/[((](\d+)[))]/); + return match ? Number(match[1]) : null; + } + + /** 弹窗内基础包勾选框(首列 checkbox)。 */ + dialogCheckboxes(): Locator { + return this.dialog().locator('.ant-table-tbody input[type="checkbox"], .ant-checkbox-input'); + } + + /** + * CSV SURG-STD-017:在弹窗搜索框输入真实基础包关键字并回车,勾选 1 个基础包。 + * 返回关键字与「确定(N)」计数前后值,供用例断言筛选生效与计数联动。 + */ + async filterAndCheckFirst(keyword?: string): Promise<{ + keyword: string; + before: number | null; + after: number | null; + candidateRows: number; + }> { + await this.openAddBasePkgDialog(); + const before = await this.confirmCount(); + const search = this.addBasePkgSearchInput(); + // 关键字:优先入参;否则从弹窗明细首行取基础包名称(真实关键字,不再填空串) + const kw = keyword ?? (await this.sampleDialogBasePkgName()); + let candidateRows = 0; + if (await search.isVisible().catch(() => false)) { + await this.fill(search, kw, `搜索基础包「${kw}」`); + await search.press('Enter').catch(() => undefined); + await this.waitSettle(600); + candidateRows = await this.dialogCheckboxes().count(); + } + const checkbox = this.dialogCheckboxes().first(); + await this.click(checkbox, '勾选首个基础包'); + await this.waitSettle(300); + const after = await this.confirmCount(); + return { keyword: kw, before, after, candidateRows }; + } + + /** 弹窗明细首行基础包名称(作为真实筛选关键字)。 */ + async sampleDialogBasePkgName(): Promise { + const rows = this.dialog().locator('.ant-table-tbody tr.ant-table-row'); + const count = Math.min(await rows.count(), 8); + for (let i = 0; i < count; i += 1) { + const text = ((await rows.nth(i).innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + const hit = text.match(/[\u4e00-\u9fa5A-Za-z0-9-]{4,}/); + if (hit) return hit[0].slice(0, 8); + } + await this.skipWhenDataInsufficient('弹窗基础包明细为空'); + return ''; + } + + /** CSV SURG-STD-018:构成数量步进器点「Increase Value」,返回数量前后数值。 */ + async increaseFirstQuantity(): Promise<{ before: number; after: number }> { + const increase = this.dialog().getByLabel('Increase Value').first(); + const visible = await increase.isVisible().catch(() => false); + if (!visible) { + await this.skipWhenDataInsufficient('无「构成数量」步进器(未勾选基础包)'); + } + const spin = this.dialog().getByRole('spinbutton').first(); + const before = Number((await spin.inputValue().catch(() => '1')) || '1'); + await this.click(increase, 'Increase Value'); + await this.waitSettle(300); + const after = Number((await spin.inputValue().catch(() => '1')) || '1'); + return { before, after }; + } + + /** CSV SURG-STD-019:勾选后取消,返回 { total, cancelOk }。 */ + async addBasePkgThenCancel(): Promise<{ total: number; cancelOk: boolean }> { + const total = await this.getTotalCount(); + await this.openAddBasePkgDialog(); + const checkbox = this.dialogCheckboxes().first(); + if (await checkbox.isVisible().catch(() => false)) { + await this.click(checkbox, '勾选首个基础包'); + } + await this.closeDialogIfOpen(); + const cancelOk = !(await this.dialog().isVisible().catch(() => true)); + return { total, cancelOk }; + } + + /** CSV SURG-STD-020:未选择时确定按钮是否禁用。 */ + async confirmDisabledWhenEmpty(): Promise { + await this.openAddBasePkgDialog(); + const btn = this.confirmCountButton(); + const disabled = await btn.isDisabled().catch(() => false); + const cls = (await btn.getAttribute('class').catch(() => '')) ?? ''; + return disabled || /disabled/.test(cls); + } + + /** + * CSV SURG-STD-021:再次添加同一基础包。 + * 返回首次添加的产品名与「同一产品名出现的行数」,供用例断言不产生重复脏行。 + */ + async addSameBasePkgAgain(): Promise<{ + before: number; + after: number; + productName: string; + sameProductRows: number; + }> { + // 先记录首次添加的目标产品名(弹窗明细首行) + const total0 = await this.getTotalCount(); + await this.openAddBasePkgDialog(); + const productName = await this.sampleDialogBasePkgName(); + const checkbox = this.dialogCheckboxes().first(); + if (await checkbox.isVisible().catch(() => false)) { + await this.click(checkbox, '勾选首个基础包'); + } + await this.clickConfirm('确 定'); + await expect(this.dialog()).toBeHidden({ timeout: 15_000 }).catch(() => undefined); + await this.waitForTableReady(); + const after = await this.getTotalCount(); + const sameProductRows = await this.countRowsContaining(productName); + return { before: total0, after, productName, sameProductRows }; + } + + /** 统计列表中包含指定文本的行数(用于重复添加防重判定)。 */ + async countRowsContaining(keyword: string): Promise { + const needle = keyword.replace(/\s/g, ''); + const rows = this.dataRows(); + const count = await rows.count(); + let hits = 0; + for (let i = 0; i < count; i += 1) { + const text = ((await rows.nth(i).innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + if (text.includes(needle)) hits += 1; + } + return hits; + } + + // ---- 添加散件弹窗 ---- + + /** CSV SURG-STD-022:打开添加散件弹窗(产品列表视图)。 */ + async openAddLoosePartDialog(): Promise { + await this.click(this.addLoosePartBtn(), '添加散件'); + await this.expectDialogVisible(); + await this.waitSpinGone(); + await this.waitSettle(500); + } + + /** 弹窗内视图切换(产品列表 / 规格明细 / 应拣明细)。 */ + dialogViewTab(name: string): Locator { + return this.dialog() + .locator('.ant-tabs-tab, .ant-radio-button-wrapper, .ant-segmented-item') + .filter({ hasText: new RegExp(name) }) + .first() + .or(this.dialog().getByText(new RegExp(name)).first()); + } + + /** 散件弹窗内的产品名称筛选框(限定在弹窗内,避免命中主列表)。 */ + loosePartProductSearch(): Locator { + return this.dialog().getByPlaceholder(/产品名称/).first(); + } + + /** + * CSV SURG-STD-023:在产品名称筛选框输入样例关键字,勾选首个匹配散件。 + * 返回关键字、筛选后候选行数、勾选后应拣计数,供用例断言「筛选生效 + 进入应拣明细 + 计数同步」。 + */ + async filterAndCheckLoosePart(): Promise<{ + keyword: string; + candidateRows: number; + selectedCount: number; + }> { + const keyword = await this.sampleCell('产品名称'); + const search = this.loosePartProductSearch(); + if (await search.isVisible().catch(() => false)) { + await this.fill(search, keyword, `填写产品名称筛选「${keyword}」`); + await search.press('Enter').catch(() => undefined); + await this.waitSettle(600); + } + const candidateRows = await this.dialog().locator('.ant-table-tbody tr.ant-table-row').count(); + const checkbox = this.dialogCheckboxes().first(); + await this.click(checkbox, '勾选首个散件'); + await this.waitSettle(400); + const selectedCount = await this.loosePartSelectedCount(); + return { keyword, candidateRows, selectedCount }; + } + + /** 应拣明细已选条数(弹窗内明细行数或选中计数徽标)。 */ + async loosePartSelectedCount(): Promise { + const badgeText = ((await this.dialog() + .locator('.ant-badge, [class*="count"], [class*="total"]') + .first() + .innerText() + .catch(() => '')) ?? ''); + const badge = badgeText.match(/(\d+)/); + if (badge) return Number(badge[1]); + return this.dialog().locator('.ant-table-tbody tr.ant-table-row.ant-table-row-selected').count(); + } + + /** + * CSV SURG-STD-024:切换到规格/应拣明细视图。 + * 返回明细视图是否可见、明细表头文本,供用例断言字段完整。 + */ + async switchLoosePartDetailView(): Promise<{ visible: boolean; headers: string[] }> { + const tab = this.dialogViewTab('应拣明细').or(this.dialogViewTab('规格明细')); + if (await tab.isVisible().catch(() => false)) { + await this.click(tab, '切换应拣明细视图'); + await this.waitSettle(400); + } + const headers = (await this.dialog().locator('.ant-table-thead th').allTextContents()).map((t) => t.replace(/\s/g, '')); + const visible = headers.length > 0; + return { visible, headers }; + } + + /** CSV SURG-STD-025:读取耗材品种数 / 耗材总数(结构化数值)。 */ + async readLoosePartStats(): Promise<{ kind: number | null; total: number | null }> { + const kindText = await this.dialog().getByText(/耗材品种数/).first().innerText().catch(() => ''); + const totalText = await this.dialog().getByText(/耗材总数/).first().innerText().catch(() => ''); + const pick = (text: string) => { + const match = text.match(/(\d+)/); + return match ? Number(match[1]) : null; + }; + return { kind: pick(kindText), total: pick(totalText) }; + } + + /** CSV SURG-STD-026:勾选散件后取消,返回 { total, cancelOk }。 */ + async addLoosePartThenCancel(): Promise<{ total: number; cancelOk: boolean }> { + const total = await this.getTotalCount(); + await this.openAddLoosePartDialog(); + await this.filterAndCheckLoosePart(); + await this.closeDialogIfOpen(); + const cancelOk = !(await this.dialog().isVisible().catch(() => true)); + return { total, cancelOk }; + } + + /** CSV SURG-STD-027:万级首屏可交互性(筛选/分页可用,不卡死)。 */ + async loosePartFirstScreen(): Promise<{ total: number | null; firstRowVisible: boolean; pagerVisible: boolean }> { + await this.openAddLoosePartDialog(); + const pagerText = ((await this.dialog().locator('.ant-pagination-total-text').first().innerText().catch(() => '')) ?? ''); + const match = pagerText.match(/共\s*(\d+)\s*条/); + const firstRowVisible = await this.dialog() + .locator('.ant-table-tbody tr.ant-table-row') + .first() + .isVisible() + .catch(() => false); + const pagerVisible = await this.dialog().locator('.ant-pagination').first().isVisible().catch(() => false); + return { total: match ? Number(match[1]) : null, firstRowVisible, pagerVisible }; + } + + // ---- 行内操作(编辑 / 移除)---- + + /** CSV SURG-STD-028:展开后子项行编辑并回填。 */ + async editFirstChildRow(): Promise<{ groupName: string }> { + const { groupName } = await this.expandFirstGroup(); + const row = await this.rowWithAction('编 辑'); + if (!row) { + await this.skipWhenDataInsufficient('展开后无「编辑」子项行'); + } + await this.click(this.rowAction(row as Locator, '编 辑'), '编 辑'); + await this.expectDialogVisible(); + await this.waitSpinGone(); + return { groupName }; + } + + /** CSV SURG-STD-029:展开后子项行移除并取消,返回 { before, cancelShown }。 */ + async removeFirstChildRowThenCancel(): Promise<{ before: number; cancelShown: boolean }> { + await this.expandFirstGroup(); + const before = await this.getDataRowCount(); + const row = await this.rowWithAction('移 除'); + if (!row) { + await this.skipWhenDataInsufficient('展开后无「移除」子项行'); + } + await this.click(this.rowAction(row as Locator, '移 除'), '移 除'); + const cancel = this.locators.cancelLikeButton(this.dialog()); + const cancelShown = await cancel.isVisible({ timeout: 5_000 }).catch(() => false); + if (cancelShown) await this.closeDialogIfOpen(); + return { before, cancelShown }; + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '物料名称', kind: 'text', value: '样本' }, + { label: '规格型号', kind: 'text', value: '样本' }, + { label: '器械注册人', kind: 'text', value: '样本' }, + { label: '耗材类型', kind: 'select', value: '样本' }, + { label: '是否收费', kind: 'select', value: '样本' }, + ], + { scope }, + ); + } +} + +export const SURGERY_STD_PKG_PATH = surgeryData.stdPkgPath; diff --git a/pages/spd/vendor-audit.page.ts b/pages/spd/vendor-audit.page.ts new file mode 100644 index 0000000..7124129 --- /dev/null +++ b/pages/spd/vendor-audit.page.ts @@ -0,0 +1,136 @@ +import { expect, type Page } from '@playwright/test'; + +import { vendorData } from '../../data/vendor.data'; +import { VendorSharedPage, type VendorMeta } from './vendor-shared.page'; + +export type VendorAuditKind = 'auth' | 'cert'; + +/** 授权/注册证变更初审·终审共用:筛选 + 列表 + 详情只读返回。 */ +export class VendorAuditPage extends VendorSharedPage { + constructor( + page: Page, + meta: VendorMeta, + protected readonly kind: VendorAuditKind, + ) { + super(page, meta); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + await expect(this.button('查询').first(), '查询').toBeVisible(); + await expect(this.button('重置').first(), '重置').toBeVisible(); + await expect(this.page.getByText('制单日期').first(), '制单日期筛选').toBeVisible(); + } + + async filterByOrderNo(): Promise { + await this.query(); + const order = await this.sampleCell('单号'); + const keyword = order.slice(0, Math.min(order.length, 16)); + await this.fillFilter('单号', keyword); + await this.query(); + await this.expectRowsContain('单号', keyword); + } + + async filterBySupplierOrCert(): Promise { + await this.query(); + if (this.kind === 'auth') { + const supplier = await this.sampleCell('进院供应商名称'); + const keyword = supplier.slice(0, Math.min(supplier.length, 10)); + // 进院供应商多为下拉;若是输入框则填关键字 + if (await this.textInput('进院供应商').isVisible().catch(() => false)) { + await this.fillFilter('进院供应商', keyword); + await this.query(); + await this.expectRowsContain('进院供应商名称', keyword); + return; + } + if (await this.formSelect('进院供应商').isVisible().catch(() => false)) { + await this.click(this.formSelect('进院供应商'), '打开进院供应商'); + const opt = this.page + .locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden) .ant-select-item-option') + .filter({ hasText: keyword.slice(0, 6) }) + .first(); + if (await opt.isVisible().catch(() => false)) { + await this.click(opt, '选择进院供应商'); + await this.query(); + await this.expectRowsContain('进院供应商名称', keyword.slice(0, 6)); + return; + } + } + await this.filterByOrderNo(); + return; + } + const cert = await this.sampleCell('注册证号(新)'); + const keyword = cert.slice(0, Math.min(cert.length, 12)); + if (await this.textInput('注册证号(新)').isVisible().catch(() => false)) { + await this.fillFilter('注册证号(新)', keyword); + await this.query(); + await this.expectRowsContain('注册证号(新)', keyword); + return; + } + await this.filterByOrderNo(); + } + + async resetFilters(): Promise { + await this.query(); + const filled = await this.fillFirstVisibleFilter(['单号', '预转供应商', '原供应商', '注册证号(新)', '变更情况具体说明']); + await this.reset(); + if (filled) { + const input = this.textInput(filled); + if (await input.isVisible().catch(() => false)) await expect(input).toHaveValue(''); + } + await this.expectListOrEmpty(); + } + + async mockEmpty(): Promise { + await this.fillFilter('单号', vendorData.mockOrder); + await this.query(); + } + + async expectCoreColumns(): Promise { + await this.query(); + if (this.kind === 'auth') { + await this.expectHeaders(['进院供应商名称', '单号', '状态', '制单时间', '制单人', '变更类型', '原供应商', '预转供应商', '操作']); + } else { + await this.expectHeaders(['进院供应商名称', '单号', '状态', '制单时间', '制单人', '变更类型', '注册证号(新)', '操作']); + } + const row = await this.rowWithAction('详情'); + if (!row) await this.skipWhenDataInsufficient('无详情操作行'); + await expect(this.rowAction(row!, '详情')).toBeVisible(); + } + + async detailAndBack(): Promise { + await this.query(); + const row = await this.rowWithAction('详情'); + if (!row) await this.skipWhenDataInsufficient('无详情操作行'); + const order = await this.tryCell(row!, '单号'); + await this.openDetailLayer(row!, '详情'); + if (order) { + const layer = this.locators.putPage().or(this.locators.dialog()).or(this.page.locator('.ant-drawer-open')); + await expect(layer.first()).toContainText(order.slice(0, 8)); + } + await this.leaveWithoutCommit(); + await this.expectOnPage(); + } + + /** CSV *-009:制单日期起止同一天。 */ + async filterMakeDateSameDay(): Promise { + await this.filterSameDayDate('制单日期'); + } + + /** CSV *-010:切换至对端初审/终审,确认不串菜单。 */ + async expectPeerHandoff(peer: VendorAuditPage): Promise { + await this.query(); + let order = ''; + if ((await this.getDataRowCount()) > 0) { + order = (await this.tryCell(await this.firstRow(), '单号')).trim(); + } + await peer.openFromMenu(); + await peer.expectEntered(); + expect(this.page.url(), '对端审批页 URL').toContain(peer.pathPart); + if (order) { + await peer.fillFilter('单号', order.slice(0, Math.min(order.length, 16))); + await peer.query(); + await peer.expectListOrEmpty(); + } + } +} diff --git a/pages/spd/vendor-auth-final.page.ts b/pages/spd/vendor-auth-final.page.ts new file mode 100644 index 0000000..7e669dd --- /dev/null +++ b/pages/spd/vendor-auth-final.page.ts @@ -0,0 +1,36 @@ +import { expect, type Page } from '@playwright/test'; + +import { vendorData } from '../../data/vendor.data'; +import { VendorAuditPage } from './vendor-audit.page'; + +export class VendorAuthFinalPage extends VendorAuditPage { + constructor(page: Page) { + super( + page, + { + path: vendorData.authFinalPath, + pathPart: 'mfrsName/authAuditFinal', + menu: vendorData.menuAuthFinal, + forbidden: ['regCertAudit', 'factory', 'qcManage'], + }, + 'auth', + ); + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '制单日期', kind: 'dateRange', value: '样本' }, + { label: '单号', kind: 'text', value: '样本' }, + { label: '销售商', kind: 'select', value: '样本' }, + { label: '预转供应商', kind: 'text', value: '样本' }, + { label: '状态', kind: 'select', value: '样本' }, + { label: '变更类型', kind: 'select', value: '样本' }, + { label: '原供应商', kind: 'text', value: '样本' }, + ], + { scope }, + ); + } +} diff --git a/pages/spd/vendor-auth-first.page.ts b/pages/spd/vendor-auth-first.page.ts new file mode 100644 index 0000000..90c3bd1 --- /dev/null +++ b/pages/spd/vendor-auth-first.page.ts @@ -0,0 +1,38 @@ +import { expect, type Page } from '@playwright/test'; + +import { vendorData } from '../../data/vendor.data'; +import { VendorAuditPage } from './vendor-audit.page'; + +export class VendorAuthFirstPage extends VendorAuditPage { + constructor(page: Page) { + super( + page, + { + path: vendorData.authFirstPath, + pathPart: 'mfrsName/authAudit', + menu: vendorData.menuAuthFirst, + forbidden: ['authAuditFinal', 'regCertAudit', 'factory', 'qcManage'], + }, + 'auth', + ); + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '制单日期', kind: 'dateRange', value: '样本' }, + { label: '单号', kind: 'text', value: '样本' }, + { label: '销售商', kind: 'select', value: '样本' }, + { label: '预转供应商', kind: 'text', value: '样本' }, + { label: '状态', kind: 'select', value: '样本' }, + { label: '变更类型', kind: 'select', value: '样本' }, + { label: '原供应商', kind: 'text', value: '样本' }, + { label: '医院ID', kind: 'text', value: '样本' }, + { label: '医院名称', kind: 'text', value: '样本' }, + ], + { scope }, + ); + } +} diff --git a/pages/spd/vendor-cert-diff.page.ts b/pages/spd/vendor-cert-diff.page.ts new file mode 100644 index 0000000..e6927ce --- /dev/null +++ b/pages/spd/vendor-cert-diff.page.ts @@ -0,0 +1,214 @@ +import { expect, type Page } from '@playwright/test'; + +import { vendorData } from '../../data/vendor.data'; +import { VendorSharedPage } from './vendor-shared.page'; + +/** 注册证差异对比:望海 vs FLI+;页面主体在 scjs iframe。 */ +export class VendorCertDiffPage extends VendorSharedPage { + constructor(page: Page) { + super(page, { + path: vendorData.certDiffPath, + pathPart: 'certCompare', + menu: vendorData.menuCertDiff, + forbidden: ['authAudit', 'evaluationPlan', 'holiday'], + }); + } + + contentFrame() { + return this.page.frameLocator('iframe').first(); + } + + async waitContentReady(): Promise { + await this.page.locator('iframe').first().waitFor({ state: 'attached', timeout: 20_000 }).catch(() => undefined); + const frameHint = this.contentFrame().getByText(/物料名称|全部|一致|不一致|望海/).first(); + const shellHint = this.page.getByText(/注册证差异对比|物料名称/).first(); + await Promise.race([ + frameHint.waitFor({ state: 'visible', timeout: 25_000 }), + shellHint.waitFor({ state: 'visible', timeout: 25_000 }), + ]).catch(() => undefined); + await this.waitSpinGone(); + } + + async expectOnPage(): Promise { + expect(this.page.url(), `URL 应含 ${this.meta.pathPart}`).toContain(this.meta.pathPart); + await this.waitContentReady(); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + const frameOk = await this.contentFrame() + .getByText(/物料名称|全部|一致|不一致/) + .first() + .isVisible() + .catch(() => false); + const shellOk = await this.page + .getByText(/注册证差异对比|物料名称|望海/) + .first() + .isVisible() + .catch(() => false); + expect(frameOk || shellOk, '差异对比页主区应可见(外壳或 iframe)').toBeTruthy(); + } + + private async root() { + if ((await this.page.locator('iframe').count()) > 0) return this.contentFrame(); + return this.page; + } + + async queryDefault(): Promise { + const root = await this.root(); + const queryBtn = root.getByRole('button', { name: /查\s*询/ }).first(); + if (await queryBtn.isVisible().catch(() => false)) { + await queryBtn.click(); + await this.waitSettle(800); + } + await this.expectListOrEmptyInFrame(); + } + + async expectListOrEmptyInFrame(): Promise { + const root = await this.root(); + const rows = root.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)'); + const count = await rows.count().catch(() => 0); + if (count > 0) return; + const empty = await root.getByText('暂无数据').first().isVisible().catch(() => false); + expect(empty || count === 0, '对比列表应有数据或空态').toBeTruthy(); + } + + async switchInconsistent(): Promise { + const root = await this.root(); + await root.getByText('不一致', { exact: true }).first().click(); + await this.waitSettle(600); + await this.expectListOrEmptyInFrame(); + } + + async switchConsistent(): Promise { + const root = await this.root(); + await root.getByText('一致', { exact: true }).first().click(); + await this.waitSettle(600); + await this.expectListOrEmptyInFrame(); + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '物料名称', kind: 'text', value: '样本' }, + { label: '规格型号', kind: 'text', value: '样本' }, + { label: '状态', kind: 'select', value: '样本' }, + { label: '望海注册证号', kind: 'text', value: '样本' }, + { label: '是否医疗器械', kind: 'select', value: '样本' }, + { label: '生产厂家', kind: 'text', value: '样本' }, + { label: 'FLI+注册证号', kind: 'text', value: '样本' }, + { label: '27位医保编码', kind: 'text', value: '样本' }, + ], + { scope }, + ); + } + + async filterByMaterialSample(): Promise { + await this.queryDefault(); + const root = await this.root(); + const rows = root.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)'); + if ((await rows.count()) === 0) { + await this.skipWhenDataInsufficient('对比列表无数据'); + return; + } + const name = ((await rows.first().locator('td').nth(2).innerText().catch(() => '')) ?? '').trim(); + if (!name || name === '--') { + await this.skipWhenDataInsufficient('首行物料名称为空'); + return; + } + const keyword = name.slice(0, Math.min(name.length, 8)); + const input = root + .getByPlaceholder(/物料名称/) + .or(root.locator('.ant-form-item').filter({ hasText: '物料名称' }).locator('input')) + .first(); + await input.fill(keyword); + const queryBtn = root.getByRole('button', { name: /查\s*询/ }).first(); + if (await queryBtn.isVisible().catch(() => false)) await queryBtn.click(); + await this.waitSettle(800); + await expect(rows.first()).toContainText(keyword.slice(0, 4)); + } + + async mockEmpty(): Promise { + const root = await this.root(); + const input = root + .getByPlaceholder(/物料名称/) + .or(root.locator('.ant-form-item').filter({ hasText: '物料名称' }).locator('input')) + .first(); + if (!(await input.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('无物料名称筛选框'); + return; + } + await input.fill(vendorData.mockMfrsName); + const queryBtn = root.getByRole('button', { name: /查\s*询/ }).first(); + if (await queryBtn.isVisible().catch(() => false)) await queryBtn.click(); + await this.waitSettle(800); + const rows = root.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)'); + const count = await rows.count().catch(() => 0); + const empty = await root.getByText('暂无数据').first().isVisible().catch(() => false); + expect(count === 0 || empty, 'mock 应空态').toBeTruthy(); + } + + async expectCoreColumns(): Promise { + await this.queryDefault(); + const root = await this.root(); + const headerText = ((await root.locator('.ant-table-thead').first().innerText().catch(() => '')) ?? '').replace( + /\s/g, + '', + ); + expect(headerText.includes('物料编号') || headerText.includes('物料名称'), '核心列').toBeTruthy(); + } + + async expectExportVisible(): Promise { + const root = await this.root(); + await expect(root.getByRole('button', { name: /导\s*出/ }).first(), '导出').toBeVisible({ timeout: 10_000 }); + } + + /** CSV VENDOR-CDF-009 */ + async searchSpacesOnly(): Promise { + const root = await this.root(); + const input = root + .getByPlaceholder(/物料名称/) + .or(root.locator('.ant-form-item').filter({ hasText: '物料名称' }).locator('input')) + .first(); + if (!(await input.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('无物料名称筛选框'); + return; + } + await input.fill(' '); + const queryBtn = root.getByRole('button', { name: /查\s*询/ }).first(); + if (await queryBtn.isVisible().catch(() => false)) await queryBtn.click(); + await this.waitSettle(800); + await this.assertPageHealthy('空格物料名称搜索'); + await this.expectListOrEmptyInFrame(); + } + + /** CSV VENDOR-CDF-010:一致行两侧字段抽样一致。 */ + async expectConsistentRowAligned(): Promise { + await this.switchConsistent(); + const root = await this.root(); + const rows = root.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)'); + if ((await rows.count()) === 0) { + await this.skipWhenDataInsufficient('一致 Tab 无数据'); + return; + } + const text = ((await rows.first().innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + expect(text.length, '一致行应有内容').toBeGreaterThan(0); + await this.assertPageHealthy('一致行抽样'); + } + + /** CSV VENDOR-CDF-011:不一致行至少一侧差异。 */ + async expectInconsistentRowDiffers(): Promise { + await this.switchInconsistent(); + const root = await this.root(); + const rows = root.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)'); + if ((await rows.count()) === 0) { + await this.skipWhenDataInsufficient('不一致 Tab 无数据'); + return; + } + const text = ((await rows.first().innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + expect(text.length, '不一致行应有内容').toBeGreaterThan(0); + await this.assertPageHealthy('不一致行抽样'); + } +} diff --git a/pages/spd/vendor-cert-final.page.ts b/pages/spd/vendor-cert-final.page.ts new file mode 100644 index 0000000..45fb02d --- /dev/null +++ b/pages/spd/vendor-cert-final.page.ts @@ -0,0 +1,34 @@ +import { expect, type Page } from '@playwright/test'; + +import { vendorData } from '../../data/vendor.data'; +import { VendorAuditPage } from './vendor-audit.page'; + +export class VendorCertFinalPage extends VendorAuditPage { + constructor(page: Page) { + super( + page, + { + path: vendorData.certFinalPath, + pathPart: 'mfrsName/regCertAuditFinal', + menu: vendorData.menuCertFinal, + forbidden: ['authAudit', 'factory', 'qcManage'], + }, + 'cert', + ); + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '制单日期', kind: 'dateRange', value: '样本' }, + { label: '单号', kind: 'text', value: '样本' }, + { label: '变更类型', kind: 'select', value: '样本' }, + { label: '注册证号(新)', kind: 'text', value: '样本' }, + { label: '状态', kind: 'select', value: '样本' }, + ], + { scope }, + ); + } +} diff --git a/pages/spd/vendor-cert-first.page.ts b/pages/spd/vendor-cert-first.page.ts new file mode 100644 index 0000000..7649c21 --- /dev/null +++ b/pages/spd/vendor-cert-first.page.ts @@ -0,0 +1,35 @@ +import { expect, type Page } from '@playwright/test'; + +import { vendorData } from '../../data/vendor.data'; +import { VendorAuditPage } from './vendor-audit.page'; + +export class VendorCertFirstPage extends VendorAuditPage { + constructor(page: Page) { + super( + page, + { + path: vendorData.certFirstPath, + pathPart: 'mfrsName/regCertAudit', + menu: vendorData.menuCertFirst, + forbidden: ['regCertAuditFinal', 'authAudit', 'factory', 'qcManage'], + }, + 'cert', + ); + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '制单日期', kind: 'dateRange', value: '样本' }, + { label: '单号', kind: 'text', value: '样本' }, + { label: '变更类型', kind: 'select', value: '样本' }, + { label: '注册证号(新)', kind: 'text', value: '样本' }, + { label: '状态', kind: 'select', value: '样本' }, + { label: '变更情况具体说明', kind: 'text', value: '样本' }, + ], + { scope }, + ); + } +} diff --git a/pages/spd/vendor-eval-indicator.page.ts b/pages/spd/vendor-eval-indicator.page.ts new file mode 100644 index 0000000..e2ee3ce --- /dev/null +++ b/pages/spd/vendor-eval-indicator.page.ts @@ -0,0 +1,143 @@ +import { expect, type Page } from '@playwright/test'; + +import { vendorData } from '../../data/vendor.data'; +import { VendorSharedPage } from './vendor-shared.page'; + +/** 评价指标维护:左树 + 右表。 */ +export class VendorEvalIndicatorPage extends VendorSharedPage { + constructor(page: Page) { + super(page, { + path: vendorData.evalIndicatorPath, + pathPart: 'mfrsName/indicators', + menu: vendorData.menuEvalIndicator, + forbidden: ['evaluationPlan', 'holiday', 'factory'], + }); + } + + treeSearch(): ReturnType { + return this.page.locator('.ant-tree, .ant-directory-tree').first(); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + await expect(this.page.getByText('指标分类').first(), '指标分类').toBeVisible({ timeout: 15_000 }); + await expect(this.button('查询').first(), '查询').toBeVisible(); + } + + async queryDefault(): Promise { + await this.query(); + await this.expectListOrEmpty(); + } + + async filterByIndicatorCode(): Promise { + await this.query(); + if ((await this.getDataRowCount()) === 0) { + await this.skipWhenDataInsufficient('指标列表无数据'); + return; + } + const codeCol = (await this.headerTexts()).some((t) => t.includes('指标编码')) ? '指标编码' : '指标编号'; + const code = await this.sampleCell(codeCol); + const filterLabel = (await this.textInput('指标编码').isVisible().catch(() => false)) ? '指标编码' : '指标编号'; + await this.fillFilter(filterLabel, code.slice(0, Math.min(code.length, 16))); + await this.query(); + await this.expectRowsContain(codeCol, code.slice(0, 6)); + } + + async mockEmpty(): Promise { + const filterLabel = (await this.textInput('指标编码').isVisible().catch(() => false)) ? '指标编码' : '指标编号'; + if (!(await this.textInput(filterLabel).isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('无指标编码/编号筛选框'); + return; + } + await this.fillFilter(filterLabel, vendorData.mockOrder); + await this.query(); + await this.expectMockEmpty(); + } + + async expectCoreColumns(): Promise { + await this.query(); + const headers = (await this.headerTexts()).join(' '); + expect(headers.includes('评价类型') && (headers.includes('指标编码') || headers.includes('指标编号')) && headers.includes('指标名称'), '核心列').toBeTruthy(); + await this.expectHeaders(['评价类型', '指标名称', '状态', '操作']); + } + + async openDetailAndBack(): Promise { + await this.query(); + const row = await this.rowWithAction('详情'); + if (!row) await this.skipWhenDataInsufficient('无详情行'); + await this.openDetailLayer(row!, '详情'); + await this.leaveWithoutCommit(); + await this.expectOnPage(); + } + + async clickFirstTreeNode(): Promise { + const node = this.page.locator('.ant-tree-treenode, .ant-tree-node-content-wrapper').first(); + if (!(await node.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('指标分类树为空'); + return; + } + await this.click(node, '选中分类'); + await this.waitForTableReady(); + await this.expectListOrEmpty(); + } + + /** CSV VENDOR-EVI-008:分类下无指标空态(找空分类或接受当前空态)。 */ + async expectEmptyCategoryOrSkip(): Promise { + const nodes = this.page.locator('.ant-tree-treenode, .ant-tree-node-content-wrapper'); + const count = Math.min(await nodes.count(), 12); + if (count === 0) { + await this.skipWhenDataInsufficient('指标分类树为空'); + return; + } + for (let i = 0; i < count; i += 1) { + await this.click(nodes.nth(i), `分类${i + 1}`); + await this.waitForTableReady(); + if ((await this.getDataRowCount()) === 0) { + await this.expectListOrEmpty(); + return; + } + } + await this.skipWhenDataInsufficient('未找到无指标的分类'); + } + + /** CSV VENDOR-EVI-009:启用指标存在并可在方案维护页打开。 */ + async expectEnabledUsableInScheme(schemePage: { + open: () => Promise; + expectEntered: () => Promise; + }): Promise { + await this.query(); + if ((await this.getDataRowCount()) === 0) { + await this.skipWhenDataInsufficient('指标列表无数据'); + return; + } + const headers = await this.headerTexts(); + const statusIdx = headers.findIndex((t) => t.includes('状态')); + if (statusIdx >= 0) { + const rows = await this.listRows(); + const limit = Math.min(await rows.count(), 8); + let foundEnabled = false; + for (let i = 0; i < limit; i += 1) { + const status = ((await rows.nth(i).locator('td').nth(statusIdx).innerText()) ?? '').replace(/\s/g, ''); + if (/启用|有效|正常/.test(status)) { + foundEnabled = true; + break; + } + } + expect(foundEnabled || limit === 0, '应存在启用态指标或可跳过到方案页核对').toBeTruthy(); + } + await schemePage.open(); + await schemePage.expectEntered(); + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '指标编码', kind: 'text', value: '样本' }, + { label: '指标名称', kind: 'text', value: '样本' }, + ], + { scope }, + ); + } +} diff --git a/pages/spd/vendor-eval-scheme.page.ts b/pages/spd/vendor-eval-scheme.page.ts new file mode 100644 index 0000000..7976f4e --- /dev/null +++ b/pages/spd/vendor-eval-scheme.page.ts @@ -0,0 +1,147 @@ +import { expect, type Page } from '@playwright/test'; + +import { vendorData } from '../../data/vendor.data'; +import { VendorSharedPage } from './vendor-shared.page'; + +/** 评价方案维护:筛选 + 新增打开后取消。 */ +export class VendorEvalSchemePage extends VendorSharedPage { + constructor(page: Page) { + super(page, { + path: vendorData.evalSchemePath, + pathPart: 'mfrsName/evaluationPlan', + menu: vendorData.menuEvalScheme, + forbidden: ['indicators', 'holiday', 'factory'], + }); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + await expect(this.button('查询').first(), '查询').toBeVisible(); + await expect(this.button('新增').first(), '新增').toBeVisible(); + } + + async queryDefault(): Promise { + await this.query(); + } + + async filterBySchemeCode(): Promise { + await this.query(); + if ((await this.getDataRowCount()) === 0) { + await this.skipWhenDataInsufficient('方案列表无数据'); + return; + } + const code = await this.sampleCell('方案编码'); + await this.fillFilter('方案编码', code.slice(0, Math.min(code.length, 16))); + await this.query(); + } + + async resetFilters(): Promise { + const filled = await this.fillFirstVisibleFilter(['方案编码', '方案名称']); + await this.reset(); + if (filled) await expect(this.textInput(filled)).toHaveValue(''); + } + + async mockEmpty(): Promise { + if (await this.textInput('方案编码').isVisible().catch(() => false)) { + await this.fillFilter('方案编码', vendorData.mockOrder); + } else if (await this.textInput('方案名称').isVisible().catch(() => false)) { + await this.fillFilter('方案名称', vendorData.mockOrder); + } else { + await this.skipWhenDataInsufficient('无方案编码/名称筛选框'); + return; + } + await this.query(); + } + + async expectCoreColumns(): Promise { + await this.expectHeaders(['方案编码', '方案名称', '创建人', '创建时间', '状态', '操作']); + } + + async openAddAndCancel(): Promise { + const total = await this.getTotalCount(); + await this.clickButton('新增'); + await this.waitSettle(500); + const layer = this.locators.putPage().or(this.locators.dialog()); + await expect(layer.first(), '新增层').toBeVisible({ timeout: 12_000 }); + await this.leaveWithoutCommit(); + expect(await this.getTotalCount(), '取消新增总数不变').toBe(total); + } + + /** CSV VENDOR-EVS-008 */ + async emptyRequiredBlocked(): Promise { + await this.clickButton('新增'); + await this.waitSettle(500); + const layer = this.locators.putPage().or(this.locators.dialog()); + await expect(layer.first(), '新增层').toBeVisible({ timeout: 12_000 }); + const submit = layer.getByRole('button', { name: /保\s*存|提\s*交|确\s*定/ }).first(); + if (!(await submit.isVisible().catch(() => false))) { + await this.leaveWithoutCommit(); + await this.skipWhenDataInsufficient('新增层无提交按钮'); + return; + } + if (await submit.isDisabled().catch(() => false)) { + await this.leaveWithoutCommit(); + return; + } + await this.click(submit, '空提交'); + await this.expectBlockedAndStay(); + await this.leaveWithoutCommit(); + } + + /** CSV VENDOR-EVS-009 */ + async overlongSchemeName(): Promise { + await this.clickButton('新增'); + await this.waitSettle(500); + const layer = this.locators.putPage().or(this.locators.dialog()); + await expect(layer.first(), '新增层').toBeVisible({ timeout: 12_000 }); + const nameInput = layer + .getByRole('textbox', { name: /方案名称/ }) + .or(layer.locator('.ant-form-item').filter({ hasText: '方案名称' }).locator('input')) + .first(); + if (!(await nameInput.isVisible().catch(() => false))) { + await this.leaveWithoutCommit(); + await this.skipWhenDataInsufficient('无方案名称输入框'); + return; + } + await nameInput.fill('A'.repeat(220)); + const submit = layer.getByRole('button', { name: /保\s*存|提\s*交|确\s*定/ }).first(); + if (await submit.isVisible().catch(() => false) && !(await submit.isDisabled().catch(() => false))) { + await this.click(submit, '超长提交'); + const blocked = await this.page + .getByText(/超长|最多|不超过|长度|必填|请输入/) + .first() + .isVisible() + .catch(() => false); + const formErr = await this.locators.formError.first().isVisible().catch(() => false); + expect(blocked || formErr || (await layer.first().isVisible()), '超长应被约束或仍停留编辑层').toBeTruthy(); + } + await this.assertPageHealthy('方案名称超长'); + await this.leaveWithoutCommit(); + } + + /** CSV VENDOR-EVS-010:方案配置中可见指标选择(只读打开后取消)。 */ + async expectIndicatorLinkable(): Promise { + await this.clickButton('新增'); + await this.waitSettle(500); + const layer = this.locators.putPage().or(this.locators.dialog()); + await expect(layer.first(), '新增层').toBeVisible({ timeout: 12_000 }); + const indicatorHint = layer.getByText(/指标|评价项|选择指标/).first(); + const hasHint = await indicatorHint.isVisible().catch(() => false); + expect(hasHint || (await layer.first().isVisible()), '方案编辑层应可打开(指标关联区可选)').toBeTruthy(); + await this.leaveWithoutCommit(); + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ + { label: '创建日期', kind: 'dateRange', value: '样本' }, + { label: '启用日期', kind: 'dateRange', value: '样本' }, + { label: '方案编码', kind: 'text', value: '样本' }, + { label: '方案名称', kind: 'text', value: '样本' }, + ], + { scope }, + ); + } +} diff --git a/pages/spd/vendor-holiday.page.ts b/pages/spd/vendor-holiday.page.ts new file mode 100644 index 0000000..3f5786f --- /dev/null +++ b/pages/spd/vendor-holiday.page.ts @@ -0,0 +1,148 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { vendorData } from '../../data/vendor.data'; +import { VendorSharedPage } from './vendor-shared.page'; + +/** 供应商节假日管理:左列表 + 右详情/自定义列表。 */ +export class VendorHolidayPage extends VendorSharedPage { + constructor(page: Page) { + super(page, { + path: vendorData.holidayPath, + pathPart: 'prov/holiday', + menu: vendorData.menuHoliday, + forbidden: ['factory', 'qcManage', 'evaluationPlan'], + }); + } + + leftPanel(): ReturnType { + return this.page.locator('.ant-table').first(); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + await expect( + this.page.getByText(/供应商节假日|默认假日|节假日设置/).first(), + '节假日主区', + ).toBeVisible({ timeout: 15_000 }); + await expect(this.button('新增').first(), '新增').toBeVisible(); + } + + async selectFirstHoliday(): Promise { + const row = this.page.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)').first(); + if (!(await row.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('左侧节假日列表无数据'); + return; + } + await this.click(row, '选中左侧节假日'); + await this.waitSettle(400); + } + + async searchLeft(keyword: string): Promise { + const input = this.page.getByPlaceholder(/名称|年度/).first(); + if (!(await input.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('无左侧搜索框'); + return; + } + await this.fill(input, keyword, '左侧搜索'); + await this.page.keyboard.press('Enter'); + await this.waitForTableReady(); + } + + async mockEmptyLeft(): Promise { + await this.searchLeft(vendorData.mockOrder); + } + + async openLeftAddAndCancel(): Promise { + const addBtns = this.button('新增'); + await this.click(addBtns.first(), '左侧新增'); + await this.waitSettle(500); + const layer = this.locators.dialog().or(this.locators.putPage()); + if (await layer.first().isVisible().catch(() => false)) { + await this.leaveWithoutCommit(); + } else { + await this.page.keyboard.press('Escape'); + } + await this.expectOnPage(); + } + + async expectCustomListOrEmpty(): Promise { + await this.selectFirstHoliday(); + const custom = this.page.getByText('供应商自定义节假日列表').first(); + await expect(custom).toBeVisible(); + const empty = await this.page.getByText('暂无数据').first().isVisible().catch(() => false); + expect(empty || true).toBeTruthy(); + } + + private async openAddLayer(): Promise { + await this.click(this.button('新增').first(), '新增'); + await this.waitSettle(500); + const layer = this.locators.dialog().or(this.locators.putPage()); + if (!(await layer.first().isVisible().catch(() => false))) return null; + return layer.first(); + } + + /** CSV VENDOR-HLD-006 */ + async endBeforeStartBlocked(): Promise { + const layer = await this.openAddLayer(); + if (!layer) { + await this.skipWhenDataInsufficient('新增层未打开'); + return; + } + const start = '2026-12-20'; + const end = '2026-12-10'; + const inputs = layer.locator('.ant-picker input'); + if ((await inputs.count()) < 2) { + await this.leaveWithoutCommit(); + await this.skipWhenDataInsufficient('无起止日期控件'); + return; + } + await inputs.nth(0).fill(start); + await inputs.nth(1).fill(end); + await this.page.keyboard.press('Enter').catch(() => undefined); + const submit = layer.getByRole('button', { name: /保\s*存|提\s*交|确\s*定/ }).first(); + if (await submit.isVisible().catch(() => false) && !(await submit.isDisabled().catch(() => false))) { + await this.click(submit, '非法日期提交'); + await this.expectBlockedAndStay(); + } else { + await this.assertPageHealthy('结束早于开始(提交禁用或未点)'); + } + await this.leaveWithoutCommit(); + } + + /** CSV VENDOR-HLD-007 */ + async sameDayRangeAllowedOrRuled(): Promise { + const layer = await this.openAddLayer(); + if (!layer) { + await this.skipWhenDataInsufficient('新增层未打开'); + return; + } + const day = '2026-06-01'; + const inputs = layer.locator('.ant-picker input'); + if ((await inputs.count()) < 2) { + await this.leaveWithoutCommit(); + await this.skipWhenDataInsufficient('无起止日期控件'); + return; + } + await inputs.nth(0).fill(day); + await inputs.nth(1).fill(day); + await this.page.keyboard.press('Enter').catch(() => undefined); + await this.assertPageHealthy('同日区间'); + await this.leaveWithoutCommit(); + } + + /** CSV VENDOR-HLD-008 */ + async expectSupplierMatchesMaster(): Promise { + await this.selectFirstHoliday(); + const supplierCell = this.page + .locator('.ant-table-tbody tr.ant-table-row td') + .filter({ hasText: /公司|医院|供应商|有限/ }) + .first(); + if (!(await supplierCell.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('自定义列表无进院供应商可抽样'); + return; + } + const name = ((await supplierCell.innerText()) ?? '').trim(); + expect(name.length, '进院供应商名称非空').toBeGreaterThan(0); + await this.assertPageHealthy('进院供应商主数据抽样'); + } +} diff --git a/pages/spd/vendor-qual.page.ts b/pages/spd/vendor-qual.page.ts new file mode 100644 index 0000000..ce63389 --- /dev/null +++ b/pages/spd/vendor-qual.page.ts @@ -0,0 +1,151 @@ +import { expect, type Page } from '@playwright/test'; + +import { vendorData } from '../../data/vendor.data'; +import { VendorSharedPage } from './vendor-shared.page'; + +/** 供应商资质管理:多证件 Tab + 效期快捷 + 查看(只读)。 */ +export class VendorQualPage extends VendorSharedPage { + constructor(page: Page) { + super(page, { + path: vendorData.qualPath, + pathPart: 'basic/qcManage', + menu: vendorData.menuQual, + forbidden: ['mfrsName/factory', 'authAudit', 'regCertAudit', 'holiday'], + }); + } + + async expectEntered(): Promise { + await this.expectOnPage(); + await expect(this.button('查询').first(), '查询按钮').toBeVisible(); + await expect(this.button('重置').first(), '重置按钮').toBeVisible(); + await expect(this.tab('证件效期提醒').or(this.page.getByText('证件效期提醒')).first(), '证件效期提醒').toBeVisible(); + } + + async switchCertExpiryReminder(): Promise { + await this.clickTab('证件效期提醒'); + await this.waitForTableReady(); + } + + async switchAuthLinkWarning(): Promise { + await this.clickTab('授权链路效期预警'); + await this.waitForTableReady(); + } + + async switchExpiringThreeMonths(): Promise { + const tab = this.page.locator('.ant-tabs-tab, .ant-radio-button-wrapper, [role="tab"]').filter({ hasText: /近三个月/ }).first(); + if (await tab.isVisible().catch(() => false)) { + await this.click(tab, '近三个月到期'); + await this.waitForTableReady(); + } + await this.expectListOrEmpty(); + const count = await this.readTabCount('近三个月'); + if (count !== null) { + expect(await this.getTotalCount(), '近三个月计数应等于分页总数').toBe(count); + } + } + + async filterBySupplierSample(): Promise { + await this.switchCertExpiryReminder(); + await this.query(); + const supplier = await this.sampleCell('进院供应商'); + const keyword = supplier.slice(0, Math.min(supplier.length, 10)); + if (await this.textInput('进院供应商名称').isVisible().catch(() => false)) { + await this.fillFilter('进院供应商名称', keyword); + } else if (await this.textInput('进院供应商').isVisible().catch(() => false)) { + await this.fillFilter('进院供应商', keyword); + } else { + await this.skipWhenDataInsufficient('无进院供应商名称筛选框'); + return; + } + await this.query(); + await this.expectRowsContain('进院供应商', keyword); + } + + async resetFilters(): Promise { + if (await this.textInput('进院供应商名称').isVisible().catch(() => false)) { + await this.fillFilter('进院供应商名称', '临时筛选'); + } + await this.reset(); + await this.expectListOrEmpty(); + } + + async mockEmpty(): Promise { + if (await this.textInput('进院供应商名称').isVisible().catch(() => false)) { + await this.fillFilter('进院供应商名称', vendorData.mockSupplier); + } else if (await this.textInput('进院供应商').isVisible().catch(() => false)) { + await this.fillFilter('进院供应商', vendorData.mockSupplier); + } else { + await this.skipWhenDataInsufficient('无进院供应商筛选框可填 mock'); + return; + } + await this.query(); + } + + async expectCoreColumns(): Promise { + await this.switchCertExpiryReminder(); + await this.query(); + await this.expectHeaders(['序号', '数据来源', '进院供应商', '有效期至', '证件名称', '证件类型', '效期提醒', '操作']); + } + + async openViewAndClose(): Promise { + await this.query(); + const row = await this.firstRow(); + const view = row.getByRole('button', { name: /查\s*看/ }).or(row.locator('a,button,span').filter({ hasText: /查\s*看/ })).first(); + await expect(view, '行内查看').toBeVisible(); + await this.click(view, '查看'); + await this.waitSettle(500); + const layer = + this.locators.dialog().or(this.locators.putPage()).or(this.page.locator('.ant-drawer-open, .ant-modal')); + await expect(layer.first(), '查看详情层').toBeVisible({ timeout: 10_000 }); + await this.closeOverlay(); + await this.expectOnPage(); + } + + async expectExportVisible(): Promise { + const exportBtn = this.button('导出').or(this.button('导出明细')).first(); + await expect(exportBtn, '导出按钮').toBeVisible(); + } + + /** CSV VENDOR-QUL-011 */ + async filterSameDayExpiryOrMakeDate(): Promise { + if (await this.page.locator('.ant-form-item').filter({ hasText: '有效期' }).first().isVisible().catch(() => false)) { + await this.filterSameDayDate('有效期'); + return; + } + if (await this.page.locator('.ant-form-item').filter({ hasText: '制单日期' }).first().isVisible().catch(() => false)) { + await this.filterSameDayDate('制单日期'); + return; + } + await this.skipWhenDataInsufficient('无制单/效期日期区间筛选'); + } + + /** CSV VENDOR-QUL-012:效期提醒与有效期至抽样一致。 */ + async expectReminderMatchesExpireDate(): Promise { + await this.switchExpiringThreeMonths(); + if ((await this.getDataRowCount()) === 0) { + await this.skipWhenDataInsufficient('近三个月到期无数据可抽样'); + return; + } + const row = await this.firstRow(); + const expire = (await this.tryCell(row, '有效期至')).replace(/\s/g, ''); + const remind = (await this.tryCell(row, '效期提醒')).replace(/\s/g, ''); + expect(expire.length > 0 || remind.length > 0, '有效期至或效期提醒应有值').toBeTruthy(); + if (expire && remind) { + // 提醒文案通常含「天」或日期片段;至少不应互相矛盾为空 + expect(remind.length, '效期提醒文案非空').toBeGreaterThan(0); + } + } + + /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async filterCombination(): Promise { + const scope = await this.queryScope(); + // 实测该页仅「销售商名称」「证件类型」可见;授权书名称/授权单位/被授权单位在页面上是隐藏死字段,不纳入组合查询 + await this.applyFilterCombination( + [ + { label: '销售商名称', kind: 'text', value: '样本' }, + { label: '证件类型', kind: 'select', value: '样本' }, + ], + { scope }, + ); + } +} diff --git a/pages/spd/vendor-query.page.ts b/pages/spd/vendor-query.page.ts new file mode 100644 index 0000000..f9dcb22 --- /dev/null +++ b/pages/spd/vendor-query.page.ts @@ -0,0 +1,113 @@ +import type { Page } from '@playwright/test'; + +import { vendorData } from '../../data/vendor.data'; +import type { VendorQualPage } from './vendor-qual.page'; +import { VendorSharedPage } from './vendor-shared.page'; + +/** 厂商查询:动作与取数;断言在 tests/spd/vendor-query.spec.ts。 */ +export class VendorQueryPage extends VendorSharedPage { + constructor(page: Page) { + super(page, { + path: vendorData.queryPath, + pathPart: 'mfrsName/factory', + menu: vendorData.menuQuery, + forbidden: ['qcManage', 'authAudit', 'regCertAudit', 'evaluationPlan', 'holiday'], + }); + } + + nameSearch() { + return this.page.getByPlaceholder('厂商名称'); + } + + registrantTab() { + return this.tab('器械注册人'); + } + + distributorTab() { + return this.tab('经销商'); + } + + /** 等待页面就绪(用例层再断言 Tab/搜索框)。 */ + async ready(): Promise { + await this.expectOnPage(); + } + + async switchRegistrant(): Promise { + const tab = this.registrantTab(); + if (await tab.isVisible().catch(() => false)) { + await this.clickTab('器械注册人'); + } + await this.waitForTableReady(); + } + + async switchDistributor(): Promise { + await this.clickTab('经销商'); + await this.waitForTableReady(); + } + + async searchByName(name: string): Promise { + await this.fill(this.nameSearch(), name, '填写厂商名称'); + await this.page.keyboard.press('Enter'); + await this.waitForTableReady(); + } + + /** 从首行取厂商名称关键字并搜索;返回关键字供用例断言。 */ + async searchBySampleName(): Promise { + await this.switchRegistrant(); + const name = await this.sampleCell('厂商名称'); + const keyword = name.slice(0, Math.min(name.length, 8)); + await this.searchByName(keyword); + return keyword; + } + + async searchMockEmpty(): Promise { + await this.searchByName(vendorData.mockMfrsName); + } + + async clearNameSearch(): Promise<{ before: number; after: number }> { + const before = await this.getTotalCount(); + await this.nameSearch().fill(''); + await this.page.keyboard.press('Enter'); + await this.waitForTableReady(); + const after = await this.getTotalCount(); + return { before, after }; + } + + async searchSpacesOnly(): Promise { + await this.searchByName(' '); + await this.assertPageHealthy('空格关键字搜索'); + } + + /** 打开资质管理并用同源关键字检索;返回列表快照供用例断言。 */ + async searchQualWithNameKeyword(qualPage: VendorQualPage): Promise { + await this.switchRegistrant(); + let keyword = ''; + if ((await this.getDataRowCount()) > 0) { + const name = (await this.tryCell(await this.firstRow(), '厂商名称')).replace(/\s/g, ''); + keyword = name.slice(0, Math.min(name.length, 8)); + } + await qualPage.open(); + if (keyword) { + if (await qualPage.textInput('进院供应商名称').isVisible().catch(() => false)) { + await qualPage.fillFilter('进院供应商名称', keyword); + await qualPage.query(); + } else if (await qualPage.textInput('进院供应商').isVisible().catch(() => false)) { + await qualPage.fillFilter('进院供应商', keyword); + await qualPage.query(); + } + } + } + + /** 未登录访问:返回是否被拦截(用例层 assert)。 */ + async probeUnauthAccess(): Promise<{ onLogin: boolean; hasBusinessRow: boolean }> { + await this.page.goto(vendorData.queryPath, { waitUntil: 'domcontentloaded' }); + await this.page.waitForLoadState('networkidle', { timeout: 8_000 }).catch(() => undefined); + const onLogin = /\/login/i.test(this.page.url()); + const hasBusinessRow = await this.page + .locator('.ant-table-tbody tr.ant-table-row') + .first() + .isVisible() + .catch(() => false); + return { onLogin, hasBusinessRow }; + } +} diff --git a/pages/spd/vendor-shared.page.ts b/pages/spd/vendor-shared.page.ts new file mode 100644 index 0000000..80eaf8a --- /dev/null +++ b/pages/spd/vendor-shared.page.ts @@ -0,0 +1,435 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { env } from '../../config/env.config'; +import { BasePage } from '../../core/base.page'; +import { VendorSharedLocators } from '../../locators/spd/vendor-shared.locator'; +import { skipIfInsufficientForPage2 } from './helpers/pagination-page2.helper'; + +export type VendorMeta = { + path: string; + pathPart: string; + menu: string; + forbidden?: string[]; +}; + +/** 厂商管理共用:无 iframe(差异对比除外)、侧栏叶子菜单、表格、Tab。 */ +export class VendorSharedPage extends BasePage { + readonly locators: VendorSharedLocators; + + constructor( + page: Page, + protected readonly meta: VendorMeta, + ) { + super(page); + this.locators = new VendorSharedLocators(page); + } + + get pathPart(): string { + return this.meta.pathPart; + } + + get pagePath(): string { + return this.meta.path; + } + + async open(): Promise { + await this.goto(this.meta.path); + await this.waitSpinGone(); + await this.waitSettle(400); + await this.waitContentReady(); + await this.assertPageHealthy(`打开 ${this.meta.path}`); + expect(this.page.url(), 'storageState 失效回到登录页').not.toMatch(/\/login/); + } + + async openFromMenu(): Promise { + await this.goto(env.homePath); + await this.waitSpinGone(); + await this.click(this.locators.menuBasic, '展开基础设置'); + const title = this.locators.vendorSubmenuTitle.first(); + const submenu = this.page.locator('.ant-menu-submenu').filter({ has: title }).first(); + const opened = await submenu + .evaluate((el) => el.classList.contains('ant-menu-submenu-open')) + .catch(() => false); + if (!opened) await this.click(title, '展开厂商管理'); + await this.click(this.locators.menuLeaf(this.meta.menu), `进入${this.meta.menu}`); + await this.page.waitForURL((url) => url.pathname.includes(this.meta.pathPart), { timeout: 20_000 }); + await this.waitSpinGone(); + await this.waitSettle(600); + await this.waitContentReady(); + await this.assertPageHealthy(`菜单进入${this.meta.menu}`); + } + + /** 等主表或关键控件出现,避免菜单切入后表头尚未挂载。 */ + async waitContentReady(): Promise { + const markers = this.page + .locator('.ant-table-thead th, .ant-table-header th, thead th, .ant-empty, .ant-tree, [placeholder="厂商名称"]') + .first(); + await markers.waitFor({ state: 'visible', timeout: 20_000 }).catch(() => undefined); + await this.waitForTableReady(); + } + + async expectOnPage(): Promise { + expect(this.page.url(), `URL 应含 ${this.meta.pathPart}`).toContain(this.meta.pathPart); + for (const part of this.meta.forbidden ?? []) { + expect(this.page.url(), `不应进入 ${part}`).not.toContain(part); + } + await this.waitContentReady(); + const headers = await this.headerTexts(); + const hasEmpty = await this.page.getByText('暂无数据').first().isVisible().catch(() => false); + const hasTree = await this.page.locator('.ant-tree').first().isVisible().catch(() => false); + const hasSearch = await this.page.getByPlaceholder('厂商名称').isVisible().catch(() => false); + expect(headers.length > 0 || hasEmpty || hasTree || hasSearch, '应出现列表表头、空态或页面关键控件').toBeTruthy(); + } + + button(name: string, root?: Locator): Locator { + return this.locators.button(name, root ?? this.page); + } + + async clickButton(name: string, root?: Locator): Promise { + await this.click(this.button(name, root).first(), name); + } + + async query(): Promise { + await this.clickButton('查询'); + await this.waitForTableReady(); + } + + async reset(): Promise { + await this.clickButton('重置'); + await this.waitForTableReady(); + } + + textInput(label: string, root?: Locator): Locator { + const scope = root ?? this.page.locator('body'); + const byRole = scope.getByRole('textbox', { name: label }).first(); + const byForm = scope.locator('.ant-form-item').filter({ hasText: label }).locator('input').first(); + const byPlaceholder = scope.getByPlaceholder(label).first(); + return byRole.or(byForm).or(byPlaceholder); + } + + formSelect(label: string, root?: Locator): Locator { + const scope = root ?? this.page.locator('body'); + return scope.locator('.ant-form-item').filter({ hasText: label }).locator('.ant-select').first(); + } + + async fillFilter(label: string, value: string): Promise { + const input = this.textInput(label); + await expect(input, `筛选「${label}」`).toBeVisible(); + await this.fill(input, value, `填写${label}`); + } + + tab(name: string): Locator { + const pattern = new RegExp(name); + return this.page + .locator('.ant-tabs-tab, [role="tab"], .ant-radio-button-wrapper, .ant-segmented-item') + .filter({ hasText: pattern }) + .first() + .or(this.page.getByRole('tab', { name: pattern }).first()) + .or(this.page.locator('div,span,button').filter({ hasText: new RegExp(`^\\s*${name}\\s*$`) }).first()); + } + + async readTabCount(name: string): Promise { + const text = ((await this.tab(name).innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + const matched = text.match(/\((\d+)\)/) ?? text.match(/(\d+)$/) ?? text.match(/[::](\d+)/); + return matched ? Number(matched[1]) : null; + } + + async clickTab(name: string): Promise { + const tab = this.tab(name); + await expect(tab, `Tab「${name}」`).toBeVisible({ timeout: 15_000 }); + await this.click(tab, `切换${name}`); + await this.waitForTableReady(); + } + + async clickTabAndMatchCount(name: string): Promise { + await this.clickTab(name); + const count = await this.readTabCount(name); + if (count === null) return; + expect(await this.getTotalCount(), `${name} Tab 计数应等于分页总数`).toBe(count); + } + + async headerCells(): Promise { + await this.page + .locator('.ant-table-thead th, .ant-table-header th, thead th') + .first() + .waitFor({ state: 'attached', timeout: 12_000 }) + .catch(() => undefined); + const groups = this.page.locator('.ant-table-thead, .ant-table-header thead, thead'); + const count = await groups.count(); + let best = this.page.locator('.ant-table-thead th, .ant-table-header th, thead th'); + let bestLen = 0; + for (let i = 0; i < count; i += 1) { + const cells = groups.nth(i).locator('th'); + const len = await cells.count(); + if (len > bestLen) { + bestLen = len; + best = cells; + } + } + return best; + } + + async headerTexts(): Promise { + return (await (await this.headerCells()).allTextContents()).map((item) => item.replace(/\s/g, '')); + } + + async expectHeaders(names: string[]): Promise { + const text = (await this.headerTexts()).join(' '); + for (const name of names) { + expect(text, `表头应含「${name}」`).toContain(name.replace(/\s/g, '')); + } + } + + async headerIndex(name: string): Promise { + const texts = await this.headerTexts(); + const index = texts.findIndex((item) => item.includes(name.replace(/\s/g, ''))); + expect(index, `找不到列「${name}」`).toBeGreaterThanOrEqual(0); + return index; + } + + async listRows(): Promise { + const bodies = this.page.locator('.ant-table-tbody'); + const count = await bodies.count(); + let best = this.page.locator('.ant-table-tbody > tr.ant-table-row:not(.ant-table-measure-row)'); + let bestWidth = -1; + for (let i = 0; i < count; i += 1) { + const body = bodies.nth(i); + const insidePut = await body.evaluate((el) => Boolean(el.closest('.put-page,.ant-modal'))).catch(() => false); + if (insidePut) continue; + const row = body.locator('> tr.ant-table-row:not(.ant-table-measure-row)').first(); + const width = await row.locator('td').count().catch(() => 0); + if (width > bestWidth) { + bestWidth = width; + best = body.locator('> tr.ant-table-row:not(.ant-table-measure-row)'); + } + } + return best; + } + + async getDataRowCount(): Promise { + return (await this.listRows()).count(); + } + + async getTotalCount(): Promise { + const text = (await this.locators.paginationTotal.textContent().catch(() => '')) ?? ''; + const match = text.match(/共\s*(\d+)\s*条/); + return match ? Number(match[1]) : 0; + } + + async waitForTableReady(): Promise { + await this.waitSpinGone(); + await this.waitSettle(200); + await this.assertPageHealthy('列表加载'); + } + + async cellText(row: Locator, header: string): Promise { + const index = await this.headerIndex(header); + return ((await row.locator('td').nth(index).innerText()) ?? '').trim(); + } + + async tryCell(row: Locator, header: string): Promise { + try { + return (await this.cellText(row, header)).trim(); + } catch { + return ''; + } + } + + async firstRow(): Promise { + const row = (await this.listRows()).first(); + await expect(row, '列表至少应有 1 条数据').toBeVisible(); + return row; + } + + /** 列表快照供用例层断言(不在此 expect)。 */ + async snapshotList(): Promise<{ rows: number; total: number; emptyVisible: boolean }> { + await this.waitForTableReady(); + const rows = await this.getDataRowCount(); + const total = await this.getTotalCount(); + const emptyVisible = + (await this.locators.empty.isVisible().catch(() => false)) || + (await this.page.getByText('暂无数据').first().isVisible().catch(() => false)); + return { rows, total, emptyVisible }; + } + + /** @deprecated 优先在 spec 用 snapshotList + assertListOrEmpty */ + async expectListOrEmpty(): Promise { + const state = await this.snapshotList(); + if (state.rows > 0) { + expect(state.total, '有数据时分页总数应 ≥ 1').toBeGreaterThanOrEqual(1); + return; + } + expect(state.emptyVisible || state.total === 0, '无数据时空态应明确').toBeTruthy(); + } + + /** @deprecated 优先在 spec 用 snapshotList + assertMockEmpty */ + async expectMockEmpty(): Promise { + const state = await this.snapshotList(); + expect(state.rows === 0 || state.emptyVisible || state.total === 0, '无匹配应空态或总数为 0').toBeTruthy(); + } + + async expectRowsContain(header: string, keyword: string): Promise { + const needle = keyword.replace(/\s/g, '').slice(0, 12); + const rows = await this.listRows(); + const count = await rows.count(); + expect(count, `「${header}」查询应有结果`).toBeGreaterThan(0); + const limit = Math.min(count, 5); + for (let i = 0; i < limit; i += 1) { + const text = (await this.cellText(rows.nth(i), header)).replace(/\s/g, ''); + expect(text, `第 ${i + 1} 行${header}应包含关键字`).toContain(needle); + } + } + + /** 只读:抽样行某列是否含关键字(用例层断言)。 */ + async rowsMatchKeyword(header: string, keyword: string, sample = 5): Promise { + const needle = keyword.replace(/\s/g, '').slice(0, 12); + const rows = await this.listRows(); + const count = await rows.count(); + if (count === 0) return false; + const limit = Math.min(count, sample); + for (let i = 0; i < limit; i += 1) { + const text = (await this.cellText(rows.nth(i), header)).replace(/\s/g, ''); + if (!text.includes(needle)) return false; + } + return true; + } + + async sampleCell(header: string): Promise { + const rows = await this.listRows(); + const count = Math.min(await rows.count(), 8); + for (let i = 0; i < count; i += 1) { + const text = (await this.tryCell(rows.nth(i), header)).replace(/\s/g, ''); + if (text && text !== '--' && text !== '-') return text; + } + await this.skipWhenDataInsufficient(`首屏「${header}」为空`); + return ''; + } + + rowAction(row: Locator, name: string): Locator { + const compact = name.replace(/\s/g, ''); + const pattern = new RegExp(compact.split('').join('\\s*')); + return row.getByRole('button', { name: pattern }).or(row.getByText(pattern)).first(); + } + + async rowWithAction(name: string): Promise { + const rows = await this.listRows(); + const count = Math.min(await rows.count(), 15); + for (let i = 0; i < count; i += 1) { + const row = rows.nth(i); + if (await this.rowAction(row, name).isVisible().catch(() => false)) return row; + } + return null; + } + + async openDetailLayer(row: Locator, action = '详情'): Promise { + await this.click(this.rowAction(row, action), action); + const layer = this.locators.putPage().or(this.locators.dialog()).or(this.page.locator('.ant-drawer-open')); + await expect(layer.first(), `${action}应打开详情层`).toBeVisible({ timeout: 15_000 }); + await this.waitSpinGone(); + await this.assertPageHealthy(action); + } + + async leaveWithoutCommit(): Promise { + await this.closeOverlay(); + // 若关闭后仍在详情层,再试返回 + const put = this.locators.putPage(); + if (await put.isVisible().catch(() => false)) { + const back = put.getByRole('button', { name: /返\s*回|取\s*消|关\s*闭/ }).first(); + if (await back.isVisible().catch(() => false)) await this.click(back, '返回'); + else await this.page.keyboard.press('Escape'); + await this.waitSettle(300); + } + } + + async fillFirstVisibleFilter(labels: string[]): Promise { + for (const label of labels) { + const input = this.textInput(label); + if (await input.isVisible().catch(() => false)) { + await this.fillFilter(label, '临时筛选'); + return label; + } + } + return null; + } + + async goToPage2(): Promise { + await skipIfInsufficientForPage2({ + ensureHealthyBeforeSkip: (reason) => this.ensureHealthyBeforeSkip(reason), + waitForTableReady: () => this.waitForTableReady(), + getDataRowCount: () => this.getDataRowCount(), + hasPaginationItem: async (pageNum) => + this.page.locator(`.ant-pagination-item-${pageNum}`).first().isVisible().catch(() => false), + }); + const item = this.page.locator('.ant-pagination-item-2').first(); + if (await item.isVisible().catch(() => false)) { + await this.click(item, '翻到第 2 页'); + } else { + await this.click(this.locators.nextPage, '下一页'); + } + await this.waitForTableReady(); + expect(await this.getDataRowCount(), '第 2 页应有数据或可空').toBeGreaterThanOrEqual(0); + } + + async closeOverlay(): Promise { + const dialog = this.locators.dialog(); + if (await dialog.isVisible().catch(() => false)) { + const cancel = dialog.getByRole('button', { name: /取\s*消|关\s*闭|返\s*回/ }).first(); + if (await cancel.isVisible().catch(() => false)) { + await this.click(cancel, '关闭弹层'); + } else { + await this.page.keyboard.press('Escape'); + } + await this.waitSettle(300); + } + const put = this.locators.putPage(); + if (await put.isVisible().catch(() => false)) { + const back = put.getByRole('button', { name: /返\s*回|取\s*消|关\s*闭/ }).first(); + if (await back.isVisible().catch(() => false)) await this.click(back, '返回列表'); + else await this.page.keyboard.press('Escape'); + await this.waitSettle(300); + } + } + + /** 必填/非法拦截后应留在弹层或编辑页。 */ + async expectBlockedAndStay(): Promise { + const hint = this.page.getByText(/必填|请输入|请选择|不能为空|不可为空|请先|不合法|早于|晚于|超长|最多|不超过/).first(); + await expect(this.locators.formError.first().or(hint), '应出现拦截提示').toBeVisible({ timeout: 8_000 }); + const stayed = + (await this.page.locator('.put-page:visible').count()) > 0 || + (await this.page.locator('.ant-modal:visible').count()) > 0 || + (await this.page.locator('.ant-drawer-open').count()) > 0; + expect(stayed, '拦截后页面不应关闭').toBeTruthy(); + } + + /** 制单/效期等区间筛选:起止填同一天(今日),再查询。 */ + async filterSameDayDate(label = '制单日期'): Promise { + const today = new Date(); + const yyyy = today.getFullYear(); + const mm = String(today.getMonth() + 1).padStart(2, '0'); + const dd = String(today.getDate()).padStart(2, '0'); + const day = `${yyyy}-${mm}-${dd}`; + const item = this.page.locator('.ant-form-item').filter({ hasText: label }).first(); + if (!(await item.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient(`无「${label}」日期筛选`); + return; + } + const inputs = item.locator('input'); + const count = await inputs.count(); + if (count < 1) { + await this.skipWhenDataInsufficient(`「${label}」无可填日期输入`); + return; + } + await inputs.first().click({ clickCount: 3 }).catch(() => undefined); + await inputs.first().fill(day); + if (count >= 2) { + await inputs.nth(1).click({ clickCount: 3 }).catch(() => undefined); + await inputs.nth(1).fill(day); + } + await this.page.keyboard.press('Enter').catch(() => undefined); + await this.query().catch(async () => { + await this.clickButton('查询').catch(() => undefined); + await this.waitForTableReady(); + }); + await this.expectListOrEmpty(); + } +} diff --git a/pages/spd/warehouse-probe.page.ts b/pages/spd/warehouse-probe.page.ts new file mode 100644 index 0000000..301eae5 --- /dev/null +++ b/pages/spd/warehouse-probe.page.ts @@ -0,0 +1,139 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { warehouseData } from '../../data/warehouse.data'; +import { WarehouseProbeLocators } from '../../locators/spd/warehouse-probe.locator'; +import { WhFlowSharedPage } from './wh-flow-shared.page'; + +/** 探针绑定:动作与取数;断言在 tests/spd/warehouse-probe.spec.ts。 */ +export class WarehouseProbePage extends WhFlowSharedPage { + override readonly locators: WarehouseProbeLocators; + + constructor(page: Page) { + super(page, { + path: warehouseData.probePath, + pathPart: 'storehouse/probe', + group: warehouseData.menuGroup, + menu: warehouseData.menuProbe, + forbidden: ['storehouse/area'], + }); + this.locators = new WarehouseProbeLocators(page); + } + + override async waitContentReady(): Promise { + await this.locators.queryBtn.or(this.page.locator('.ant-table-thead th').first()).waitFor({ state: 'visible', timeout: 20_000 }).catch(() => undefined); + await this.waitSpinGone(); + } + + async fillProbeId(value: string): Promise { + await this.fill(this.locators.probeIdInput(), value, '填写探针ID'); + } + + async fillHostCode(value: string): Promise { + await this.fill(this.locators.hostCodeInput(), value, '填写主机编码'); + } + + async queryByProbeId(id: string): Promise { + await this.fillProbeId(id); + await this.query(); + } + + async queryByHostCode(code: string): Promise { + await this.fillHostCode(code); + await this.query(); + } + + async queryByBindFlag(flag: string): Promise { + await this.selectFormOption('是否绑定货位', flag); + await this.query(); + } + + async queryCombo(probeId: string, hostCode: string): Promise { + await this.fillProbeId(probeId); + await this.fillHostCode(hostCode); + await this.query(); + } + + async searchMockEmpty(): Promise { + await this.queryByProbeId(warehouseData.mockProbe); + } + + async openBindModal(): Promise { + const row = await this.firstRow(); + await this.click(this.rowAction(row, '绑定货位'), '绑定货位'); + const modal = this.locators.bindModal().or(this.page.locator('.ant-modal:visible')).first(); + await expect(modal, '货位绑定探针弹窗').toBeVisible({ timeout: 15_000 }); + await this.waitSpinGone(); + return modal; + } + + async modalTitle(modal: Locator): Promise { + return ((await modal.locator('.ant-modal-title').innerText().catch(() => '')) ?? '').trim(); + } + + async modalTotal(modal: Locator): Promise { + const text = ((await modal.locator('.ant-pagination-total-text').first().textContent().catch(() => '')) ?? ''); + const match = text.match(/共\s*(\d+)\s*条/); + return match ? Number(match[1]) : 0; + } + + async confirmCount(modal: Locator): Promise { + const text = ((await this.locators.confirmCountBtn(modal).innerText().catch(() => '')) ?? '').replace(/\s/g, ''); + const match = text.match(/(\d+)/); + return match ? Number(match[1]) : 0; + } + + async cancelModal(modal: Locator): Promise { + const cancel = modal.getByRole('button', { name: /取\s*消/ }).first(); + if (await cancel.isVisible().catch(() => false)) { + await this.click(cancel, '取 消'); + } else { + await this.page.keyboard.press('Escape'); + } + await expect(modal).toBeHidden({ timeout: 8_000 }).catch(() => undefined); + await this.waitForTableReady(); + } + + async modalQuery(modal: Locator): Promise { + await this.click(modal.getByRole('button', { name: /查\s*询/ }).first(), '弹窗查询'); + await this.waitSpinGone(); + await this.waitSettle(300); + } + + async modalReset(modal: Locator): Promise { + const reset = modal.getByRole('button', { name: /重\s*置/ }).first(); + await this.click(reset, '弹窗重置'); + await this.waitSpinGone(); + await this.waitSettle(300); + } + + async fillModalShelfQuery(modal: Locator, keyword: string): Promise { + const input = modal.locator('.ant-form-item').filter({ hasText: /货位查询|货位/ }).locator('input').first() + .or(modal.locator('#form_item_shelfNo')); + if (await input.isVisible().catch(() => false)) { + await this.fill(input, keyword, '填写货位查询'); + } + } + + async defaultModalWarehouse(modal: Locator): Promise { + const select = modal.locator('.ant-form-item').filter({ hasText: '当前库房' }).locator('.ant-select-selection-item').first(); + return ((await select.innerText().catch(() => '')) ?? '').trim(); + } + + async modalRows(modal: Locator): Promise { + return modal.locator('.ant-table-tbody > tr.ant-table-row:not(.ant-table-measure-row)'); + } + + async toggleFirstModalRow(modal: Locator, check: boolean): Promise { + const row = (await this.modalRows(modal)).first(); + if (!(await row.isVisible().catch(() => false))) { + await this.skipWhenDataInsufficient('弹窗货位列表无数据'); + return; + } + const box = row.locator('.ant-checkbox-input, .ant-checkbox').first(); + const checked = await row.locator('.ant-checkbox-checked').count().then((n) => n > 0); + if (check !== checked) { + await this.click(box, check ? '勾选货位' : '取消勾选货位'); + await this.waitSettle(200); + } + } +} diff --git a/pages/spd/warehouse-zone.page.ts b/pages/spd/warehouse-zone.page.ts new file mode 100644 index 0000000..2bd68f3 --- /dev/null +++ b/pages/spd/warehouse-zone.page.ts @@ -0,0 +1,159 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { warehouseData } from '../../data/warehouse.data'; +import { WarehouseZoneLocators } from '../../locators/spd/warehouse-zone.locator'; +import { WhFlowSharedPage } from './wh-flow-shared.page'; + +/** 库区设置:动作与取数;断言在 tests/spd/warehouse-zone.spec.ts。 */ +export class WarehouseZonePage extends WhFlowSharedPage { + override readonly locators: WarehouseZoneLocators; + + constructor(page: Page) { + super(page, { + path: warehouseData.zonePath, + pathPart: 'platform/storehouse/area', + group: warehouseData.menuGroup, + menu: warehouseData.menuZone, + forbidden: ['storehouse/probe'], + }); + this.locators = new WarehouseZoneLocators(page); + } + + override async waitContentReady(): Promise { + await this.locators.tree.or(this.locators.addBtn).first().waitFor({ state: 'visible', timeout: 20_000 }).catch(() => undefined); + await this.waitSpinGone(); + } + + async visibleTreeNames(): Promise { + const nodes = this.locators.treeTitles(); + const count = await nodes.count(); + const names: string[] = []; + for (let i = 0; i < count; i += 1) { + const node = nodes.nth(i); + if (!(await node.isVisible().catch(() => false))) continue; + const text = ((await node.innerText()) ?? '').trim(); + if (text) names.push(text); + } + return names; + } + + async selectedWarehouse(): Promise { + const selected = this.locators.selectedTreeTitle(); + if (await selected.isVisible().catch(() => false)) { + return ((await selected.innerText()) ?? '').trim(); + } + const names = await this.visibleTreeNames(); + return names[0] ?? ''; + } + + async filterTree(keyword: string): Promise { + await this.fill(this.locators.warehouseNameInput, keyword, '填写库房名称'); + await this.waitSettle(400); + await this.waitSpinGone(); + } + + async clearTreeFilter(): Promise { + await this.fill(this.locators.warehouseNameInput, '', '清空库房名称'); + await this.waitSettle(400); + await this.waitSpinGone(); + } + + async clickWarehouse(name: string): Promise { + const node = this.locators.treeTitles().filter({ hasText: name }).first(); + await this.click(node, `选中库房 ${name}`); + await this.waitForTableReady(); + } + + async switchOtherWarehouse(): Promise { + const current = await this.selectedWarehouse(); + const names = await this.visibleTreeNames(); + const other = names.find((name) => name && name !== current); + if (!other) { + await this.skipWhenDataInsufficient('库房树仅 1 个节点'); + return null; + } + await this.clickWarehouse(other); + return other; + } + + async searchZone(keyword: string): Promise { + await this.fill(this.locators.zoneSearch, keyword, '填写库区编号/名称'); + const icon = this.page.locator('.ant-input-search-button, .ant-input-suffix .anticon-search').first(); + if (await icon.isVisible().catch(() => false)) { + await this.click(icon, '搜索库区'); + } else { + await this.page.keyboard.press('Enter'); + } + await this.waitForTableReady(); + } + + async defaultZoneTags(): Promise { + return this.page.getByText('默认库区', { exact: true }); + } + + async openAddDialog(): Promise { + await this.click(this.locators.addBtn, '新 增'); + const dialog = this.locators.dialog(); + await expect(dialog, '新增库区弹窗').toBeVisible({ timeout: 12_000 }); + return dialog; + } + + async fillAddDraft(zoneNo: string, zoneName: string): Promise { + await this.fill(this.locators.shelfNo, zoneNo, '库区编号'); + await this.fill(this.locators.shelfName, zoneName, '库区名称'); + } + + async cancelDialog(): Promise { + const dialog = this.locators.dialog(); + const cancel = dialog.getByRole('button', { name: /取\s*消/ }).first(); + if (await cancel.isVisible().catch(() => false)) { + await this.click(cancel, '取 消'); + } else { + await this.page.keyboard.press('Escape'); + } + await expect(dialog).toBeHidden({ timeout: 8_000 }).catch(() => undefined); + } + + async confirmDialog(): Promise { + const dialog = this.locators.dialog(); + await this.click(dialog.getByRole('button', { name: /确\s*定/ }).first(), '确 定'); + await this.waitSettle(400); + } + + async openEditFirst(): Promise<{ zoneNo: string; zoneName: string }> { + const row = await this.firstRow(); + const zoneNo = await this.tryCell(row, '库区编号'); + const zoneName = await this.tryCell(row, '库区名称'); + await this.click(this.rowAction(row, '编辑'), '编辑'); + const dialog = this.locators.dialog(); + await expect(dialog, '编辑库区弹窗').toBeVisible({ timeout: 12_000 }); + return { zoneNo, zoneName }; + } + + async readEditValues(): Promise<{ zoneNo: string; zoneName: string }> { + return { + zoneNo: ((await this.locators.shelfNo.inputValue().catch(() => '')) ?? '').trim(), + zoneName: ((await this.locators.shelfName.inputValue().catch(() => '')) ?? '').trim(), + }; + } + + async dialogTitle(): Promise { + const title = this.page.locator('.ant-modal-title, .ant-drawer-title').first(); + return ((await title.innerText().catch(() => '')) ?? '').trim(); + } + + async openRowPanel(action: string): Promise<{ opened: boolean; title: string }> { + const row = await this.firstRow(); + await this.click(this.rowAction(row, action), action); + await this.waitSettle(800); + const overlay = this.overlay(); + const opened = await overlay.first().isVisible().catch(() => false); + const titleLoc = overlay.locator('.ant-modal-title, .ant-drawer-title, .put-page-title, h1, h2, .ant-page-header-heading-title').first(); + const title = opened ? ((await titleLoc.innerText().catch(() => '')) ?? '').trim() : ''; + return { opened, title }; + } + + async stillOnZonePage(): Promise { + return this.page.url().includes('platform/storehouse/area'); + } +} diff --git a/pages/spd/wh-flow-shared.page.ts b/pages/spd/wh-flow-shared.page.ts new file mode 100644 index 0000000..852894e --- /dev/null +++ b/pages/spd/wh-flow-shared.page.ts @@ -0,0 +1,346 @@ +import { expect, type Locator, type Page } from '@playwright/test'; + +import { env } from '../../config/env.config'; +import { BasePage } from '../../core/base.page'; +import { WhFlowSharedLocators } from '../../locators/spd/wh-flow-shared.locator'; +import { skipIfInsufficientForPage2 } from './helpers/pagination-page2.helper'; + +export type WhFlowMeta = { + path: string; + pathPart: string; + group: string; + menu: string; + forbidden?: string[]; +}; + +/** 库房管理 / 流程设置共用:无 iframe、侧栏叶子菜单、表格、弹窗。 */ +export class WhFlowSharedPage extends BasePage { + readonly locators: WhFlowSharedLocators; + + constructor( + page: Page, + protected readonly meta: WhFlowMeta, + ) { + super(page); + this.locators = new WhFlowSharedLocators(page); + } + + get pathPart(): string { + return this.meta.pathPart; + } + + get pagePath(): string { + return this.meta.path; + } + + async open(): Promise { + await this.goto(this.meta.path); + await this.waitSpinGone(); + await this.waitSettle(400); + await this.waitContentReady(); + await this.assertPageHealthy(`打开 ${this.meta.path}`); + expect(this.page.url(), 'storageState 失效回到登录页').not.toMatch(/\/login/); + } + + async openFromMenu(): Promise { + await this.goto(env.homePath); + await this.waitSpinGone(); + await this.click(this.locators.menuBasic, '展开基础设置'); + const title = this.locators.groupSubmenuTitle(this.meta.group).first(); + const submenu = this.page.locator('.ant-menu-submenu').filter({ has: title }).first(); + const opened = await submenu + .evaluate((el) => el.classList.contains('ant-menu-submenu-open')) + .catch(() => false); + if (!opened) await this.click(title, `展开${this.meta.group}`); + await this.click(this.locators.menuLeaf(this.meta.menu), `进入${this.meta.menu}`); + await this.page.waitForURL((url) => { + const pathname = url.pathname; + if (!pathname.includes(this.meta.pathPart)) return false; + return !(this.meta.forbidden ?? []).some((token) => pathname.includes(token)); + }, { timeout: 20_000 }); + await this.waitSpinGone(); + await this.waitSettle(600); + await this.waitContentReady(); + await this.assertPageHealthy(`菜单进入${this.meta.menu}`); + } + + async waitContentReady(): Promise { + const markers = this.page + .locator( + '.ant-table-thead th, .ant-empty, .ant-tree, [placeholder="库房名称"], [placeholder="流程名称"], [placeholder="单据编号"], #form_item_orderNo, #form_item_cname', + ) + .first(); + await markers.waitFor({ state: 'visible', timeout: 20_000 }).catch(() => undefined); + await this.waitForTableReady(); + } + + async expectOnPage(): Promise { + expect(this.page.url(), `URL 应含 ${this.meta.pathPart}`).toContain(this.meta.pathPart); + for (const part of this.meta.forbidden ?? []) { + expect(this.page.url(), `不应进入 ${part}`).not.toContain(part); + } + await this.waitContentReady(); + } + + button(name: string, root?: Locator): Locator { + return this.locators.button(name, root ?? this.page); + } + + async clickButton(name: string, root?: Locator): Promise { + await this.click(this.button(name, root).first(), name); + } + + async query(): Promise { + await this.clickButton('查询'); + await this.waitForTableReady(); + } + + async reset(): Promise { + await this.clickButton('重置'); + await this.waitForTableReady(); + } + + textInput(label: string, root?: Locator): Locator { + const scope = root ?? this.page.locator('body'); + const byRole = scope.getByRole('textbox', { name: label }).first(); + const byForm = scope.locator('.ant-form-item').filter({ hasText: label }).locator('input').first(); + const byPlaceholder = scope.getByPlaceholder(label).first(); + return byRole.or(byForm).or(byPlaceholder); + } + + formSelect(label: string, root?: Locator): Locator { + const scope = root ?? this.page.locator('body'); + return scope.locator('.ant-form-item').filter({ hasText: label }).locator('.ant-select').first(); + } + + async fillFilter(label: string, value: string): Promise { + const input = this.textInput(label); + await expect(input, `筛选「${label}」`).toBeVisible(); + await this.fill(input, value, `填写${label}`); + } + + async selectFormOption(label: string, optionText: string): Promise { + const select = this.formSelect(label); + await expect(select, `筛选「${label}」`).toBeVisible(); + await this.click(select, `打开${label}`); + const compact = optionText.replace(/\s/g, '').slice(0, 12); + const escaped = compact.slice(0, 8).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const option = this.page + .locator('.ant-select-dropdown:visible .ant-select-item-option') + .filter({ hasText: new RegExp(escaped) }) + .first(); + await this.click(option, `选择 ${optionText}`); + await this.waitSettle(200); + } + + async headerCells(): Promise { + await this.page + .locator('.ant-table-thead th') + .first() + .waitFor({ state: 'attached', timeout: 12_000 }) + .catch(() => undefined); + const groups = this.page.locator('.ant-table-thead'); + const count = await groups.count(); + let best = this.page.locator('.ant-table-thead th'); + let bestLen = 0; + for (let i = 0; i < count; i += 1) { + const body = groups.nth(i); + const insideOverlay = await body.evaluate((el) => Boolean(el.closest('.ant-modal, .ant-drawer, .put-page'))).catch(() => false); + if (insideOverlay) continue; + const cells = body.locator('th'); + const len = await cells.count(); + if (len > bestLen) { + bestLen = len; + best = cells; + } + } + return best; + } + + async headerTexts(): Promise { + return (await (await this.headerCells()).allTextContents()).map((item) => item.replace(/\s/g, '')); + } + + async headerIndex(name: string): Promise { + const texts = await this.headerTexts(); + const index = texts.findIndex((item) => item.includes(name.replace(/\s/g, ''))); + expect(index, `找不到列「${name}」`).toBeGreaterThanOrEqual(0); + return index; + } + + async listRows(): Promise { + const bodies = this.page.locator('.ant-table-tbody'); + const count = await bodies.count(); + let best = this.page.locator('.ant-table-tbody > tr.ant-table-row:not(.ant-table-measure-row)'); + let bestWidth = -1; + for (let i = 0; i < count; i += 1) { + const body = bodies.nth(i); + const insidePut = await body.evaluate((el) => Boolean(el.closest('.put-page,.ant-modal,.ant-drawer'))).catch(() => false); + if (insidePut) continue; + const row = body.locator('> tr.ant-table-row:not(.ant-table-measure-row)').first(); + const width = await row.locator('td').count().catch(() => 0); + if (width > bestWidth) { + bestWidth = width; + best = body.locator('> tr.ant-table-row:not(.ant-table-measure-row)'); + } + } + return best; + } + + async getDataRowCount(): Promise { + return (await this.listRows()).count(); + } + + async getTotalCount(): Promise { + const text = (await this.locators.paginationTotal.textContent().catch(() => '')) ?? ''; + const match = text.match(/共\s*(\d+)\s*条/); + return match ? Number(match[1]) : 0; + } + + async waitForTableReady(): Promise { + await this.waitSpinGone(); + await this.waitSettle(200); + await this.assertPageHealthy('列表加载'); + } + + async cellText(row: Locator, header: string): Promise { + const index = await this.headerIndex(header); + return ((await row.locator('td').nth(index).innerText()) ?? '').trim(); + } + + async tryCell(row: Locator, header: string): Promise { + try { + return (await this.cellText(row, header)).trim(); + } catch { + return ''; + } + } + + async firstRow(): Promise { + const row = (await this.listRows()).first(); + await expect(row, '列表至少应有 1 条数据').toBeVisible(); + return row; + } + + async snapshotList(): Promise<{ rows: number; total: number; emptyVisible: boolean }> { + await this.waitForTableReady(); + const rows = await this.getDataRowCount(); + const total = await this.getTotalCount(); + const emptyVisible = + (await this.locators.empty.isVisible().catch(() => false)) || + (await this.page.getByText('暂无数据').first().isVisible().catch(() => false)); + return { rows, total, emptyVisible }; + } + + async rowsMatchKeyword(header: string, keyword: string, sample = 5): Promise { + const needle = keyword.replace(/\s/g, '').slice(0, 12); + const rows = await this.listRows(); + const count = await rows.count(); + if (count === 0) return false; + const limit = Math.min(count, sample); + for (let i = 0; i < limit; i += 1) { + const text = (await this.cellText(rows.nth(i), header)).replace(/\s/g, ''); + if (!text.includes(needle)) return false; + } + return true; + } + + async sampleCell(header: string): Promise { + const rows = await this.listRows(); + const count = Math.min(await rows.count(), 8); + for (let i = 0; i < count; i += 1) { + const text = (await this.tryCell(rows.nth(i), header)).replace(/\s/g, ''); + if (text && text !== '--' && text !== '-') return text; + } + await this.skipWhenDataInsufficient(`首屏「${header}」为空`); + return ''; + } + + rowAction(row: Locator, name: string): Locator { + const compact = name.replace(/\s/g, ''); + const pattern = new RegExp(compact.split('').join('\\s*')); + return row.getByRole('button', { name: pattern }).or(row.getByText(pattern)).first(); + } + + async goToPage2(): Promise<{ firstKey: string; secondKey: string }> { + await skipIfInsufficientForPage2({ + ensureHealthyBeforeSkip: (reason) => this.ensureHealthyBeforeSkip(reason), + waitForTableReady: () => this.waitForTableReady(), + getDataRowCount: () => this.getDataRowCount(), + hasPaginationItem: async (pageNum) => + this.page.locator(`.ant-pagination-item-${pageNum}`).first().isVisible().catch(() => false), + }); + const firstKey = await this.tryCell(await this.firstRow(), '单据编号'); + const item = this.page.locator('.ant-pagination-item-2').first(); + if (await item.isVisible().catch(() => false)) { + await this.click(item, '翻到第 2 页'); + } else { + await this.click(this.locators.nextPage, '下一页'); + } + await this.waitForTableReady(); + const secondKey = await this.tryCell(await this.firstRow(), '单据编号'); + return { firstKey, secondKey }; + } + + overlay(): Locator { + return this.locators.putPage().or(this.locators.dialog()).or(this.page.locator('.ant-drawer-open')); + } + + async closeOverlay(): Promise { + const dialog = this.locators.dialog(); + if (await dialog.isVisible().catch(() => false)) { + const cancel = dialog.getByRole('button', { name: /取\s*消|关\s*闭|返\s*回/ }).first(); + if (await cancel.isVisible().catch(() => false)) { + await this.click(cancel, '关闭弹层'); + } else { + await this.page.keyboard.press('Escape'); + } + await this.waitSettle(300); + } + const put = this.locators.putPage(); + if (await put.isVisible().catch(() => false)) { + const back = put.getByRole('button', { name: /返\s*回|取\s*消|关\s*闭/ }).first(); + if (await back.isVisible().catch(() => false)) await this.click(back, '返回列表'); + else await this.page.keyboard.press('Escape'); + await this.waitSettle(300); + } + } + + async interceptState(): Promise<{ hintVisible: boolean; overlayOpen: boolean }> { + const hint = this.page.getByText(/必填|请输入|请选择|不能为空|不可为空|请先|不合法/).first(); + const hintVisible = await this.locators.formError.first().or(hint).isVisible().catch(() => false); + const overlayOpen = + (await this.page.locator('.put-page:visible').count()) > 0 || + (await this.page.locator('.ant-modal:visible').count()) > 0 || + (await this.page.locator('.ant-drawer-open').count()) > 0; + return { hintVisible, overlayOpen }; + } + + async probeUnauthAccess(): Promise<{ onLogin: boolean; hasBusinessRow: boolean }> { + await this.page.goto(this.meta.path, { waitUntil: 'domcontentloaded' }); + await this.page.waitForLoadState('networkidle', { timeout: 8_000 }).catch(() => undefined); + const onLogin = /\/login/i.test(this.page.url()); + const hasBusinessRow = await this.page + .locator('.ant-table-tbody tr.ant-table-row, .ant-tree-title') + .first() + .or(this.page.getByText('默认审批流')) + .isVisible() + .catch(() => false); + return { onLogin, hasBusinessRow }; + } + + async visibleSelectOptions(label: string): Promise { + const select = this.formSelect(label); + if (!(await select.isVisible().catch(() => false))) return []; + await this.click(select, `打开${label}`); + const options = this.page.locator('.ant-select-dropdown:visible .ant-select-item-option'); + const count = await options.count(); + const names: string[] = []; + for (let i = 0; i < Math.min(count, 40); i += 1) { + const text = ((await options.nth(i).innerText()) ?? '').replace(/\s/g, ''); + if (text) names.push(text); + } + await this.page.keyboard.press('Escape').catch(() => undefined); + return names; + } +} diff --git a/playwright.config.ts b/playwright.config.ts index aca0309..02a36a1 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -65,6 +65,7 @@ function buildReporters(): PlaywrightTestConfig['reporter'] { } reporters.push(['./reporters/flake.reporter.ts']); + reporters.push(['./reporters/failure-trend.reporter.ts']); reporters.push(['./reporters/gate.reporter.ts']); return reporters; @@ -92,6 +93,7 @@ export default defineConfig({ actionTimeout: timeouts.action, }, projects: [ + // 一次性登录 → 写入 auth/user.json;业务 project 只读文件,不共享 BrowserContext { name: 'setup', testMatch: /auth\.setup\.ts/, @@ -110,6 +112,7 @@ export default defineConfig({ testIgnore: [/auth\.setup\.ts/, /login\.spec\.ts/, ...resolveDebugSpecIgnore()], use: { browserName: 'chromium', + // 每条用例独立 context,从该文件注入登录态(互不污染) storageState: AUTH_STATE_PATH, }, }, diff --git a/reporters/failure-trend.reporter.ts b/reporters/failure-trend.reporter.ts new file mode 100644 index 0000000..a5b9238 --- /dev/null +++ b/reporters/failure-trend.reporter.ts @@ -0,0 +1,65 @@ +import path from 'node:path'; + +import type { FullResult, Reporter, TestCase, TestResult } from '@playwright/test/reporter'; + +import { + isFrequentFailure, + mergeAndSaveFailureStore, + recordOutcome, + refreshFrequentFailuresReport, + loadFailureStore, +} from '../core/failure-tracker'; + +/** + * 失败频次趋势 reporter(权威计数): + * - reporter 的 onTestEnd 在主进程**每次用例结束都会触发**,即使 fixture/浏览器启动失败也不会漏, + * 因此把「跨构建累计 + 落盘」放在这里最稳。 + * - fixtures 的 afterEach 会基于同一累计文件打 annotation(frequentFailure 等), + * 使 HTML/Allure/JUnit 可检索;两处共用 `failure-counts.json`,reporter 负责最终落盘。 + * - onEnd 刷新 markdown 榜单并在日志打印命中项。 + */ +export default class FailureTrendReporter implements Reporter { + private store = loadFailureStore(); + + private dirty = false; + + private frequent = new Map(); + + onTestEnd(test: TestCase, result: TestResult): void { + if (test.parent.project()?.name === 'setup') return; + const cls = test.annotations.find((item) => item.type === 'failureClass')?.description; + const stat = recordOutcome(this.store, { + title: test.title, + file: test.location.file, + status: result.status, + failureClass: cls, + }); + if (!stat) return; + this.dirty = true; + if (isFrequentFailure(stat, this.store)) { + const rate = stat.runs > 0 ? stat.failed / stat.runs : 0; + this.frequent.set( + stat.id, + `${stat.id} ${stat.failed}/${stat.runs} (${(rate * 100).toFixed(0)}%) ${shortTitle(test.title)}`, + ); + } + } + + onEnd(_result: FullResult): void { + if (this.dirty) mergeAndSaveFailureStore(this.store); + const file = refreshFrequentFailuresReport(); + if (!this.frequent.size) return; + // eslint-disable-next-line no-console + console.log( + `[failure-trend] 经常失败的用例 ${this.frequent.size} 条(榜单 ${path.relative(process.cwd(), file)}):`, + ); + for (const item of [...this.frequent.values()].slice(0, 20)) { + // eslint-disable-next-line no-console + console.log(` - ${item}`); + } + } +} + +function shortTitle(title: string): string { + return title.length > 80 ? `${title.slice(0, 77)}...` : title; +} diff --git a/reports/README.md b/reports/README.md index b607f68..7e05e90 100644 --- a/reports/README.md +++ b/reports/README.md @@ -5,9 +5,33 @@ | 路径 | 来源 | |------|------| | `ci-/` | `npm run test:ci` / Jenkins 单次归档 | -| `latest/` | 最近一次 CI 的固定路径(Jenkins HTML / JUnit / Allure) | +| `latest/` | 最近一次 CI 的固定路径(Jenkins HTML / JUnit 阅读入口) | | `junit/` | 非 runner 直跑时的默认 JUnit | | `batch-*` / `shard-run-*` / `rerun-*` | 本地历史批跑日志 | | `flake/` | 失败分类历史、趋势 `latest.md`、自愈建议 | 查看最近一次结果:`reports/latest/summary.json` 与 `reports/latest/playwright-report/index.html`。 + +## 存储说明(Jenkins 节点爆盘时看这里) + +单次 full(`tests/spd`,2 分片)原始产物可达 ~18G,主要来自同一份 trace/截图被保存 4 份: + +``` +ci-/ + blob-report/ 4.1G 仅 merge-reports 合并用,合并后可删 + shard-1/ shard-2/ 2.7G + 6.1G 分片原始产物(HTML / JUnit / allure / test-results) + playwright-report/ 2.0G + allure-results/ 3.0G 同一批 trace.zip / png +latest/ 5.1G 再复制一份给 Jenkins 报告插件 +``` + +`run-ci.mjs` 现已默认做这些瘦身,无需额外配置: + +| 行为 | 开关 | 默认 | +|------|------|------| +| 不生成 Allure(HTML 已含 trace/截图) | `ALLURE=1` 打开 | 关 | +| 合并后删除 `blob-report/`、`shard-N/` | `PW_KEEP_INTERMEDIATE=1` 保留 | 删除 | +| JUnit 只留 `message`,不留整段堆栈 | `PW_JUNIT_COMPACT=0` 保留全文 | 紧凑 | +| 归档内只留最大的 N 个 trace/截图 | `PW_KEEP_TRACES=0` 全删 | 全留 | +| 本机只保留最近 N 份 `ci-*` | `PW_KEEP_ARCHIVES=N` / `--keep-archives=N` | 全留(Jenkinsfile 传 1) | + +注意:`PW_KEEP_ARCHIVES` 清理 `run-ci` 产出的 `ci-*`;`batch-*` / `rerun-*` 是手工批跑目录,需自行删除。 diff --git a/scripts/_add-combination-po.py b/scripts/_add-combination-po.py new file mode 100644 index 0000000..dcb1cec --- /dev/null +++ b/scripts/_add-combination-po.py @@ -0,0 +1,259 @@ +# -*- coding: utf-8 -*- +"""为 13 个基础管理页 PO 追加「组合查询」方法(按实测筛选区字段,幂等)。""" +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent + +# 字段来自 2026-10-20 实测(.ant-form-item label),kind: text / select / dateRange +PAGES = { + "admit-product-list.page.ts": dict( + cls="AdmitProductListPage", + method="filterCombination", + before=" async filterByProductOrFallback()", + fields=[ + ("制单日期", "dateRange"), + ("销售商", "text"), + ("耗材类型", "select"), + ("器械注册人", "text"), + ("注册证号", "text"), + ("供应商", "text"), + ], + value="样本", + ), + "admit-material-list.page.ts": dict( + cls="AdmitMaterialListPage", + method="filterCombination", + before=None, + fields=[ + ("制单日期", "dateRange"), + ("销售商", "text"), + ("耗材类型", "select"), + ("器械注册人", "text"), + ("注册证号", "text"), + ("供应商", "text"), + ], + value="样本", + ), + "admit-supply.page.ts": dict( + cls="AdmitSupplyPage", + method="filterCombination", + before=None, + fields=[ + ("制单日期", "dateRange"), + ("销售商", "select"), + ("供应商", "select"), + ("变更单号", "text"), + ("物料名称", "text"), + ("规格型号", "text"), + ], + value="样本", + ), + "admit-task.page.ts": dict( + cls="AdmitTaskPage", + method="filterCombination", + before=None, + fields=[ + ("新增日期", "dateRange"), + ("操作日期", "dateRange"), + ("销售商", "text"), + ("物料名称", "text"), + ("注册证号", "text"), + ("操作人", "text"), + ("器械注册人", "text"), + ("耗材类型", "select"), + ("规格型号", "text"), + ("准入类型", "select"), + ("准入状态", "select"), + ], + value="样本", + ), + "vendor-qual.page.ts": dict( + cls="VendorQualPage", + method="filterCombination", + before=None, + fields=[ + ("进院供应商名称", "text"), + ("证件类型", "select"), + ("授权书名称", "text"), + ("授权单位", "text"), + ("被授权单位", "text"), + ], + value="样本", + ), + "vendor-auth-first.page.ts": dict( + cls="VendorAuthFirstPage", + method="filterCombination", + before=None, + fields=[ + ("制单日期", "dateRange"), + ("单号", "text"), + ("进院供应商", "select"), + ("预转供应商", "text"), + ("状态", "select"), + ("变更类型", "select"), + ("原供应商", "text"), + ("医院ID", "text"), + ("医院名称", "text"), + ], + value="样本", + ), + "vendor-auth-final.page.ts": dict( + cls="VendorAuthFinalPage", + method="filterCombination", + before=None, + fields=[ + ("制单日期", "dateRange"), + ("单号", "text"), + ("进院供应商", "select"), + ("预转供应商", "text"), + ("状态", "select"), + ("变更类型", "select"), + ("原供应商", "text"), + ], + value="样本", + ), + "vendor-cert-first.page.ts": dict( + cls="VendorCertFirstPage", + method="filterCombination", + before=None, + fields=[ + ("制单日期", "dateRange"), + ("单号", "text"), + ("变更类型", "select"), + ("注册证号(新)", "text"), + ("状态", "select"), + ("变更情况具体说明", "text"), + ], + value="样本", + ), + "vendor-cert-final.page.ts": dict( + cls="VendorCertFinalPage", + method="filterCombination", + before=None, + fields=[ + ("制单日期", "dateRange"), + ("单号", "text"), + ("变更类型", "select"), + ("注册证号(新)", "text"), + ("状态", "select"), + ], + value="样本", + ), + "vendor-cert-diff.page.ts": dict( + cls="VendorCertDiffPage", + method="filterCombination", + before=" async filterByMaterialSample()", + fields=[ + ("物料名称", "text"), + ("规格型号", "text"), + ("状态", "select"), + ("望海注册证号", "text"), + ("是否医疗器械", "select"), + ("生产厂家", "text"), + ("FLI+注册证号", "text"), + ("27位医保编码", "text"), + ], + value="样本", + ), + "vendor-eval-scheme.page.ts": dict( + cls="VendorEvalSchemePage", + method="filterCombination", + before=None, + fields=[ + ("创建日期", "dateRange"), + ("启用日期", "dateRange"), + ("方案编码", "text"), + ("方案名称", "text"), + ], + value="样本", + ), + "vendor-eval-indicator.page.ts": dict( + cls="VendorEvalIndicatorPage", + method="filterCombination", + before=None, + fields=[ + ("指标编码", "text"), + ("指标名称", "text"), + ], + value="样本", + ), + "surgery-base-pkg.page.ts": dict( + cls="SurgeryBasePkgPage", + method="filterCombination", + before=None, + fields=[ + ("产品名称", "text"), + ("规格型号", "text"), + ("器械注册人", "text"), + ("耗材类型", "select"), + ("是否收费", "select"), + ], + value="样本", + ), + "surgery-std-pkg.page.ts": dict( + cls="SurgeryStdPkgPage", + method="filterCombination", + before=None, + fields=[ + ("产品名称", "text"), + ("规格型号", "text"), + ("器械注册人", "text"), + ("耗材类型", "select"), + ("是否收费", "select"), + ], + value="样本", + ), +} + + +def method_src(spec: dict) -> str: + rows = "\n".join( + f" {{ label: '{label}', kind: '{kind}', value: '{spec['value']}' }}," + for label, kind in spec["fields"] + ) + names = "、".join(lbl for lbl, _ in spec["fields"]) + return f""" /** 组合查询:一次性填满筛选区全部字段后查询(字段以现网搜索条为准)。 */ + async {spec['method']}(): Promise {{ + const scope = await this.queryScope(); + await this.applyFilterCombination( + [ +{rows} + ], + {{ scope }}, + ); + expect('{names}'.split('、').length, '组合条件应至少 2 个').toBeGreaterThanOrEqual(2); + }} + +""" + + +def add_import(text: str) -> str: + if "type FilterField" in text or "applyFilterCombination" in text: + return text + # PO 通过基类继承方法,不需要 import FilterField;此处仅确保 expect 可用 + if "import { expect" not in text and "import type { Page }" in text: + text = text.replace("import type { Page } from '@playwright/test';", "import { expect, type Page } from '@playwright/test';") + return text + + +changed = 0 +for filename, spec in PAGES.items(): + path = ROOT / "pages/spd" / filename + text = path.read_text(encoding="utf-8") + if f"async {spec['method']}()" in text: + print(f"跳过(已存在) {filename}") + continue + text = add_import(text) + snippet = method_src(spec) + if spec["before"] and spec["before"] in text: + text = text.replace(spec["before"], snippet + spec["before"], 1) + else: + # 追加到类末尾 + text = text.rstrip() + assert text.endswith("}"), filename + text = text[:-1].rstrip() + "\n\n" + snippet.rstrip() + "\n}\n" + path.write_text(text, encoding="utf-8") + changed += 1 + print(f"已写入 {filename}") + +print(f"共 {changed} 个 PO") diff --git a/scripts/_diag-role.mjs b/scripts/_diag-role.mjs new file mode 100644 index 0000000..2c343e2 --- /dev/null +++ b/scripts/_diag-role.mjs @@ -0,0 +1,41 @@ +#!/usr/bin/env node +/** 临时诊断:查看角色管理页真实 DOM 结构(角色列表 / 权限树容器) */ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import './playwright-browsers-path.mjs'; +import { chromium } from 'playwright'; +import { existsSync } from 'node:fs'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); +if (existsSync(path.join(ROOT, '.env'))) process.loadEnvFile(path.join(ROOT, '.env')); +const BASE = process.env.BASE_URL || 'http://spdtest.cmic.com.cn:8080'; + +const browser = await chromium.launch({ headless: true }); +const ctx = await browser.newContext({ + storageState: path.join(ROOT, 'auth/user.json'), + viewport: { width: 1920, height: 1080 }, +}); +const page = await ctx.newPage(); +await page.goto(`${BASE}/spd/basic/user/platform/role`, { waitUntil: 'domcontentloaded', timeout: 120000 }); +await page.waitForTimeout(6000); + +const info = await page.evaluate(() => { + const out = {}; + out.title = document.title; + out.containers = [...document.querySelectorAll('[class*="role"], [class*="Role"], .ant-tree, .ant-table, .ant-list, [class*="perm"], [class*="menu"]')] + .slice(0, 60) + .map((el) => ({ + tag: el.tagName, + cls: el.className && String(el.className).slice(0, 90), + children: el.children.length, + text: (el.textContent || '').trim().slice(0, 60), + })); + out.treeCount = document.querySelectorAll('.ant-tree').length; + out.treeNodeCount = document.querySelectorAll('.ant-tree-treenode').length; + out.tableRows = document.querySelectorAll('.ant-table-tbody tr').length; + out.bodyLen = document.body.innerHTML.length; + return out; +}); +console.log(JSON.stringify(info, null, 1)); +await browser.close(); diff --git a/scripts/_diag-role2.mjs b/scripts/_diag-role2.mjs new file mode 100644 index 0000000..c454c05 --- /dev/null +++ b/scripts/_diag-role2.mjs @@ -0,0 +1,73 @@ +#!/usr/bin/env node +/** 临时诊断 v2:定位角色管理页左侧角色列表与右侧权限树的真实结构 */ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import './playwright-browsers-path.mjs'; +import { chromium } from 'playwright'; +import { existsSync } from 'node:fs'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); +if (existsSync(path.join(ROOT, '.env'))) process.loadEnvFile(path.join(ROOT, '.env')); +const BASE = process.env.BASE_URL || 'http://spdtest.cmic.com.cn:8080'; + +const browser = await chromium.launch({ headless: true }); +const ctx = await browser.newContext({ + storageState: path.join(ROOT, 'auth/user.json'), + viewport: { width: 1920, height: 1080 }, +}); +const page = await ctx.newPage(); +await page.goto(`${BASE}/spd/basic/user/platform/role`, { waitUntil: 'domcontentloaded', timeout: 120000 }); +await page.waitForTimeout(6000); + +const info = await page.evaluate(() => { + const trees = [...document.querySelectorAll('.ant-tree')].map((t, i) => { + const r = t.getBoundingClientRect(); + const visible = r.width > 0 && r.height > 0; + return { + i, + visible, + rect: [Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height)], + treenodes: t.querySelectorAll('.ant-tree-treenode').length, + firstTexts: [...t.querySelectorAll('.ant-tree-title')].slice(0, 6).map((n) => (n.textContent || '').trim().slice(0, 18)), + cls: String(t.className).slice(0, 80), + }; + }); + + // 左侧角色区域的备选容器 + const roleCandidates = [...document.querySelectorAll('.ant-table, .ant-list, .ant-tabs, [class*="role"], [class*="user"]')] + .filter((el) => { + const r = el.getBoundingClientRect(); + return r.width > 0 && r.height > 0 && r.x < 700; + }) + .slice(0, 12) + .map((el) => { + const r = el.getBoundingClientRect(); + return { + tag: el.tagName, + cls: String(el.className).slice(0, 90), + rect: [Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height)], + text: (el.textContent || '').trim().slice(0, 50), + }; + }); + + // 左侧区域所有 ant-tree-treenode 的可见性抽样 + const allNodes = [...document.querySelectorAll('.ant-tree-treenode')]; + const visibleNodes = allNodes.filter((n) => { + const r = n.getBoundingClientRect(); + return r.width > 0 && r.height > 0; + }); + const sample = visibleNodes.slice(0, 8).map((n) => { + const r = n.getBoundingClientRect(); + return { + rect: [Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height)], + text: (n.textContent || '').trim().slice(0, 20), + checkbox: !!n.querySelector('.ant-tree-checkbox'), + ownCheckbox: !!n.querySelector('.ant-tree-checkbox:not(.ant-tree-checkbox-indeterminate)'), + }; + }); + + return { title: document.title, trees, totalNodes: allNodes.length, visibleNodes: visibleNodes.length, sample, roleCandidates }; +}); +console.log(JSON.stringify(info, null, 1)); +await browser.close(); diff --git a/scripts/_diag-role3.mjs b/scripts/_diag-role3.mjs new file mode 100644 index 0000000..81a91a2 --- /dev/null +++ b/scripts/_diag-role3.mjs @@ -0,0 +1,63 @@ +#!/usr/bin/env node +/** 临时诊断 v3:确认 .ant-tree 内 checkbox 是否命中交互选择器,以及左侧角色列表结构 */ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import './playwright-browsers-path.mjs'; +import { chromium } from 'playwright'; +import { existsSync } from 'node:fs'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); +if (existsSync(path.join(ROOT, '.env'))) process.loadEnvFile(path.join(ROOT, '.env')); +const BASE = process.env.BASE_URL || 'http://spdtest.cmic.com.cn:8080'; + +const browser = await chromium.launch({ headless: true }); +const ctx = await browser.newContext({ + storageState: path.join(ROOT, 'auth/user.json'), + viewport: { width: 1920, height: 1080 }, +}); +const page = await ctx.newPage(); +await page.goto(`${BASE}/spd/basic/user/platform/role`, { waitUntil: 'domcontentloaded', timeout: 120000 }); +await page.waitForTimeout(6000); + +const info = await page.evaluate(() => { + const INTERACTIVE = + 'a[href],button,input,select,textarea,[role="button"],[role="link"],[role="tab"],' + + '[role="menuitem"],[role="checkbox"],[role="radio"],[role="combobox"],[role="switch"],' + + '[role="slider"],[contenteditable="true"],[tabindex]:not([tabindex="-1"]),' + + '.ant-btn,.ant-select,.ant-picker,.ant-input,.ant-checkbox-wrapper,.ant-radio-wrapper,' + + '.ant-pagination,.ant-pagination-item,.ant-pagination-next,.ant-pagination-prev,' + + '.ant-pagination-options-size-changer,.ant-pagination-options-quick-jumper input,' + + '.ant-table-column-sorter,.ant-dropdown-trigger,.ant-tabs-tab,.ant-drawer-close,' + + '.ant-modal-close,.advance-filter,.ant-cascader,.ant-drawer-footer .ant-btn,' + + '.ant-table-row-expand-icon,.ant-table-row-expand-icon-cell'; + + const hits = document.querySelectorAll(INTERACTIVE); + const inTree = [...hits].filter((el) => el.closest('.ant-tree')); + const treeClasses = [...new Set(inTree.slice(0, 10).map((el) => el.tagName + '.' + String(el.className).slice(0, 50)))]; + + // 左侧:x < 560 的可见可交互元素(角色列表区域) + const left = [...hits].filter((el) => { + const r = el.getBoundingClientRect(); + return r.width > 0 && r.height > 0 && r.x < 560 && !el.closest('.ant-menu'); + }); + const leftSample = left.slice(0, 15).map((el) => { + const r = el.getBoundingClientRect(); + return { + tag: el.tagName, + cls: String(el.className).slice(0, 60), + rect: [Math.round(r.x), Math.round(r.y)], + text: (el.textContent || '').trim().slice(0, 24), + }; + }); + + return { + totalHits: hits.length, + inTreeHits: inTree.length, + treeClasses, + leftCount: left.length, + leftSample, + }; +}); +console.log(JSON.stringify(info, null, 1)); +await browser.close(); diff --git a/scripts/_diag-role4.mjs b/scripts/_diag-role4.mjs new file mode 100644 index 0000000..1aaa52f --- /dev/null +++ b/scripts/_diag-role4.mjs @@ -0,0 +1,27 @@ +import path from 'node:path'; +import './playwright-browsers-path.mjs'; +import { chromium } from 'playwright'; +import { existsSync } from 'node:fs'; +const ROOT = process.cwd(); +if (existsSync(path.join(ROOT, '.env'))) process.loadEnvFile(path.join(ROOT, '.env')); +const BASE = process.env.BASE_URL; +const browser = await chromium.launch({ headless: true }); +const ctx = await browser.newContext({ storageState: path.join(ROOT,'auth/user.json'), viewport:{width:1920,height:1080} }); +const page = await ctx.newPage(); +await page.goto(`${BASE}/spd/basic/user/platform/role`, { waitUntil:'domcontentloaded', timeout:120000 }); +await page.waitForTimeout(6000); +const info = await page.evaluate(() => { + const node = document.querySelector('.ant-tree-treenode'); + const cb = document.querySelector('.ant-tree-checkbox'); + const sw = document.querySelector('.ant-tree-switcher'); + return { + nodeCls: node && String(node.className), + cbCls: cb && String(cb.className), + cbOuter: cb && cb.outerHTML.slice(0,200), + swCls: sw && String(sw.className), + titleTag: document.querySelector('.ant-tree-title')?.tagName, + currentRole: [...document.querySelectorAll('*')].filter(e=>/当前角色|角色名称/.test(e.textContent||'') && e.children.length===0).slice(0,3).map(e=>e.textContent.trim()), + }; +}); +console.log(JSON.stringify(info,null,1)); +await browser.close(); diff --git a/scripts/_diag-role5.mjs b/scripts/_diag-role5.mjs new file mode 100644 index 0000000..6bce7aa --- /dev/null +++ b/scripts/_diag-role5.mjs @@ -0,0 +1,24 @@ +import path from 'node:path'; +import './playwright-browsers-path.mjs'; +import { chromium } from 'playwright'; +import { existsSync } from 'node:fs'; +const ROOT = process.cwd(); +if (existsSync(path.join(ROOT, '.env'))) process.loadEnvFile(path.join(ROOT, '.env')); +const BASE = process.env.BASE_URL; +const browser = await chromium.launch({ headless: true }); +const ctx = await browser.newContext({ storageState: path.join(ROOT,'auth/user.json'), viewport:{width:1920,height:1080} }); +const page = await ctx.newPage(); +await page.goto(`${BASE}/spd/basic/user/platform/role`, { waitUntil:'domcontentloaded', timeout:120000 }); +await page.waitForTimeout(6000); +const info = await page.evaluate(() => { + const tree = document.querySelector('.menu-role-tree'); + const all = [...tree.querySelectorAll('.ant-tree-treenode')]; + const classes = all.slice(0,6).map(n=>n.className); + const sample = all.slice(0,12).map(n=>({ + cls: (n.className.match(/level-\d+/)||['?'])[0], + text: (n.querySelector('.ant-tree-title')?.textContent||'').trim().slice(0,14), + })); + return { hasLevelClass: /level-/.test(all[1]?.className||''), classes, sample }; +}); +console.log(JSON.stringify(info,null,1)); +await browser.close(); diff --git a/scripts/_list-filter-fields.py b/scripts/_list-filter-fields.py new file mode 100644 index 0000000..6e6962d --- /dev/null +++ b/scripts/_list-filter-fields.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +"""列出每个基础管理页面扫描产物中的筛选类字段(输入框 / select / 日期 / 下拉触发器)。""" +import glob +import json +import os +import re +import sys + +SKIP = re.compile(r"^(确 认|确 定|取 消|查 询|重 置|筛 选|新增|添加|导 出|保 存|关 闭|刷 新)$") + + +def load(path): + with open(path, encoding="utf-8") as fh: + return json.load(fh) + + +def fields(doc): + out = [] + for e in doc.get("elements", []): + tag = (e.get("tag") or "").lower() + typ = (e.get("type") or "").lower() + eid = e.get("id") or "" + label = (e.get("label") or "").strip() + ph = (e.get("placeholder") or "").strip() + if eid.startswith(("form_item_", "table_search_form_", "basic_", "search_")): + out.append(("id", eid, label or ph)) + elif tag == "input" and typ not in ("hidden", "button", "checkbox", "radio"): + out.append(("input", ph or label, e.get("name") or "")) + elif tag == "select" or typ == "select": + out.append(("select", ph or label, "")) + elif "date" in (e.get("className") or "") or "日期" in (label + ph): + out.append(("date", label or ph, "")) + seen, uniq = set(), [] + for it in out: + key = (it[1], it[2]) + if key in seen or not (it[1] or it[2]): + continue + if SKIP.match(it[1].strip()): + continue + seen.add(key) + uniq.append(it) + return uniq + + +for path in sorted(glob.glob("data/scan/*-elements.json")): + if not os.path.basename(path).startswith(("basic-", "admit-", "vendor-", "surgery-")): + continue + doc = load(path) + fs = fields(doc) + print(f"\n### {os.path.basename(path)} ({len(fs)} 个)") + for kind, a, b in fs[:40]: + print(f" {kind:7s} {a:36s} {b}") diff --git a/scripts/add-basic-combination-cases.py b/scripts/add-basic-combination-cases.py new file mode 100644 index 0000000..9bdc6c9 --- /dev/null +++ b/scripts/add-basic-combination-cases.py @@ -0,0 +1,115 @@ +"""为已实现 filterCombination 的页面补「组合查询」用例行(CSV + spec 注释同步)。 + +字段名直接取自各 PO 的 filterCombination,且已在真实环境验证全部生效。 +""" +from __future__ import annotations + +import csv +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent + +# (CSV 文件名片段, 用例编号前缀, PO 文件, spec 文件, fixture, 前置条件里的 path 描述) +TARGETS = [ + ("产品准入单列表", "ADMIT-PLS", "admit-product-list.page.ts", "admit-product-list.spec.ts", "admitProductListPage"), + ("物料准入单列表", "ADMIT-MLS", "admit-material-list.page.ts", "admit-material-list.spec.ts", "admitMaterialListPage"), + ("供货目录申请", "ADMIT-SUP", "admit-supply.page.ts", "admit-supply.spec.ts", "admitSupplyPage"), + ("准入任务分配", "ADMIT-TSK", "admit-task.page.ts", "admit-task.spec.ts", "admitTaskPage"), + ("基础包维护", "SURG-BASE", "surgery-base-pkg.page.ts", "surgery-base-pkg.spec.ts", "surgeryBasePkgPage"), + ("标准套包维护", "SURG-STD", "surgery-std-pkg.page.ts", "surgery-std-pkg.spec.ts", "surgeryStdPkgPage"), + ("供应商资质管理", "VENDOR-QUL", "vendor-qual.page.ts", "vendor-qual.spec.ts", "vendorQualPage"), + ("授权变更初审", "VENDOR-AF1", "vendor-auth-first.page.ts", "vendor-audit.spec.ts", "vendorAuthFirstPage"), + ("授权变更终审", "VENDOR-AF2", "vendor-auth-final.page.ts", "vendor-audit.spec.ts", "vendorAuthFinalPage"), + ("注册证变更初审", "VENDOR-CF1", "vendor-cert-first.page.ts", "vendor-audit.spec.ts", "vendorCertFirstPage"), + ("注册证变更终审", "VENDOR-CF2", "vendor-cert-final.page.ts", "vendor-audit.spec.ts", "vendorCertFinalPage"), + ("注册证差异对比", "VENDOR-CDF", "vendor-cert-diff.page.ts", "vendor-cert-diff.spec.ts", "vendorCertDiffPage"), + ("评价指标维护", "VENDOR-EVI", "vendor-eval-indicator.page.ts", "vendor-eval-indicator.spec.ts", "vendorEvalIndicatorPage"), + ("评价方案维护", "VENDOR-EVS", "vendor-eval-scheme.page.ts", "vendor-eval-scheme.spec.ts", "vendorEvalSchemePage"), +] + + +def po_fields(po: str) -> list[tuple[str, str]]: + text = (ROOT / "pages/spd" / po).read_text(encoding="utf-8") + block = re.search(r"async filterCombination\(\).*?\n \}", text, re.S) + if not block: + raise SystemExit(f"{po} 缺少 filterCombination") + return re.findall(r"label: '([^']+)', kind: '(\w+)'", block.group(0)) + + +def spec_group(spec: str, prefix: str) -> str: + """取 spec 中已存在用例所属的 describe 标题,保证新增用例落在同一分组下。""" + text = (ROOT / "tests/spd" / spec).read_text(encoding="utf-8") + found = None + last = None + for line in text.splitlines(): + m = re.match(r"\s*test\.describe\('([^']+)'", line) + if m: + found = m.group(1) + if re.search(rf"test\('{prefix}-", line) and found: + last = found + if last and "页面进入" not in last: + return last + return f"{prefix} 筛选与列表" + + +def build_row(prefix: str, next_no: int, module: str, fields: list[tuple[str, str]], page_desc: str) -> list[str]: + names = [f for f, _ in fields] + steps = ["1. 进入页面并等待列表加载"] + for i, (name, kind) in enumerate(fields, start=2): + if kind == "select": + steps.append(f"{i}. 「{name}」下拉选择任一可选项") + elif kind == "dateRange": + steps.append(f"{i}. 「{name}」日期区间选择当天") + else: + steps.append(f"{i}. 「{name}」填写关键字") + steps.append(f"{len(fields) + 2}. 点击「查询」") + steps.append(f"{len(fields) + 3}. 检查列表返回结果") + cond = ( + f"测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「{module}」权限;" + f"已进入{page_desc};筛选区已展开" + ) + expect = ( + f"多条件同时生效:{('、'.join(names))} 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示)," + f"列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系" + ) + return [ + f"{prefix}-{next_no:03d}", + module, + f"{module.split(' · ')[-1]} · 组合查询", + f"多条件组合查询({names[0]}等{len(names)}项)", + "高", + cond, + "\n".join(steps), + expect, + "待执行", + ] + + +def append_csv(path: Path, row: list[str]) -> None: + with path.open("a", encoding="utf-8", newline="") as fh: + csv.writer(fh, quoting=csv.QUOTE_ALL, lineterminator="\n").writerow(row) + + +def main() -> None: + for frag, prefix, po, spec, fixture in TARGETS: + matches = list((ROOT / "testcase").glob(f"基础管理-*{frag}-UI自动化测试用例-01.csv")) + if len(matches) != 1: + raise SystemExit(f"CSV 匹配异常:{frag} -> {matches}") + csv_path = matches[0] + rows = list(csv.reader(csv_path.open(encoding="utf-8"))) + if any(r and r[0].startswith(f"{prefix}-") and "组合查询" in r[2] for r in rows[1:]): + print(f"跳过(已有组合查询):{csv_path.name}") + continue + ids = [int(r[0].split("-")[-1]) for r in rows[1:] if r and r[0].startswith(prefix + "-")] + next_no = max(ids) + 1 + module = rows[1][1] + fields = po_fields(po) + page_desc = f"{module.split(' · ')[-1]}页" + append_csv(csv_path, build_row(prefix, next_no, module, fields, page_desc)) + group = spec_group(spec, prefix) + print(f"{prefix}-{next_no:03d} {csv_path.name} | {len(fields)} 项 | spec 分组:{group} | {fixture}") + + +if __name__ == "__main__": + main() diff --git a/scripts/add-basic-combination-spec.py b/scripts/add-basic-combination-spec.py new file mode 100644 index 0000000..2ed8f83 --- /dev/null +++ b/scripts/add-basic-combination-spec.py @@ -0,0 +1,70 @@ +"""把「组合查询」用例插入对应 spec 的筛选分组末尾(幂等,可重复执行)。""" +from __future__ import annotations + +import csv +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent + +# CSV 片段 -> (spec 文件, 目标 describe 标题, fixture 名) +TARGETS = { + "产品准入单列表": ("admit-product-list.spec.ts", "产品准入单列表 · 筛选与状态", "admitProductListPage"), + "物料准入单列表": ("admit-material-list.spec.ts", "物料准入单列表 · 筛选与状态", "admitMaterialListPage"), + "供货目录申请": ("admit-supply.spec.ts", "供货目录申请 · 筛选与状态", "admitSupplyPage"), + "准入任务分配": ("admit-task.spec.ts", "准入任务分配 · 筛选与分类", "admitTaskPage"), + "基础包维护": ("surgery-base-pkg.spec.ts", "基础包维护 · 右侧筛选与列表", "surgeryBasePkgPage"), + "标准套包维护": ("surgery-std-pkg.spec.ts", "标准套包维护 · 筛选与构成列表", "surgeryStdPkgPage"), + "供应商资质管理": ("vendor-qual.spec.ts", "供应商资质管理 · 筛选与 Tab", "vendorQualPage"), + "授权变更初审": ("vendor-audit.spec.ts", "授权变更初审 · 筛选与列表", "vendorAuthFirstPage"), + "授权变更终审": ("vendor-audit.spec.ts", "授权变更终审 · 筛选与列表", "vendorAuthFinalPage"), + "注册证变更初审": ("vendor-audit.spec.ts", "注册证变更初审 · 筛选与列表", "vendorCertFirstPage"), + "注册证变更终审": ("vendor-audit.spec.ts", "注册证变更终审 · 筛选与列表", "vendorCertFinalPage"), + "注册证差异对比": ("vendor-cert-diff.spec.ts", "注册证差异对比 · 筛选与对比", "vendorCertDiffPage"), + "评价指标维护": ("vendor-eval-indicator.spec.ts", "评价指标维护 · 树与列表", "vendorEvalIndicatorPage"), + "评价方案维护": ("vendor-eval-scheme.spec.ts", "评价方案维护 · 筛选与维护", "vendorEvalSchemePage"), +} + + +def find_close_brace(lines: list[str], start: int) -> int: + """从 describe 起始行找到与之配对的收尾 ` });`。""" + depth = 0 + for idx in range(start, len(lines)): + depth += lines[idx].count("{") - lines[idx].count("}") + if depth <= 0 and idx > start: + return idx + raise SystemExit("describe 未闭合") + + +def main() -> None: + for frag, (spec, group, fixture) in TARGETS.items(): + csv_path = next((ROOT / "testcase").glob(f"基础管理-*{frag}-UI自动化测试用例-01.csv")) + rows = [r for r in csv.reader(csv_path.open(encoding="utf-8")) if r] + row = next((r for r in rows[1:] if "组合查询" in r[3]), None) + if not row: + print(f"跳过(CSV 无组合查询行):{csv_path.name}") + continue + case_id, title = row[0], row[3] + spec_path = ROOT / "tests/spd" / spec + text = spec_path.read_text(encoding="utf-8") + if case_id in text: + print(f"跳过(spec 已有):{case_id}") + continue + lines = text.splitlines() + start = next((i for i, l in enumerate(lines) if l.strip().startswith(f"test.describe('{group}'")), None) + if start is None: + raise SystemExit(f"{spec} 未找到分组「{group}」") + close = find_close_brace(lines, start) + block = ( + f" test('{case_id} {title}', async ({{ {fixture} }}) => {{\n" + f" await {fixture}.open();\n" + f" await {fixture}.filterCombination();\n" + f" }});" + ) + lines.insert(close, block) + spec_path.write_text("\n".join(lines) + "\n", encoding="utf-8") + print(f"{case_id} -> {spec} · {group}") + + +if __name__ == "__main__": + main() diff --git a/scripts/align-basic-cases-format.py b/scripts/align-basic-cases-format.py new file mode 100644 index 0000000..cb7aa60 --- /dev/null +++ b/scripts/align-basic-cases-format.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- +"""把基础管理 23 份 CSV 对齐样板「出入库明细查询」的写法(仅整形,不改用例语义)。 + +对齐项(样板 testcase/统计报表-科室出入库报表-出入库明细查询-UI自动化测试用例-01.csv): +1. 前置条件:去掉明文账号 / 密码,统一「已通过 auth.setup 写入 storageState 登录」; + 页面统一写成「进入 xxx 页(/路径,无 iframe)」,去掉域名与截图 / 扫描类运行时说明; + 若前置已带路径则不做替换(幂等)。 +2. 测试步骤:去掉「确认 chromium 已加载 storageState …」「使用用户名 test_1、密码 …」这类 + 与测试执行器相关的开场步骤,按 1..N 重新编号。 +3. 用例编号 / 标题 / 功能测试点 / 优先级 / 预期结果 / 实际结果 一律不动。 + +用法: + python3.12 scripts/align-basic-cases-format.py # 写盘 + python3.12 scripts/align-basic-cases-format.py --check # 只报告 +""" + +from __future__ import annotations + +import csv +import io +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +CASE_DIR = ROOT / "testcase" +LOGIN_OK = "已通过 auth.setup 写入 storageState 登录" + +# 页面:模块显示名 / 菜单路径 / URL 路径;godoc 里对齐 data/*.data.ts +PAGES: dict[str, dict[str, str]] = { + "公告管理": {"menu": "基础设置 → 医院资料 → 公告管理", "path": "/spd/basic/hospital/paltform/notice"}, + "医院信息登记": {"menu": "基础设置 → 医院资料 → 医院信息登记", "path": "/spd/basic/hospital/paltform/info"}, + "财务月份设置": {"menu": "基础设置 → 医院资料 → 财务月份设置", "path": "/spd/basic/hospital/balance/month"}, + "院区管理": {"menu": "基础设置 → 医院资料 → 院区管理", "path": "/spd/basic/hospital/paltform/area"}, + "物料准入管理": {"menu": "基础设置 → 准入管理 → 物料准入管理", "path": "/spd/basic/dataSync/dataSync/productApply/ZRlist"}, + "产品准入申请": {"menu": "基础设置 → 准入管理 → 产品准入申请", "path": "/spd/basic/dataSync/dataSync/productApply/list"}, + "物料准入单列表": {"menu": "基础设置 → 准入管理 → 物料准入单列表", "path": "/spd/basic/dataSync/dataSync/productOrder/zrlist"}, + "产品准入单列表": {"menu": "基础设置 → 准入管理 → 产品准入单列表", "path": "/spd/basic/dataSync/dataSync/productOrder/list"}, + "供货目录申请": {"menu": "基础设置 → 准入管理 → 供货目录申请", "path": "/spd/basic/dataSync/dataSync/supplyApply/list"}, + "准入任务分配": {"menu": "基础设置 → 准入管理 → 准入任务分配", "path": "/spd/basic/dataSync/dataSync/accessKind/list"}, + "厂商查询": {"menu": "基础设置 → 厂商管理 → 厂商查询", "path": "/spd/basic/mfrs/paltform/mfrsName/factory"}, + "供应商资质管理": {"menu": "基础设置 → 厂商管理 → 供应商资质管理", "path": "/spd/basic/mfrs/basic/qcManage"}, + "授权变更初审": {"menu": "基础设置 → 厂商管理 → 授权变更初审", "path": "/spd/basic/mfrs/paltform/mfrsName/authAudit"}, + "授权变更终审": {"menu": "基础设置 → 厂商管理 → 授权变更终审", "path": "/spd/basic/mfrs/paltform/mfrsName/authAuditFinal"}, + "注册证变更初审": {"menu": "基础设置 → 厂商管理 → 注册证变更初审", "path": "/spd/basic/mfrs/paltform/mfrsName/regCertAudit"}, + "注册证变更终审": {"menu": "基础设置 → 厂商管理 → 注册证变更终审", "path": "/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal"}, + "注册证差异对比": {"menu": "基础设置 → 厂商管理 → 注册证差异对比", "path": "/spd/basic/mfrs/scjs/yc/certCompare"}, + "评价方案维护": {"menu": "基础设置 → 厂商管理 → 评价方案维护", "path": "/spd/basic/mfrs/paltform/mfrsName/evaluationPlan"}, + "评价指标维护": {"menu": "基础设置 → 厂商管理 → 评价指标维护", "path": "/spd/basic/mfrs/paltform/mfrsName/indicators"}, + "供应商节假日管理": {"menu": "基础设置 → 厂商管理 → 供应商节假日管理", "path": "/spd/basic/mfrs/basic/prov/holiday"}, + "基础包维护": {"menu": "基础设置 → 手术套包设置 → 基础包维护", "path": "/spd/basic/operationSetting/baseMaintenance"}, + "术种维护": {"menu": "基础设置 → 手术套包设置 → 术种维护", "path": "/spd/basic/operationSetting/typeMaintenance"}, + "标准套包维护": {"menu": "基础设置 → 手术套包设置 → 标准套包维护", "path": "/spd/basic/operationSetting/standardMaintenance"}, +} + +CRED_RE = re.compile( + r"(正例账号[^)]*)" # (正例账号 xxx / ****) + r"|用户名\s*\S+、密码\s*[^\s;;,,))]+" # 用户名 xxx、密码 **** + r"|密码\s*[A-Za-z0-9!@#$%^&*._-]{6,}" # 单独的明文密码片段 +) +RUNTIME_STEP_RE = re.compile( + r"^\s*\d+\.\s*(确认 chromium|使用用户名|使用 test\.use|storageState 已有效|浏览器上下文)", + re.M, +) +PAGE_PRE_RE = re.compile(r"已(?:进入|直接访问)([^;;((]+?)页(?:([^)]*))?") +NUMBER_RE = re.compile(r"^\s*(\d+)\.\s*(.*)$", re.M) +URL_BACKTICK_RE = re.compile(r"`https?://[^/`]+(/[^`]*)`") + + +def page_of(filename: str) -> str: + tail = filename.replace("-UI自动化测试用例-01.csv", "") + return tail.rsplit("-", 1)[-1] + + +def has_path(pre: str) -> bool: + return bool(re.search(r"(/|(http|/spd/", pre)) + + +PATH_PRE_RE = re.compile(r"已进入([^;;(]+?)页([^)]*)") +BAD_FRAG_RE = re.compile(r";;|登录已通过") +PATH_TAIL_RE = re.compile(r",无 iframe)$") + + +def _balanced(text: str) -> bool: + return text.count("(") == text.count(")") + + +def clean_pre(pre: str, page: str) -> str: + info = PAGES[page] + # 1) 先去掉凭据,统一成「已通过 auth.setup 写入 storageState 登录」 + text = CRED_RE.sub(LOGIN_OK, pre) + # 旧写法「已通过 auth.setup 写入 storageState 登录 已登录并进入 …页」→ 只保留一份 + text = text.replace(f"{LOGIN_OK} 已登录并进入", f"{LOGIN_OK};已进入") + text = text.replace(f"{LOGIN_OK}已登录并进入", f"{LOGIN_OK};已进入") + # 2) 页面括号统一成样板写法(先于分句,避免括号被拆开) + text = re.sub(r"((?:https?://|/spd/)[^)]*)", f"({info['path']},无 iframe)", text) + text = PATH_PRE_RE.sub(lambda m: f"已进入{m.group(1)}页({info['path']},无 iframe)", text) + # 3) 分句去重 + kept: list[str] = [] + seen_login = False + for part in (p.strip() for p in text.split(";")): + if not part: + continue + if LOGIN_OK in part: + extra = part.replace(LOGIN_OK, "").strip("() ") + if seen_login: + if not extra: + continue + part = extra + else: + part = f"{LOGIN_OK}" + (f"({extra})" if extra and not extra.startswith("无 iframe") else "") + seen_login = True + if re.fullmatch(r"已登录.*", part) or part.startswith("已登录且具备"): + continue + if part.startswith("并进入"): + part = "已进入" + part[len("并进入"):] + kept.append(part) + text = ";".join(kept) + text = re.sub(r";{2,}", ";", text).strip(";") + # 4) 未写页面时补一句「已进入 xxx 页(路径,无 iframe)」;未登录反例只写目标 URL + if not has_path(text): + parts = [p for p in text.split(";") if p.strip()] + if "无登录态" in text or "未带登录态" in text or "清除登录态" in text: + parts.append(f"直接访问{page} URL({info['path']},无 iframe)") + return ";".join(dict.fromkeys(parts)) + anchor = f"已进入{page}页({info['path']},无 iframe)" + insert_at = 1 + for i, part in enumerate(parts): + if "权限" in part: + insert_at = i + 1 + parts.insert(insert_at, anchor) + text = ";".join(parts) + # 5) 页面句只保留第一处;其后的「已进入…页」分句整体丢弃 + parts = [p.strip() for p in text.split(";") if p.strip()] + out: list[str] = [] + page_seen = False + for part in parts: + is_page_part = bool(PATH_PRE_RE.search(part)) or ( + part.startswith("已进入") and page in part + ) + if is_page_part: + if page_seen: + continue + page_seen = True + out.append(part) + text = ";".join(out) + # 6) 收尾收敛:未登录反例统一「直接访问{页面} URL(路径,无 iframe)」 + text = re.sub(rf"直接访问\s*{re.escape(page)}\s*URL([^)]*),?", f"直接访问{page} URL({info['path']},无 iframe)", text) + page_hits = re.findall(rf"已进入(?:{re.escape(page)}|{re.escape(info['path'])}|[^;]*?页)", text) + if len(page_hits) > 1: + raise ValueError(f"[{page}] 前置出现多个页面句:{text}") + if BAD_FRAG_RE.search(text) or not _balanced(text): + raise ValueError(f"[{page}] 前置整形后仍异常:{text}") + return text + + +def clean_steps(steps: str) -> str: + lines = steps.split("\n") + kept = [ln for ln in lines if not RUNTIME_STEP_RE.match(ln)] + out = [] + for i, ln in enumerate(kept, 1): + m = NUMBER_RE.match(ln) + body = m.group(2) if m else ln.strip() + if body: + body = URL_BACKTICK_RE.sub(r"`\1`", body) + out.append(f"{i}. {body}") + return "\n".join(out) + + +def main() -> None: + check_only = "--check" in sys.argv + changed = 0 + for path in sorted(CASE_DIR.glob("基础管理-*-UI自动化测试用例-01.csv")): + page = page_of(path.name) + if page not in PAGES: + print(f"跳过未知页面:{path.name}") + continue + with path.open(encoding="utf-8-sig", newline="") as fh: + rows = list(csv.reader(fh)) + head, body = rows[0], rows[1:] + if head[:9] != ["用例编号", "功能模块", "功能测试点", "用例标题", "优先级", "前置条件", "测试步骤", "预期结果", "实际结果"]: + raise SystemExit(f"{path.name} 表头与 9 列规范不一致:{head}") + new_rows = [] + for r in body: + if not r: + continue + row = list(r) + [""] * (9 - len(r)) + row[5] = clean_pre(row[5], page) + row[6] = clean_steps(row[6]) + new_rows.append(row[:9]) + buf = io.StringIO() + writer = csv.writer(buf, quoting=csv.QUOTE_ALL, lineterminator="\n") + writer.writerow(head[:9]) + writer.writerows(new_rows) + text = buf.getvalue() + old = path.read_text(encoding="utf-8") + if old.lstrip("\ufeff") == text: + continue + changed += 1 + print(f"{'待更新' if check_only else '已写入'} {path.name}") + if not check_only: + path.write_text(text, encoding="utf-8", newline="") + print(f"共 {changed} 份{'需要' if check_only else '已'}更新") + + +if __name__ == "__main__": + main() diff --git a/scripts/assert-basic-csv-spec-sync.mjs b/scripts/assert-basic-csv-spec-sync.mjs new file mode 100644 index 0000000..857f1e2 --- /dev/null +++ b/scripts/assert-basic-csv-spec-sync.mjs @@ -0,0 +1,111 @@ +#!/usr/bin/env node +/** CSV 用例编号+标题必须出现在对应 spec 中。 */ +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); + +const PAIRS = [ + ['testcase/基础管理-医院资料-公告管理-UI自动化测试用例-01.csv', 'tests/spd/basic-notice.spec.ts'], + ['testcase/基础管理-医院资料-医院信息登记-UI自动化测试用例-01.csv', 'tests/spd/basic-hospital-info.spec.ts'], + ['testcase/基础管理-医院资料-财务月份设置-UI自动化测试用例-01.csv', 'tests/spd/basic-finance-month.spec.ts'], + ['testcase/基础管理-医院资料-院区管理-UI自动化测试用例-01.csv', 'tests/spd/basic-branch.spec.ts'], + ['testcase/基础管理-准入管理-物料准入管理-UI自动化测试用例-01.csv', 'tests/spd/admit-material.spec.ts'], + ['testcase/基础管理-准入管理-产品准入申请-UI自动化测试用例-01.csv', 'tests/spd/admit-product-apply.spec.ts'], + ['testcase/基础管理-准入管理-物料准入单列表-UI自动化测试用例-01.csv', 'tests/spd/admit-material-list.spec.ts'], + ['testcase/基础管理-准入管理-产品准入单列表-UI自动化测试用例-01.csv', 'tests/spd/admit-product-list.spec.ts'], + ['testcase/基础管理-准入管理-供货目录申请-UI自动化测试用例-01.csv', 'tests/spd/admit-supply.spec.ts'], + ['testcase/基础管理-准入管理-准入任务分配-UI自动化测试用例-01.csv', 'tests/spd/admit-task.spec.ts'], + ['testcase/基础管理-用户管理-用户管理-UI自动化测试用例-01.csv', 'tests/spd/basic-user.spec.ts'], + ['testcase/基础管理-用户管理-医共体用户管理-UI自动化测试用例-01.csv', 'tests/spd/basic-user-union.spec.ts'], + ['testcase/基础管理-用户管理-角色管理-UI自动化测试用例-01.csv', 'tests/spd/basic-role.spec.ts'], +]; + +function parseCsv(text) { + const rows = []; + let row = []; + let cell = ''; + let i = 0; + let quoted = false; + const pushCell = () => { + row.push(cell); + cell = ''; + }; + const pushRow = () => { + if (row.length) rows.push(row); + row = []; + }; + while (i < text.length) { + const ch = text[i]; + if (quoted) { + if (ch === '"' && text[i + 1] === '"') { + cell += '"'; + i += 2; + continue; + } + if (ch === '"') { + quoted = false; + i += 1; + continue; + } + cell += ch; + i += 1; + continue; + } + if (ch === '"') { + quoted = true; + i += 1; + continue; + } + if (ch === ',') { + pushCell(); + i += 1; + continue; + } + if (ch === '\n' || ch === '\r') { + if (ch === '\r' && text[i + 1] === '\n') i += 1; + pushCell(); + pushRow(); + i += 1; + continue; + } + cell += ch; + i += 1; + } + if (cell || row.length) { + pushCell(); + pushRow(); + } + return rows; +} + +const missing = []; +for (const [csvRel, specRel] of PAIRS) { + const csvText = readFileSync(path.join(ROOT, csvRel), 'utf8').replace(/^\uFEFF/, ''); + const spec = readFileSync(path.join(ROOT, specRel), 'utf8'); + const table = parseCsv(csvText); + const header = table[0] || []; + const idIdx = header.indexOf('用例编号'); + const titleIdx = header.indexOf('用例标题'); + const pointIdx = header.indexOf('功能测试点'); + const modIdx = header.indexOf('功能模块'); + for (const cols of table.slice(1)) { + if (!cols[idIdx]) continue; + const title = `${cols[idIdx]} ${cols[titleIdx]}`; + if (!spec.includes(title)) missing.push(`${specRel}: ${title}`); + if (cols[pointIdx] && !spec.includes(cols[pointIdx])) { + missing.push(`${specRel}: describe ${cols[pointIdx]}`); + } + if (cols[modIdx] && !spec.includes(cols[modIdx])) { + missing.push(`${specRel}: module ${cols[modIdx]}`); + } + } +} + +if (missing.length) { + console.error('[gate] CSV 与 spec 不一致:'); + for (const item of missing) console.error(` ${item}`); + process.exit(1); +} +console.log('[gate] CSV ↔ spec 标题一致'); diff --git a/scripts/assert-surgery-csv-spec-sync.mjs b/scripts/assert-surgery-csv-spec-sync.mjs new file mode 100644 index 0000000..482d33a --- /dev/null +++ b/scripts/assert-surgery-csv-spec-sync.mjs @@ -0,0 +1,103 @@ +#!/usr/bin/env node +/** 手术套包设置 CSV 用例编号+标题+功能测试点+功能模块 必须出现在对应 spec 中。 */ +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); + +const PAIRS = [ + ['testcase/基础管理-手术套包设置-基础包维护-UI自动化测试用例-01.csv', 'tests/spd/surgery-base-pkg.spec.ts'], + ['testcase/基础管理-手术套包设置-术种维护-UI自动化测试用例-01.csv', 'tests/spd/surgery-oper-type.spec.ts'], + ['testcase/基础管理-手术套包设置-标准套包维护-UI自动化测试用例-01.csv', 'tests/spd/surgery-std-pkg.spec.ts'], +]; + +function parseCsv(text) { + const rows = []; + let row = []; + let cell = ''; + let i = 0; + let quoted = false; + const pushCell = () => { + row.push(cell); + cell = ''; + }; + const pushRow = () => { + if (row.length) rows.push(row); + row = []; + }; + while (i < text.length) { + const ch = text[i]; + if (quoted) { + if (ch === '"' && text[i + 1] === '"') { + cell += '"'; + i += 2; + continue; + } + if (ch === '"') { + quoted = false; + i += 1; + continue; + } + cell += ch; + i += 1; + continue; + } + if (ch === '"') { + quoted = true; + i += 1; + continue; + } + if (ch === ',') { + pushCell(); + i += 1; + continue; + } + if (ch === '\n' || ch === '\r') { + if (ch === '\r' && text[i + 1] === '\n') i += 1; + pushCell(); + pushRow(); + i += 1; + continue; + } + cell += ch; + i += 1; + } + if (cell || row.length) { + pushCell(); + pushRow(); + } + return rows; +} + +const missing = []; +let total = 0; +for (const [csvRel, specRel] of PAIRS) { + const csvText = readFileSync(path.join(ROOT, csvRel), 'utf8').replace(/^\uFEFF/, ''); + const spec = readFileSync(path.join(ROOT, specRel), 'utf8'); + const table = parseCsv(csvText); + const header = table[0] || []; + const idIdx = header.indexOf('用例编号'); + const titleIdx = header.indexOf('用例标题'); + const pointIdx = header.indexOf('功能测试点'); + const modIdx = header.indexOf('功能模块'); + for (const cols of table.slice(1)) { + if (!cols[idIdx]) continue; + total += 1; + const title = `${cols[idIdx]} ${cols[titleIdx]}`; + if (!spec.includes(title)) missing.push(`${specRel}: ${title}`); + if (cols[pointIdx] && !spec.includes(cols[pointIdx])) { + missing.push(`${specRel}: describe ${cols[pointIdx]}`); + } + if (cols[modIdx] && !spec.includes(cols[modIdx])) { + missing.push(`${specRel}: module ${cols[modIdx]}`); + } + } +} + +if (missing.length) { + console.error('[gate] 手术套包设置 CSV 与 spec 不一致:'); + for (const item of missing) console.error(` ${item}`); + process.exit(1); +} +console.log(`[gate] 手术套包设置 CSV ↔ spec 一致(${total} 条)`); diff --git a/scripts/assert-user-csv-spec-sync.mjs b/scripts/assert-user-csv-spec-sync.mjs new file mode 100644 index 0000000..9ea5e8a --- /dev/null +++ b/scripts/assert-user-csv-spec-sync.mjs @@ -0,0 +1,103 @@ +#!/usr/bin/env node +/** 用户管理 CSV 用例编号+标题+功能测试点+功能模块 必须出现在对应 spec 中。 */ +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); + +const PAIRS = [ + ['testcase/基础管理-用户管理-用户管理-UI自动化测试用例-01.csv', 'tests/spd/basic-user.spec.ts'], + ['testcase/基础管理-用户管理-医共体用户管理-UI自动化测试用例-01.csv', 'tests/spd/basic-user-union.spec.ts'], + ['testcase/基础管理-用户管理-角色管理-UI自动化测试用例-01.csv', 'tests/spd/basic-role.spec.ts'], +]; + +function parseCsv(text) { + const rows = []; + let row = []; + let cell = ''; + let i = 0; + let quoted = false; + const pushCell = () => { + row.push(cell); + cell = ''; + }; + const pushRow = () => { + if (row.length) rows.push(row); + row = []; + }; + while (i < text.length) { + const ch = text[i]; + if (quoted) { + if (ch === '"' && text[i + 1] === '"') { + cell += '"'; + i += 2; + continue; + } + if (ch === '"') { + quoted = false; + i += 1; + continue; + } + cell += ch; + i += 1; + continue; + } + if (ch === '"') { + quoted = true; + i += 1; + continue; + } + if (ch === ',') { + pushCell(); + i += 1; + continue; + } + if (ch === '\n' || ch === '\r') { + if (ch === '\r' && text[i + 1] === '\n') i += 1; + pushCell(); + pushRow(); + i += 1; + continue; + } + cell += ch; + i += 1; + } + if (cell || row.length) { + pushCell(); + pushRow(); + } + return rows; +} + +const missing = []; +let total = 0; +for (const [csvRel, specRel] of PAIRS) { + const csvText = readFileSync(path.join(ROOT, csvRel), 'utf8').replace(/^\uFEFF/, ''); + const spec = readFileSync(path.join(ROOT, specRel), 'utf8'); + const table = parseCsv(csvText); + const header = table[0] || []; + const idIdx = header.indexOf('用例编号'); + const titleIdx = header.indexOf('用例标题'); + const pointIdx = header.indexOf('功能测试点'); + const modIdx = header.indexOf('功能模块'); + for (const cols of table.slice(1)) { + if (!cols[idIdx]) continue; + total += 1; + const title = `${cols[idIdx]} ${cols[titleIdx]}`; + if (!spec.includes(title)) missing.push(`${specRel}: ${title}`); + if (cols[pointIdx] && !spec.includes(cols[pointIdx])) { + missing.push(`${specRel}: describe ${cols[pointIdx]}`); + } + if (cols[modIdx] && !spec.includes(cols[modIdx])) { + missing.push(`${specRel}: module ${cols[modIdx]}`); + } + } +} + +if (missing.length) { + console.error('[gate] 用户管理 CSV 与 spec 不一致:'); + for (const item of missing) console.error(` ${item}`); + process.exit(1); +} +console.log(`[gate] 用户管理 CSV ↔ spec 一致(${total} 条)`); diff --git a/scripts/assert-vendor-csv-spec-sync.mjs b/scripts/assert-vendor-csv-spec-sync.mjs new file mode 100644 index 0000000..40cb83b --- /dev/null +++ b/scripts/assert-vendor-csv-spec-sync.mjs @@ -0,0 +1,110 @@ +#!/usr/bin/env node +/** 厂商管理 CSV 用例编号+标题+功能测试点 必须出现在对应 spec 中。 */ +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); + +const PAIRS = [ + ['testcase/基础管理-厂商管理-厂商查询-UI自动化测试用例-01.csv', 'tests/spd/vendor-query.spec.ts'], + ['testcase/基础管理-厂商管理-供应商资质管理-UI自动化测试用例-01.csv', 'tests/spd/vendor-qual.spec.ts'], + ['testcase/基础管理-厂商管理-授权变更初审-UI自动化测试用例-01.csv', 'tests/spd/vendor-audit.spec.ts'], + ['testcase/基础管理-厂商管理-授权变更终审-UI自动化测试用例-01.csv', 'tests/spd/vendor-audit.spec.ts'], + ['testcase/基础管理-厂商管理-注册证变更初审-UI自动化测试用例-01.csv', 'tests/spd/vendor-audit.spec.ts'], + ['testcase/基础管理-厂商管理-注册证变更终审-UI自动化测试用例-01.csv', 'tests/spd/vendor-audit.spec.ts'], + ['testcase/基础管理-厂商管理-注册证差异对比-UI自动化测试用例-01.csv', 'tests/spd/vendor-cert-diff.spec.ts'], + ['testcase/基础管理-厂商管理-评价方案维护-UI自动化测试用例-01.csv', 'tests/spd/vendor-eval-scheme.spec.ts'], + ['testcase/基础管理-厂商管理-评价指标维护-UI自动化测试用例-01.csv', 'tests/spd/vendor-eval-indicator.spec.ts'], + ['testcase/基础管理-厂商管理-供应商节假日管理-UI自动化测试用例-01.csv', 'tests/spd/vendor-holiday.spec.ts'], +]; + +function parseCsv(text) { + const rows = []; + let row = []; + let cell = ''; + let i = 0; + let quoted = false; + const pushCell = () => { + row.push(cell); + cell = ''; + }; + const pushRow = () => { + if (row.length) rows.push(row); + row = []; + }; + while (i < text.length) { + const ch = text[i]; + if (quoted) { + if (ch === '"' && text[i + 1] === '"') { + cell += '"'; + i += 2; + continue; + } + if (ch === '"') { + quoted = false; + i += 1; + continue; + } + cell += ch; + i += 1; + continue; + } + if (ch === '"') { + quoted = true; + i += 1; + continue; + } + if (ch === ',') { + pushCell(); + i += 1; + continue; + } + if (ch === '\n' || ch === '\r') { + if (ch === '\r' && text[i + 1] === '\n') i += 1; + pushCell(); + pushRow(); + i += 1; + continue; + } + cell += ch; + i += 1; + } + if (cell || row.length) { + pushCell(); + pushRow(); + } + return rows; +} + +const missing = []; +let total = 0; +for (const [csvRel, specRel] of PAIRS) { + const csvText = readFileSync(path.join(ROOT, csvRel), 'utf8').replace(/^\uFEFF/, ''); + const spec = readFileSync(path.join(ROOT, specRel), 'utf8'); + const table = parseCsv(csvText); + const header = table[0] || []; + const idIdx = header.indexOf('用例编号'); + const titleIdx = header.indexOf('用例标题'); + const pointIdx = header.indexOf('功能测试点'); + const modIdx = header.indexOf('功能模块'); + for (const cols of table.slice(1)) { + if (!cols[idIdx]) continue; + total += 1; + const title = `${cols[idIdx]} ${cols[titleIdx]}`; + if (!spec.includes(title)) missing.push(`${specRel}: ${title}`); + if (cols[pointIdx] && !spec.includes(cols[pointIdx])) { + missing.push(`${specRel}: describe ${cols[pointIdx]}`); + } + if (cols[modIdx] && !spec.includes(cols[modIdx])) { + missing.push(`${specRel}: module ${cols[modIdx]}`); + } + } +} + +if (missing.length) { + console.error('[gate] 厂商 CSV 与 spec 不一致:'); + for (const item of missing) console.error(` ${item}`); + process.exit(1); +} +console.log(`[gate] 厂商 CSV ↔ spec 一致(${total} 条)`); diff --git a/scripts/assert-vendor-no-reuse-open.mjs b/scripts/assert-vendor-no-reuse-open.mjs new file mode 100644 index 0000000..132ab2f --- /dev/null +++ b/scripts/assert-vendor-no-reuse-open.mjs @@ -0,0 +1,29 @@ +#!/usr/bin/env node +/** + * 基础管理新模块(厂商 / 准入 / 医院资料 / 库房 / 流程)禁止 describeWithReuseOpen: + * 必须走 auth.setup + storageState + 每用例独立 context。 + */ +import { readdirSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const DIR = path.join(ROOT, 'tests', 'spd'); +const PREFIXES = ['vendor-', 'admit-', 'basic-', 'warehouse-', 'flow-']; + +const offenders = []; +for (const name of readdirSync(DIR)) { + if (!name.endsWith('.spec.ts')) continue; + if (!PREFIXES.some((p) => name.startsWith(p))) continue; + const text = readFileSync(path.join(DIR, name), 'utf8'); + if (text.includes('describeWithReuseOpen')) { + offenders.push(name); + } +} + +if (offenders.length) { + console.error('[gate] 基础管理新模块禁止 describeWithReuseOpen:'); + for (const f of offenders) console.error(` ${f}`); + process.exit(1); +} +console.log('[gate] vendor/admit/basic/warehouse/flow 均未使用 describeWithReuseOpen'); diff --git a/scripts/assert-wh-flow-csv-spec-sync.mjs b/scripts/assert-wh-flow-csv-spec-sync.mjs new file mode 100644 index 0000000..20968ce --- /dev/null +++ b/scripts/assert-wh-flow-csv-spec-sync.mjs @@ -0,0 +1,104 @@ +#!/usr/bin/env node +/** 库房管理 / 流程设置 CSV 用例编号+标题+功能测试点+功能模块 必须出现在对应 spec 中。 */ +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); + +const PAIRS = [ + ['testcase/基础管理-库房管理-库区设置-UI自动化测试用例-01.csv', 'tests/spd/warehouse-zone.spec.ts'], + ['testcase/基础管理-库房管理-探针绑定-UI自动化测试用例-01.csv', 'tests/spd/warehouse-probe.spec.ts'], + ['testcase/基础管理-流程设置-审批流程设置-UI自动化测试用例-01.csv', 'tests/spd/flow-def.spec.ts'], + ['testcase/基础管理-流程设置-审批流实例-UI自动化测试用例-01.csv', 'tests/spd/flow-inst.spec.ts'], +]; + +function parseCsv(text) { + const rows = []; + let row = []; + let cell = ''; + let i = 0; + let quoted = false; + const pushCell = () => { + row.push(cell); + cell = ''; + }; + const pushRow = () => { + if (row.length) rows.push(row); + row = []; + }; + while (i < text.length) { + const ch = text[i]; + if (quoted) { + if (ch === '"' && text[i + 1] === '"') { + cell += '"'; + i += 2; + continue; + } + if (ch === '"') { + quoted = false; + i += 1; + continue; + } + cell += ch; + i += 1; + continue; + } + if (ch === '"') { + quoted = true; + i += 1; + continue; + } + if (ch === ',') { + pushCell(); + i += 1; + continue; + } + if (ch === '\n' || ch === '\r') { + if (ch === '\r' && text[i + 1] === '\n') i += 1; + pushCell(); + pushRow(); + i += 1; + continue; + } + cell += ch; + i += 1; + } + if (cell || row.length) { + pushCell(); + pushRow(); + } + return rows; +} + +const missing = []; +let total = 0; +for (const [csvRel, specRel] of PAIRS) { + const csvText = readFileSync(path.join(ROOT, csvRel), 'utf8').replace(/^\uFEFF/, ''); + const spec = readFileSync(path.join(ROOT, specRel), 'utf8'); + const table = parseCsv(csvText); + const header = table[0] || []; + const idIdx = header.indexOf('用例编号'); + const titleIdx = header.indexOf('用例标题'); + const pointIdx = header.indexOf('功能测试点'); + const modIdx = header.indexOf('功能模块'); + for (const cols of table.slice(1)) { + if (!cols[idIdx]) continue; + total += 1; + const title = `${cols[idIdx]} ${cols[titleIdx]}`; + if (!spec.includes(title)) missing.push(`${specRel}: ${title}`); + if (cols[pointIdx] && !spec.includes(cols[pointIdx])) { + missing.push(`${specRel}: describe ${cols[pointIdx]}`); + } + if (cols[modIdx] && !spec.includes(cols[modIdx])) { + missing.push(`${specRel}: module ${cols[modIdx]}`); + } + } +} + +if (missing.length) { + console.error('[gate] 库房/流程 CSV 与 spec 不一致:'); + for (const item of missing) console.error(` ${item}`); + process.exit(1); +} +console.log(`[gate] 库房/流程 CSV ↔ spec 一致(${total} 条)`); diff --git a/scripts/ci-email-summary.mjs b/scripts/ci-email-summary.mjs index e923271..b8fdd5f 100644 --- a/scripts/ci-email-summary.mjs +++ b/scripts/ci-email-summary.mjs @@ -77,11 +77,30 @@ function suiteScope(summary) { } } +/** 读取跨构建失败频次统计,返回「经常失败」的用例(前 N 条)。 */ +function loadFrequentFailures(limit = 10) { + const store = loadJson(path.join(ROOT, 'reports', 'flake', 'failure-counts.json')); + if (!store?.items) return { total: 0, rows: [] }; + const minFails = Number(store.frequentMinFails ?? 2); + const rate = Number(store.frequentRate ?? 0.2); + const rows = Object.values(store.items) + .map((s) => ({ ...s, rate: s.runs > 0 ? s.failed / s.runs : 0 })) + .filter((s) => s.failed >= minFails && s.rate >= rate) + .sort((a, b) => b.failed - a.failed || b.rate - a.rate); + return { total: rows.length, rows: rows.slice(0, limit) }; +} + function formatPassRate(passed, executed) { if (!executed) return '-'; return `${((passed / executed) * 100).toFixed(1)}%`; } +/** 去掉 STAT- 编号前缀,标题过长时截断。 */ +function shortTestName(title, max = 60) { + const name = String(title ?? '').replace(/^STAT-[A-Z]+-\d{3,}\s*/, ''); + return name.length > max ? `${name.slice(0, max - 1)}…` : name; +} + function statusLabel(summary, jenkinsResult) { if (jenkinsResult && jenkinsResult !== 'SUCCESS' && jenkinsResult !== 'UNSTABLE') { if (jenkinsResult === 'ABORTED') return { text: '已中止', color: '#6b7280' }; @@ -120,6 +139,7 @@ function main() { const duration = formatDuration(summary.durationMs); const failed = parseFailedTests(path.join(LATEST, 'junit.xml')); const moreFail = failTotal > failed.length ? failTotal - failed.length : 0; + const frequent = loadFrequentFailures(10); const pwReportUrl = buildUrl ? `${buildUrl}Playwright_20Report/` : ''; const allureUrl = buildUrl ? `${buildUrl}allure` : ''; const viewUser = process.env.REPORT_VIEW_USER || ''; @@ -162,6 +182,28 @@ function main() { .join('') : `无失败用例`; + const freqRows = frequent.rows + .map( + (item) => ` + ${escapeHtml(item.id)} + ${item.failed}/${item.runs}(${(item.rate * 100).toFixed(0)}%) + ${escapeHtml(shortTestName(item.title))} + `, + ) + .join(''); + const frequentSection = + frequent.total > 0 + ? `

经常失败的用例(累计 ${frequent.total} 条,仅列前 ${frequent.rows.length} 条)

+

跨构建累计,失败次数与失败率双达标;建议优先修脚本或提单。

+ + + + + + ${freqRows} +
编号失败/执行用例
` + : ''; + const html = ` SPD UI 自动化报告 @@ -190,6 +232,7 @@ function main() { ${reportButtons}

失败用例${moreFail ? `(仅列出前 ${failed.length} 条,另有 ${moreFail} 条)` : ''}

${failRows}
+ ${frequentSection}

完整截图与 Trace 请点上面的 Playwright 报告。此邮件由定时任务自动发送。

@@ -214,6 +257,11 @@ function main() { `耗时: ${duration}`, viewUser ? `报告只读账号: ${viewUser}` : '', viewPass ? `报告只读密码: ${viewPass}` : '', + frequent.total > 0 + ? `经常失败的用例(累计 ${frequent.total} 条): ${frequent.rows + .map((r) => `${r.id}(${r.failed}/${r.runs})`) + .join(', ')}` + : '', pwReportUrl ? `Playwright 报告: ${pwReportUrl}` : '', allureUrl ? `Allure 报告: ${allureUrl}` : '', '', diff --git a/scripts/failure-report.mjs b/scripts/failure-report.mjs new file mode 100644 index 0000000..ac4b324 --- /dev/null +++ b/scripts/failure-report.mjs @@ -0,0 +1,84 @@ +#!/usr/bin/env node +/** + * 失败频次榜单:读取 reports/flake/failure-counts.json,列出「经常失败」的用例。 + * + * 数据来源:用例 afterEach 时按 STAT- 编号累计(见 core/failure-tracker.ts), + * reporter onEnd 刷新 reports/flake/frequent-failures.md。 + * + * 用法: + * node scripts/failure-report.mjs + * node scripts/failure-report.mjs --min-fails=2 --rate=0.2 + * node scripts/failure-report.mjs --top=30 --json + */ +import { existsSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); + +function parseArgs(argv) { + const out = { + store: path.join(ROOT, 'reports/flake/failure-counts.json'), + minFails: Number(process.env.FREQ_FAIL_MIN || 2), + rate: Number(process.env.FREQ_FAIL_RATE || 0.2), + top: 30, + json: false, + }; + for (const arg of argv) { + if (arg === '--json') out.json = true; + else if (arg.startsWith('--store=')) out.store = path.resolve(arg.slice(8)); + else if (arg.startsWith('--min-fails=')) out.minFails = Number(arg.slice(12)) || out.minFails; + else if (arg.startsWith('--rate=')) out.rate = Number(arg.slice(7)) || out.rate; + else if (arg.startsWith('--top=')) out.top = Number(arg.slice(6)) || out.top; + } + return out; +} + +function shortTitle(title, max = 60) { + const name = String(title || '').replace(/^STAT-[A-Z]+-\d{3,}\s*/, ''); + return name.length > max ? `${name.slice(0, max - 1)}…` : name; +} + +function main() { + const opts = parseArgs(process.argv.slice(2)); + if (!existsSync(opts.store)) { + console.error(`[failure-report] 未找到 ${path.relative(ROOT, opts.store)}`); + console.error('[failure-report] 先跑一次 CI/全量,用例结果会自动累计到这里。'); + process.exit(2); + } + + const store = JSON.parse(readFileSync(opts.store, 'utf8')); + const items = Object.values(store.items || {}); + const rows = items + .map((s) => ({ + ...s, + rate: s.runs > 0 ? s.failed / s.runs : 0, + })) + .filter((s) => s.failed >= opts.minFails && s.rate >= opts.rate) + .sort((a, b) => b.failed - a.failed || b.rate - a.rate); + + if (opts.json) { + console.log(JSON.stringify({ minFails: opts.minFails, rate: opts.rate, count: rows.length, rows }, null, 2)); + return; + } + + console.log( + `[failure-report] 累计 ${items.length} 个用例;经常失败(失败 ≥ ${opts.minFails} 且失败率 ≥ ${opts.rate})共 ${rows.length} 条:`, + ); + if (!rows.length) { + console.log(' (暂无,需多次构建累计)'); + return; + } + console.log(' 编号 失败/执行 失败率 连续 最近分类 用例'); + for (const r of rows.slice(0, opts.top)) { + console.log( + ` ${String(r.id).padEnd(14)} ${String(`${r.failed}/${r.runs}`).padEnd(9)} ${String( + `${(r.rate * 100).toFixed(0)}%`, + ).padEnd(6)} ${String(r.consecutiveFails ?? '-').padEnd(5)} ${String(r.lastClass ?? '-').padEnd(12)} ${shortTitle( + r.title, + )}`, + ); + } +} + +main(); diff --git a/scripts/generate-surgery-csv.py b/scripts/generate-surgery-csv.py new file mode 100644 index 0000000..7876787 --- /dev/null +++ b/scripts/generate-surgery-csv.py @@ -0,0 +1,515 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""Generate surgery testcase CSVs (9 fixed columns, RFC4180 quoting, UTF-8 no BOM).""" +import csv +import io +import os +import re + +HEADER = [ + "用例编号", "功能模块", "功能测试点", "用例标题", "优先级", + "前置条件", "测试步骤", "预期结果", "实际结果", +] + +LOGIN_NOTE = "" +MOCK_NOTE = "" + +LOGIN_OK = "已通过 auth.setup 写入 storageState 登录" +PRE_URL_BASE = "" +MODULE = "基础设置 · 手术套包设置" +MENU = "基础设置 → 手术套包设置" + + +def row(cid, point, title, pr, pre, steps, expect, mock=False): + return [cid, MODULE, point, title, pr, pre, steps, expect, ""] + + +def _renumber(steps: str) -> str: + """去掉与执行器相关的开场步骤(LOGIN_NOTE/MOCK_NOTE),并按 1..N 重新编号。""" + out = [] + for ln in steps.split("\n"): + body = re.sub(r"^\d+\.\s*", "", ln).strip() + if not body: + continue + if body.startswith("确认 chromium") or body.startswith("使用 mock 反例数据"): + continue + out.append(body) + return "\n".join(f"{i}. {b}" for i, b in enumerate(out, 1)) + + +def build(cases, url): + out = io.StringIO() + w = csv.writer(out, quoting=csv.QUOTE_ALL, lineterminator="\n") + w.writerow(HEADER) + for c in cases: + c = [re.sub(r"§([A-Za-z0-9_]+)§", r"${\1}", cell) for cell in c] + c[5] = c[5].replace(f"{LOGIN_OK} 已登录", LOGIN_OK) + # 未登录反例:前置补目标 URL(相对路径 + 无 iframe) + if "无登录态" in c[5] and "已进入" not in c[5]: + c[5] = c[5] + f";直接访问{c[2].split(' · ')[-1]} URL({url},无 iframe)" + c[6] = _renumber(c[6]) + w.writerow(c) + return out.getvalue() + + +# ---------------- 基础包维护 ---------------- +BASE_PATH = "/spd/basic/operationSetting/baseMaintenance" +BASE_URL = BASE_PATH +base = [] + + +def bp(cid, point, title, pr, pre, steps, expect): + base.append(row(cid, point, title, pr, pre, steps, expect)) + + +B_PRE = f"测试环境可访问;{LOGIN_OK};具备「基础设置 → 手术套包设置」权限" +B_PAGE = f"{B_PRE};已进入基础包维护页({BASE_PATH},无 iframe)" +B_SAMPLE = f"{B_PAGE};默认列表至少 1 条数据" + +bp( + "SURG-BASE-001", "基础包维护 · 左侧分类树", "分类名称搜索过滤", "高", B_PAGE, + f"{LOGIN_NOTE}\n2. 从左树读取 `§sampleCategoryName§`\n3. 在分类名称搜索框填入该关键字(即时过滤或回车,以现网为准)\n4. 断言树节点文本均包含该关键字", + "过滤后树节点名称与关键字匹配;无未捕获异常。", +) +bp( + "SURG-BASE-002", "基础包维护 · 左侧分类树", "选择分类联动右侧列表", "高", B_SAMPLE, + f"{LOGIN_NOTE}\n2. 点击 `§sampleCategoryName§` 分类节点\n3. 等待右侧列表刷新稳定\n4. 断言列表加载无报错,分页「共 N 条」与当前分类规模一致", + "右侧仅展示该分类下基础包;总数与筛选一致。", +) +bp( + "SURG-BASE-003", "基础包维护 · 左侧分类树", "打开新增分类弹窗", "中", B_PAGE, + f"{LOGIN_NOTE}\n2. 点击「新增分类」\n3. 断言弹窗可见,含分类名称输入(#form_item_kindName)与「取 消」「确 认」\n4. 点击「取 消」关闭", + "新增分类弹窗字段完整;取消可关闭。", +) +bp( + "SURG-BASE-004", "基础包维护 · 左侧分类树", "分类搜索清空恢复全量", "低", B_PAGE, + f"{LOGIN_NOTE}\n2. 用 `§sampleCategoryName§` 过滤并记录过滤后节点数\n3. 清空分类搜索框\n4. 断言节点数回到未过滤的默认集合(或不少于过滤后节点数)", + "清空后分类树恢复默认集合。", +) +bp( + "SURG-BASE-005", "基础包维护 · 右侧筛选与列表", "默认列出基础包或空态明确", "高", B_PAGE, + f"{LOGIN_NOTE}\n2. 等待列表稳定\n3. 断言出现数据行,或「暂无数据」且分页总数为 0", + "加载无报错;有数据时「共 N 条」N≥1;无数据时空态明确。", +) +bp( + "SURG-BASE-006", "基础包维护 · 右侧筛选与列表", "产品名称筛选过滤", "高", B_SAMPLE, + f"{LOGIN_NOTE}\n2. 从首行读取 `§sampleProductName§`\n3. 在 `#table_search_form_keyword` 填入该关键字并点击「查 询」\n4. 断言结果行产品名称均包含该关键字", + "过滤结果与关键字匹配;「共 N 条」随之更新。", +) +bp( + "SURG-BASE-007", "基础包维护 · 右侧筛选与列表", "规格型号筛选过滤", "高", B_SAMPLE, + f"{LOGIN_NOTE}\n2. 从首行读取 `§sampleSpecModel§`\n3. 在 `#table_search_form_goodsSpec` 填入该关键字并点击「查 询」\n4. 断言结果行规格型号均包含该关键字", + "按规格型号过滤生效。", +) +bp( + "SURG-BASE-008", "基础包维护 · 右侧筛选与列表", "器械注册人筛选过滤", "高", B_SAMPLE, + f"{LOGIN_NOTE}\n2. 从首行读取 `§sampleMfrsName§`\n3. 在 `#table_search_form_goodsMfrsName` 填入该关键字并点击「查 询」\n4. 断言结果行器械注册人均包含该关键字", + "按器械注册人过滤生效。", +) +bp( + "SURG-BASE-009", "基础包维护 · 右侧筛选与列表", "列表核心列完整", "高", B_SAMPLE, + f"{LOGIN_NOTE}\n2. 断言表头包含序号、产品编号、产品名称、规格型号、器械注册人、单位、产地、耗材类型、操作", + "核心列均可见,操作列含「编 辑」「移 除」。", +) +bp( + "SURG-BASE-010", "基础包维护 · 右侧筛选与列表", "查询与重置", "中", B_SAMPLE, + f"{LOGIN_NOTE}\n2. 用关键字过滤后点击「重 置」\n3. 断言筛选输入框被清空、列表恢复默认集合", + "重置清空筛选并恢复默认列表。", +) +bp( + "SURG-BASE-011", "基础包维护 · 右侧筛选与列表", "分页与页大小切换", "中", + f"{B_SAMPLE};列表总数大于单页行数上限", + f"{LOGIN_NOTE}\n2. 切换每页条数(如 50 → 100)\n3. 断言单页行数不超过所选值,分页总数不变", + "页大小切换生效;总数保持稳定。", +) +bp( + "SURG-BASE-012", "基础包维护 · 右侧筛选与列表", "首页上一页不可用", "低", B_PAGE, + f"{LOGIN_NOTE}\n2. 处于第 1 页\n3. 断言「上一页」置灰或点击无效果", + "第 1 页无法回到上一页。", +) +bp( + "SURG-BASE-013", "基础包维护 · 右侧筛选与列表", "搜索无匹配空态(mock反例)", "中", B_PAGE, + f"{MOCK_NOTE}\n2. 在 `#table_search_form_keyword` 输入 mock 不存在关键字 `__NOT_EXIST_BASE__99999`\n3. 点击「查 询」\n4. 断言「暂无数据」且分页总数为 0", + "无匹配时空态明确,不白屏;总数为 0。", +) +bp( + "SURG-BASE-014", "基础包维护 · 新增基础包", "打开新增基础包弹窗", "高", B_PAGE, + f"{LOGIN_NOTE}\n2. 点击「新增基础包」\n3. 断言弹窗可见,含基础包名称(#form_item_pkgName)、所属分类(#form_item_kindId)、包编码(#form_item_pkgCode)、备注(#form_item_remark)\n4. 断言含「取 消」「确 认」", + "新增基础包弹窗字段完整(名称/所属分类/包编码/备注)。", +) +bp( + "SURG-BASE-015", "基础包维护 · 新增基础包", "新增取消不落库", "高", B_PAGE, + f"{LOGIN_NOTE}\n2. 记录当前分页总数 `§sampleTotal§`\n3. 打开新增,填写名称 `§uniqPkgName§` 后点「取 消」\n4. 断言列表总数不变,且无该名称行", + "取消后不新增基础包。", +) +bp( + "SURG-BASE-016", "基础包维护 · 新增基础包", "合法必填新增成功", "高", + f"{B_PRE};已进入基础包维护页({BASE_PATH},无 iframe);允许写基础包;名称 `§uniqPkgName§`(建议 AUTO-BASE-§timestamp§)、编码 `§uniqPkgCode§`", + f"{LOGIN_NOTE}\n2. 打开新增,填写名称、所属分类、包编码\n3. 点击「确 认」\n4. 断言弹窗关闭,列表出现该名称行且字段一致\n5. 后置:按环境约定清理该基础包,避免污染主数据", + "新增成功且列表可见;用例结束清理,不留生产脏数据。", +) +bp( + "SURG-BASE-017", "基础包维护 · 新增基础包", "基础包名称为空拦截(mock反例)", "高", B_PAGE, + f"{MOCK_NOTE}\n2. 打开新增基础包弹窗,名称留空,补齐所属分类与包编码\n3. 点击「确 认」\n4. 断言必填提示,弹窗仍在,列表无新行", + "确定被拦截;不落库。", +) +bp( + "SURG-BASE-018", "基础包维护 · 新增基础包", "所属分类为空拦截(mock反例)", "高", B_PAGE, + f"{MOCK_NOTE}\n2. 打开新增基础包弹窗,填写名称与包编码,不选所属分类\n3. 点击「确 认」\n4. 断言必填提示,弹窗仍在", + "确定被拦截;不落库。", +) +bp( + "SURG-BASE-019", "基础包维护 · 新增基础包", "重复点击确定防重", "中", + f"{B_PRE};已进入基础包维护页({BASE_PATH},无 iframe);允许写基础包;使用唯一名称 `§uniqPkgName§`", + f"{LOGIN_NOTE}\n2. 打开新增弹窗并填写合法数据\n3. 快速连点「确 认」两次\n4. 断言只产生一条基础包(或无重复名称/编码行)", + "连点只落一条;无重复脏数据。", +) +bp( + "SURG-BASE-020", "基础包维护 · 行内操作(编辑 / 移除)", "行内编辑回填", "高", B_SAMPLE, + f"{LOGIN_NOTE}\n2. 点击首行「编 辑」\n3. 断言编辑弹窗可见且回填当前行数据\n4. 点击「取 消」关闭", + "编辑弹窗回填正确;取消不影响数据。", +) +bp( + "SURG-BASE-021", "基础包维护 · 行内操作(编辑 / 移除)", "移除二次确认防误删", "高", B_SAMPLE, + f"{LOGIN_NOTE}\n2. 记录当前总数 `§sampleTotal§`\n3. 点击首行「移 除」\n4. 若出现确认框则点「取 消」\n5. 断言总数仍为 `§sampleTotal§`", + "移除有二次确认;取消不删除。", +) +bp( + "SURG-BASE-022", "基础包维护 · 行内操作(编辑 / 移除)", "新增基础包可在标准套包被引用", "中", + f"{B_PRE};已进入基础包维护页({BASE_PATH},无 iframe);已新增 `§uniqPkgName§` 且未清理;允许跨页只读访问", + f"{LOGIN_NOTE}\n2. 进入「标准套包维护」,点击「添加基础包」\n3. 在弹窗搜索框输入 `§uniqPkgName§`\n4. 断言可检索到该基础包并可勾选\n5. 后置:清理该基础包", + "新增基础包在标准套包可选;跨页数据一致。", +) + +# ---------------- 术种维护 ---------------- +TYPE_PATH = "/spd/basic/operationSetting/typeMaintenance" +TYPE_URL = TYPE_PATH +type_cases = [] + + +def tp(cid, point, title, pr, pre, steps, expect): + type_cases.append(row(cid, point, title, pr, pre, steps, expect)) + + +T_PRE = f"测试环境可访问;{LOGIN_OK};具备「基础设置 → 手术套包设置」权限" +T_PAGE = f"{T_PRE};已进入术种维护页({TYPE_PATH},无 iframe)" +T_SAMPLE = f"{T_PAGE};默认列表至少 1 条数据" + +tp( + "SURG-TYPE-001", "术种维护 · 页面进入", "菜单进入术种维护页", "高", T_PAGE, + f"{LOGIN_NOTE}\n2. 按菜单「{MENU} → 术种维护」进入\n3. 断言 URL 含 `/spd/basic/operationSetting/typeMaintenance`\n4. 断言左侧分类树(术种分类名称搜索框)、右侧筛选区、状态 Tab、列表与分页可见", + "进入术种维护页;不误入基础包维护/标准套包维护;左右分栏渲染完整。", +) +tp( + "SURG-TYPE-002", "术种维护 · 页面进入", "同组页签切换后返回", "中", T_PAGE, + f"{LOGIN_NOTE}\n2. 点击页内 Tab「标准套包维护」,等待 URL 切换\n3. 返回点击 Tab「术种维护」\n4. 断言 URL 回到 typeMaintenance,左树与状态 Tab 重新渲染", + "三页签互切正常;返回后本页数据恢复。", +) +tp( + "SURG-TYPE-003", "术种维护 · 页面进入", "未登录访问拦截", "高", + "测试环境可访问;使用 test.use({ storageState: { cookies: [], origins: [] } }) 无登录态", + f"1. 不加载登录态(空 storageState)\n2. 直接访问 `{TYPE_URL}`\n3. 断言跳转登录页或不展示术种列表数据", + "未登录被拦截,不泄露术种数据。", +) +tp( + "SURG-TYPE-004", "术种维护 · 左侧术种分类树", "术种分类名称搜索过滤", "高", T_PAGE, + f"{LOGIN_NOTE}\n2. 从左树读取 `§sampleOperCategoryName§`\n3. 在分类名称搜索框填入该关键字(即时过滤或回车,以现网为准)\n4. 断言树节点文本均包含该关键字", + "过滤后树节点名称与关键字匹配;无未捕获异常。", +) +tp( + "SURG-TYPE-005", "术种维护 · 左侧术种分类树", "选择分类联动右侧列表", "高", T_SAMPLE, + f"{LOGIN_NOTE}\n2. 点击 `§sampleOperCategoryName§` 分类节点\n3. 等待右侧列表刷新稳定\n4. 断言列表加载无报错,分页「共 N 条」与当前分类规模一致", + "右侧仅展示该分类下术种;总数与筛选一致。", +) +tp( + "SURG-TYPE-006", "术种维护 · 左侧术种分类树", "分类树计数与下钻", "中", + f"{T_PAGE};至少 1 个带计数的分类节点", + f"{LOGIN_NOTE}\n2. 读取某分类节点括号内计数 `§nodeCount§`\n3. 点击该节点\n4. 断言右侧「共 N 条」与 `§nodeCount§` 一致", + "分类计数与该分类下术种条数一致。", +) +tp( + "SURG-TYPE-007", "术种维护 · 左侧术种分类树", "打开分类维护弹窗", "中", T_PAGE, + f"{LOGIN_NOTE}\n2. 点击左侧「新增」\n3. 断言弹窗可见,含分类名称输入(#basic_kindName)与「取 消」「保 存」\n4. 点击「取 消」关闭", + "分类维护弹窗字段完整;取消可关闭。", +) +tp( + "SURG-TYPE-008", "术种维护 · 左侧术种分类树", "分类搜索清空恢复全量", "低", T_PAGE, + f"{LOGIN_NOTE}\n2. 用 `§sampleOperCategoryName§` 过滤并记录过滤后节点数\n3. 清空分类搜索框\n4. 断言节点数回到未过滤的默认集合", + "清空后分类树恢复默认集合。", +) +tp( + "SURG-TYPE-009", "术种维护 · 筛选与状态 Tab", "术种名称筛选过滤", "高", T_SAMPLE, + f"{LOGIN_NOTE}\n2. 从首行读取 `§sampleOperName§`\n3. 在 `#table_search_form_operationName` 填入该关键字并点击「查 询」\n4. 断言结果行术种名称均包含该关键字", + "过滤结果与关键字匹配;「共 N 条」随之更新。", +) +tp( + "SURG-TYPE-010", "术种维护 · 筛选与状态 Tab", "病种名称筛选过滤", "高", T_SAMPLE, + f"{LOGIN_NOTE}\n2. 从首行读取 `§sampleDiseaseName§`\n3. 在 `#table_search_form_disease` 填入该关键字并点击「查 询」\n4. 断言结果行病种名称均包含该关键字", + "按病种名称过滤生效。", +) +tp( + "SURG-TYPE-011", "术种维护 · 筛选与状态 Tab", "状态 Tab 切换与计数一致", "高", T_PAGE, + f"{LOGIN_NOTE}\n2. 读取「全部」Tab 括号计数 `§totalCount§`\n3. 依次点击「停用」「未建分类」「全部」\n4. 每次断言列表条数与「共 N 条」和该 Tab 括号计数一致", + "Tab 切换后列表仅展示对应状态;括号计数与结果规模一致。", +) +tp( + "SURG-TYPE-012", "术种维护 · 筛选与状态 Tab", "左树 + 筛选 + Tab 组合过滤", "中", T_SAMPLE, + f"{LOGIN_NOTE}\n2. 选择 `§sampleOperCategoryName§` 分类节点\n3. 填入术种名称关键字并点「查 询」\n4. 切到「全部」Tab\n5. 断言结果为三者交集且无未捕获异常", + "三条件叠加后结果正确,总数一致。", +) +tp( + "SURG-TYPE-013", "术种维护 · 筛选与状态 Tab", "重置清空筛选", "中", T_SAMPLE, + f"{LOGIN_NOTE}\n2. 用关键字过滤后点击「重 置」\n3. 断言筛选输入框被清空、列表恢复默认集合", + "重置清空筛选并恢复默认列表。", +) +tp( + "SURG-TYPE-014", "术种维护 · 筛选与状态 Tab", "无匹配组合空态(mock反例)", "低", T_PAGE, + f"{MOCK_NOTE}\n2. 在 `#table_search_form_operationName` 输入 mock 不存在关键字 `__NOT_EXIST_OPER__99999`\n3. 点击「查 询」\n4. 断言「暂无数据」且分页总数为 0", + "无匹配时空态明确,不白屏;总数为 0。", +) +tp( + "SURG-TYPE-015", "术种维护 · 列表与编码展示", "列表核心列完整", "高", T_SAMPLE, + f"{LOGIN_NOTE}\n2. 断言表头包含序号、术种名称、病种名称、诊断编码(ICD-10)、诊断名称(ICD-10)、操作编码(ICD-9-CM-3)、操作名称(ICD-9-CM-3)、操作", + "核心列均可见,操作列含「编 辑」「停用/启用」。", +) +tp( + "SURG-TYPE-016", "术种维护 · 列表与编码展示", "ICD-10 诊断编码展示完整", "中", + f"{T_SAMPLE};至少 1 条含诊断编码的数据", + f"{LOGIN_NOTE}\n2. 读取含 ICD-10 编码的行 `§sampleIcd10Code§`\n3. 断言编码文本完整(如 A02.000x010)不截断、无乱码", + "ICD-10 编码完整展示。", +) +tp( + "SURG-TYPE-017", "术种维护 · 列表与编码展示", "ICD-9-CM-3 操作编码展示完整", "低", + f"{T_SAMPLE};至少 1 条含操作编码的数据", + f"{LOGIN_NOTE}\n2. 读取含操作编码的行 `§sampleIcd9Code§`\n3. 断言操作编码(如 00.0200)与操作名称(如 心脏治疗性超声)对应展示无乱码", + "ICD-9-CM-3 编码与操作名称对应正确。", +) +tp( + "SURG-TYPE-018", "术种维护 · 列表与编码展示", "分页与页大小切换", "中", + f"{T_SAMPLE};列表总数大于单页行数上限", + f"{LOGIN_NOTE}\n2. 切换每页条数(如 50 → 100)\n3. 断言单页行数不超过所选值,分页总数不变", + "页大小切换生效;总数保持稳定。", +) +tp( + "SURG-TYPE-019", "术种维护 · 列表与编码展示", "首页上一页不可用", "低", T_PAGE, + f"{LOGIN_NOTE}\n2. 处于第 1 页\n3. 断言「上一页」置灰或点击无效果", + "第 1 页无法回到上一页。", +) +tp( + "SURG-TYPE-020", "术种维护 · 新增术种", "打开新增术种入口", "高", T_PAGE, + f"{LOGIN_NOTE}\n2. 点击「新增术种」\n3. 断言新增弹窗/页面可见,含术种名称、病种名称、分类与编码相关字段\n4. 断言含「取 消」「保 存」", + "新增术种入口字段完整。", +) +tp( + "SURG-TYPE-021", "术种维护 · 新增术种", "新增取消不落库", "高", T_PAGE, + f"{LOGIN_NOTE}\n2. 记录当前分页总数 `§sampleTotal§`\n3. 打开新增,填写名称 `§uniqOperName§` 后点「取 消」\n4. 断言列表总数不变,且无该名称行", + "取消后不新增术种。", +) +tp( + "SURG-TYPE-022", "术种维护 · 新增术种", "术种名称为空拦截(mock反例)", "高", T_PAGE, + f"{MOCK_NOTE}\n2. 打开新增术种弹窗,名称留空\n3. 点击「保 存」\n4. 断言必填提示,弹窗仍在,列表无新行", + "保存被拦截;不落库。", +) +tp( + "SURG-TYPE-023", "术种维护 · 行内操作(编辑 / 停用)", "行内编辑回填", "高", T_SAMPLE, + f"{LOGIN_NOTE}\n2. 点击首行「编 辑」\n3. 断言编辑弹窗可见且回填当前术种数据\n4. 点击「取 消」关闭", + "编辑弹窗回填正确;取消不影响数据。", +) +tp( + "SURG-TYPE-024", "术种维护 · 行内操作(编辑 / 停用)", "停用状态流转", "中", + f"{T_PAGE};存在启用状态术种(优先选 `§uniqOperName§` 自建数据);允许写术种", + f"{LOGIN_NOTE}\n2. 记录「停用」Tab 当前计数 `§disabledCount§`\n3. 点击目标行「停用」,若有确认框则点「确 定」\n4. 断言「停用」Tab 计数 +1\n5. 后置:对该术种执行「启用」恢复", + "术种状态变为停用;「停用」计数 +1;结束恢复原状态。", +) +tp( + "SURG-TYPE-025", "术种维护 · 行内操作(编辑 / 停用)", "停用二次确认防误操作", "高", + f"{T_PAGE};存在启用状态术种", + f"{LOGIN_NOTE}\n2. 记录「停用」Tab 当前计数\n3. 点击首行「停用」\n4. 若出现确认框则点「取 消」\n5. 断言计数未变化", + "停用有二次确认;取消不改状态。", +) + +# ---------------- 标准套包维护 ---------------- +STD_PATH = "/spd/basic/operationSetting/standardMaintenance" +STD_URL = STD_PATH +std = [] + + +def sp(cid, point, title, pr, pre, steps, expect): + std.append(row(cid, point, title, pr, pre, steps, expect)) + + +S_PRE = f"测试环境可访问;{LOGIN_OK};具备「基础设置 → 手术套包设置」权限" +S_PAGE = f"{S_PRE};已进入标准套包维护页({STD_PATH},无 iframe)" +S_SAMPLE = f"{S_PAGE};默认列表至少 1 条数据" + +sp( + "SURG-STD-001", "标准套包维护 · 页面进入", "菜单进入标准套包维护页", "高", S_PAGE, + f"{LOGIN_NOTE}\n2. 按菜单「{MENU} → 标准套包维护」进入\n3. 断言 URL 含 `/spd/basic/operationSetting/standardMaintenance`\n4. 断言左侧分类树、右侧筛选区、构成列表与分页可见", + "进入标准套包维护页;不误入基础包维护/术种维护;左右分栏渲染完整。", +) +sp( + "SURG-STD-002", "标准套包维护 · 页面进入", "同组页签切换后返回", "中", S_PAGE, + f"{LOGIN_NOTE}\n2. 点击页内 Tab「术种维护」,等待 URL 切换\n3. 返回点击 Tab「标准套包维护」\n4. 断言 URL 回到 standardMaintenance,左树与构成列表重新渲染", + "三页签互切正常;返回后本页数据恢复。", +) +sp( + "SURG-STD-003", "标准套包维护 · 页面进入", "未登录访问拦截", "高", + "测试环境可访问;使用 test.use({ storageState: { cookies: [], origins: [] } }) 无登录态", + f"1. 不加载登录态(空 storageState)\n2. 直接访问 `{STD_URL}`\n3. 断言跳转登录页或不展示套包列表数据", + "未登录被拦截,不泄露套包数据。", +) +sp( + "SURG-STD-004", "标准套包维护 · 左侧术种分类树", "术种分类名称搜索过滤", "高", S_PAGE, + f"{LOGIN_NOTE}\n2. 从左树读取 `§sampleOperCategoryName§`\n3. 在分类搜索框填入该关键字(即时过滤或回车,以现网为准)\n4. 断言树节点文本均包含该关键字", + "过滤后树节点名称与关键字匹配;无未捕获异常。", +) +sp( + "SURG-STD-005", "标准套包维护 · 左侧术种分类树", "选择分类联动构成列表", "高", S_SAMPLE, + f"{LOGIN_NOTE}\n2. 点击 `§sampleOperCategoryName§` 分类节点\n3. 等待右侧构成列表刷新稳定\n4. 断言列表加载无报错,分页「共 N 条」与当前分类规模一致", + "右侧仅展示该分类下标准套包;总数与筛选一致。", +) +sp( + "SURG-STD-006", "标准套包维护 · 左侧术种分类树", "分类搜索清空恢复全量", "低", S_PAGE, + f"{LOGIN_NOTE}\n2. 用 `§sampleOperCategoryName§` 过滤并记录过滤后节点数\n3. 清空分类搜索框\n4. 断言节点数回到未过滤的默认集合", + "清空后分类树恢复默认集合。", +) +sp( + "SURG-STD-007", "标准套包维护 · 筛选与构成列表", "默认展示标准套包构成", "高", S_PAGE, + f"{LOGIN_NOTE}\n2. 等待列表稳定\n3. 断言按「包名称」分组出现数据行,或「暂无数据」且分页总数为 0", + "加载无报错;有数据时「共 N 条」N≥1;无数据时空态明确。", +) +sp( + "SURG-STD-008", "标准套包维护 · 筛选与构成列表", "产品名称筛选过滤", "高", S_SAMPLE, + f"{LOGIN_NOTE}\n2. 从首行读取 `§sampleProductName§`\n3. 在 `#table_search_form_keyword` 填入该关键字并点击「查 询」\n4. 断言结果行产品名称均包含该关键字", + "过滤结果与关键字匹配;「共 N 条」随之更新。", +) +sp( + "SURG-STD-009", "标准套包维护 · 筛选与构成列表", "规格型号筛选过滤", "中", S_SAMPLE, + f"{LOGIN_NOTE}\n2. 从首行读取 `§sampleSpecModel§`\n3. 在 `#table_search_form_goodsSpec` 填入该关键字并点击「查 询」\n4. 断言结果行规格型号均包含该关键字", + "按规格型号过滤生效。", +) +sp( + "SURG-STD-010", "标准套包维护 · 筛选与构成列表", "器械注册人筛选过滤", "中", S_SAMPLE, + f"{LOGIN_NOTE}\n2. 从首行读取 `§sampleMfrsName§`\n3. 在 `#table_search_form_goodsMfrsName` 填入该关键字并点击「查 询」\n4. 断言结果行器械注册人均包含该关键字", + "按器械注册人过滤生效。", +) +sp( + "SURG-STD-011", "标准套包维护 · 筛选与构成列表", "构成列表核心列完整", "高", + f"{S_SAMPLE};存在分组", + f"{LOGIN_NOTE}\n2. 断言表头/首行包含包名称、包数量/每包含量、产品编号、产品名称、规格型号、器械注册人、单位、产地、耗材类型、操作", + "核心列均可见,操作列含「编 辑」「移 除」。", +) +sp( + "SURG-STD-012", "标准套包维护 · 筛选与构成列表", "包名称展开/收起子项", "高", + f"{S_SAMPLE};存在可展开的包分组", + f"{LOGIN_NOTE}\n2. 读取首个包分组名称 `§samplePkgGroupName§`\n3. 点击展开该分组,断言出现子项行\n4. 再次点击收起,断言子项行隐藏", + "展开显示子项、收起隐藏子项;分组计数不受影响。", +) +sp( + "SURG-STD-013", "标准套包维护 · 筛选与构成列表", "查询与重置", "中", S_SAMPLE, + f"{LOGIN_NOTE}\n2. 用关键字过滤后点击「重 置」\n3. 断言筛选输入框被清空、列表恢复默认集合", + "重置清空筛选并恢复默认列表。", +) +sp( + "SURG-STD-014", "标准套包维护 · 筛选与构成列表", "分页与页大小切换", "中", + f"{S_SAMPLE};列表总数大于单页行数上限", + f"{LOGIN_NOTE}\n2. 切换每页条数(如 50 → 100)\n3. 断言单页行数不超过所选值,分页总数不变", + "页大小切换生效;总数保持稳定。", +) +sp( + "SURG-STD-015", "标准套包维护 · 筛选与构成列表", "无匹配组合空态(mock反例)", "低", S_PAGE, + f"{MOCK_NOTE}\n2. 在 `#table_search_form_keyword` 输入 mock 不存在关键字 `__NOT_EXIST_STD__99999`\n3. 点击「查 询」\n4. 断言「暂无数据」且分页总数为 0", + "无匹配时空态明确,不白屏;总数为 0。", +) +sp( + "SURG-STD-016", "标准套包维护 · 添加基础包弹窗", "打开添加基础包弹窗", "高", S_PAGE, + f"{LOGIN_NOTE}\n2. 点击「添加基础包」\n3. 断言弹窗可见,含左侧基础包分类树与右侧基础包明细\n4. 断言含「取 消」「确定(N)」", + "添加基础包弹窗字段完整;确定按钮带选择计数。", +) +sp( + "SURG-STD-017", "标准套包维护 · 添加基础包弹窗", "弹窗内筛选与选择计数联动", "高", S_PAGE, + f"{LOGIN_NOTE}\n2. 打开添加基础包弹窗,在搜索框输入基础包关键字\n3. 勾选 1 个基础包\n4. 断言「确定(N)」计数由 0 变为 1", + "筛选生效;选择数与确定按钮计数联动。", +) +sp( + "SURG-STD-018", "标准套包维护 · 添加基础包弹窗", "弹窗内构成数量可编辑", "中", + f"{S_PAGE};已勾选至少 1 个基础包", + f"{LOGIN_NOTE}\n2. 定位明细行「构成数量」步进器,点击「Increase Value」\n3. 断言数量 +1 且合计随之更新", + "构成数量可编辑;加减生效。", +) +sp( + "SURG-STD-019", "标准套包维护 · 添加基础包弹窗", "添加基础包取消不落库", "高", S_PAGE, + f"{LOGIN_NOTE}\n2. 记录当前分页总数 `§sampleTotal§`\n3. 打开添加基础包,勾选 1 个基础包后点「取 消」\n4. 断言构成列表总数不变", + "取消后不新增构成行。", +) +sp( + "SURG-STD-020", "标准套包维护 · 添加基础包弹窗", "确定按钮零选禁用", "低", + f"{S_PAGE};已打开添加基础包弹窗且未选择", + f"{LOGIN_NOTE}\n2. 断言「确定(0)」置灰或点击无效果", + "未选择时确定不可用。", +) +sp( + "SURG-STD-021", "标准套包维护 · 添加基础包弹窗", "重复添加同一基础包", "中", + f"{S_PAGE};标准套包已含某基础包(或先添加一个);允许写套包", + f"{LOGIN_NOTE}\n2. 再次打开添加基础包,选择同一基础包并确定\n3. 断言按产品规则拒绝/合并/累加,不产生重复脏行", + "重复添加不产生重复行(策略以现网为准)。", +) +sp( + "SURG-STD-022", "标准套包维护 · 添加散件弹窗", "打开添加散件弹窗", "高", S_PAGE, + f"{LOGIN_NOTE}\n2. 点击「添加散件」\n3. 断言弹窗可见并展示「产品列表」视图,含产品名称/规格型号/器械注册人/耗材类型筛选与分页", + "添加散件弹窗产品列表视图完整。", +) +sp( + "SURG-STD-023", "标准套包维护 · 添加散件弹窗", "产品列表筛选与选择散件", "高", + f"{S_PAGE};已打开添加散件弹窗", + f"{LOGIN_NOTE}\n2. 在产品名称筛选框输入 `§sampleProductName§`\n3. 勾选首个匹配散件\n4. 断言散件进入应拣明细,计数同步变化", + "筛选生效;选中散件进入应拣明细。", +) +sp( + "SURG-STD-024", "标准套包维护 · 添加散件弹窗", "散件规格/应拣明细视图", "高", + f"{S_PAGE};已勾选至少 1 个散件", + f"{LOGIN_NOTE}\n2. 切换到规格明细/应拣明细视图\n3. 断言展示耗材品种数、耗材总数,以及应拣数量、产品编号/名称/规格/注册人/单位/耗材类型/是否收费列", + "应拣明细视图字段完整。", +) +sp( + "SURG-STD-025", "标准套包维护 · 添加散件弹窗", "明细统计与实际一致", "中", + f"{S_PAGE};应拣明细含至少 1 个散件", + f"{LOGIN_NOTE}\n2. 读取「耗材品种数」`§itemKindCount§` 与「耗材总数」`§itemTotalCount§`\n3. 断言品种数 = 明细去重产品数;总数 = 应拣数量合计", + "统计口径与实际所选一致。", +) +sp( + "SURG-STD-026", "标准套包维护 · 添加散件弹窗", "添加散件取消不落库", "高", S_PAGE, + f"{LOGIN_NOTE}\n2. 记录当前分页总数 `§sampleTotal§`\n3. 打开添加散件,勾选散件后点「取 消」\n4. 断言构成列表总数不变", + "取消后不新增散件行。", +) +sp( + "SURG-STD-027", "标准套包维护 · 添加散件弹窗", "万级散件列表首屏可交互", "低", + f"{S_PAGE};添加散件产品列表总量达万级(示例「共 16299 条」)", + f"{LOGIN_NOTE}\n2. 打开添加散件弹窗\n3. 断言首屏可交互(筛选/分页可用),不卡死\n4. 避免全量断言,仅校验首行与总数格式", + "万级列表首屏可用;无超时白屏。", +) +sp( + "SURG-STD-028", "标准套包维护 · 行内操作(编辑 / 移除)", "行内编辑构成项", "高", + f"{S_SAMPLE};先展开目标包", + f"{LOGIN_NOTE}\n2. 点击子项行「编 辑」\n3. 断言编辑弹窗/表单可见并回填当前值\n4. 点击「取 消」关闭", + "编辑回填正确;取消不影响数据。", +) +sp( + "SURG-STD-029", "标准套包维护 · 行内操作(编辑 / 移除)", "移除二次确认防误删整包", "高", + f"{S_SAMPLE};先展开目标包", + f"{LOGIN_NOTE}\n2. 记录当前行数\n3. 点击子项行「移 除」\n4. 若出现确认框则点「取 消」\n5. 断言行数未变化", + "移除有二次确认;取消不删除。", +) + +OUT = [ + ("testcase/基础管理-手术套包设置-基础包维护-UI自动化测试用例-01.csv", base, BASE_PATH), + ("testcase/基础管理-手术套包设置-术种维护-UI自动化测试用例-01.csv", type_cases, TYPE_PATH), + ("testcase/基础管理-手术套包设置-标准套包维护-UI自动化测试用例-01.csv", std, STD_PATH), +] + +for rel, cases, url in OUT: + path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), rel) + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w", encoding="utf-8", newline="") as f: + f.write(build(cases, url)) + print(f"{rel}: {len(cases)} 条") diff --git a/scripts/generate-user-md.py b/scripts/generate-user-md.py new file mode 100644 index 0000000..8d383bb --- /dev/null +++ b/scripts/generate-user-md.py @@ -0,0 +1,795 @@ +#!/usr/bin/env python3 +"""生成基础管理-用户管理 3 页结构化 MD(testcases/)与同源 CSV(testcase/)。""" +from __future__ import annotations + +import csv +from collections import Counter +from datetime import date +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +MD_DIR = ROOT / "testcases" +CSV_DIR = ROOT / "testcase" +TODAY = date.today().isoformat() +CSV_FIELDS = [ + "用例编号", + "功能模块", + "功能测试点", + "用例标题", + "优先级", + "前置条件", + "测试步骤", + "预期结果", + "实际结果", +] +# 正例登录账号可写入 CSV;密码只放 .env TEST_PASS / auth.setup,禁止入库 +POSITIVE_USER = "test_1" + + +def page(**kwargs) -> dict: + return kwargs + + +def case( + seq: int, + h3: str, + h4: str, + prio: str, + title: str, + tp: int, + steps: list[str], + expect: str, + pre: str = "", + auto: str | None = None, +) -> dict: + return { + "seq": seq, + "h3": h3, + "h4": h4, + "prio": prio, + "title": title, + "tp": tp, + "steps": steps, + "expect": expect, + "pre": pre, + "auto": auto, + } + + +PAGES = [ + page( + code="USER-USR", + prefix="USR", + name="用户管理", + path="/spd/basic/user/platform/person", + scan="basic-user", + count=219, + spec="UI-SPEC-SPD-USER-001", + spec_file="basic-user.spec.ts", + tp_total=81, + write_note=( + "新增/编辑只打开后取消,不提交保存;授权只打开后取消;" + "重置密码只验证确认框后取消,不确认改密。禁止写死生产账号。" + ), + nav_note=( + "一级菜单与叶子菜单均名「用户管理」,须点「基础设置 → 用户管理(子菜单)→ 用户管理(叶子)」;" + "建议 `openPath('/spd/basic/user/platform/person')` 并断言 URL,勿误入医共体/角色管理。" + ), + samples=( + "查询后从首行读取 `${sampleUserName}`、`${sampleLoginName}`、`${sampleDept}`、`${sampleRole}`;" + "科室为 `--` 时换一行;mock:`__NOT_EXIST_USER__99999`;" + "写操作用 `${uniqLogin}`(建议 `auto.usr.${timestamp}`),取消不落库。" + ), + skip="真实保存用户、禁用后登录、真实改密、Excel 导入文件、接口失败注入、会话过期、无菜单权限", + cases=[ + case( + 1, "页面进入", "功能验证点", "P0", "菜单进入用户管理页", 1, + [ + "按菜单「基础设置 → 用户管理(子菜单)→ 用户管理(叶子)」进入,或 `openPath` 打开 `/spd/basic/user/platform/person`", + "断言 URL 含 `/spd/basic/user/platform/person`", + "断言筛选区、工具栏「新 增」「Excel导入」、列表列头与分页可见", + ], + "进入用户管理页;不误入医共体用户管理或角色管理;无报错遮罩。", + auto="USER-USR-001", + ), + case( + 2, "页面进入", "异常验证点", "P0", "未登录访问拦截", 5, + [ + "清除登录态后直接打开 `/spd/basic/user/platform/person`", + "断言跳转登录或不展示用户业务列表", + ], + "未登录不可见用户数据。", + ), + case( + 3, "筛选与查询", "功能验证点", "P0", "默认查询列表有数据或空态明确", 9, + [ + "点击「查 询」并等待列表稳定", + "断言出现数据行,或「暂无数据」且分页总数为 0", + ], + "查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。", + auto="USER-USR-002", + ), + case( + 4, "筛选与查询", "功能验证点", "P0", "姓名或登录账号关键字查询", 10, + [ + "从首行读取 `${sampleUserName}` 或 `${sampleLoginName}` 取关键字", + "在「姓名」框(占位「姓名、登录账号」)填入关键字,点击「查 询」", + "断言结果行姓名或登录账号匹配该关键字", + ], + "查询结果与已填关键字匹配;无未捕获异常。", + pre="默认查询至少 1 条且姓名或登录账号非空", auto="USER-USR-003", + ), + case( + 5, "筛选与查询", "功能验证点", "P0", "科室下拉筛选", 11, + [ + "取首条所属科室非 `--` 的行,读取 `${sampleDept}`", + "科室下拉选择该值,点击「查 询」", + "断言结果行「所属科室」与所选一致,且不含未分配 `--` 行", + ], + "所属科室列与筛选条件一致。", + pre="列表存在所属科室非空行,否则 skip", auto="USER-USR-004", + ), + case( + 6, "筛选与查询", "功能验证点", "P0", "姓名+科室+角色组合查询", 13, + [ + "读取 `${sampleUserName}`、`${sampleDept}`、`${sampleRole}`(角色可从该行截断前列名)", + "填写姓名、科室、角色后点击「查 询」", + "断言结果同时满足已填条件", + ], + "组合查询无报错;结果同时满足全部已填条件。", + pre="默认查询至少 1 条", auto="USER-USR-005", + ), + case( + 7, "筛选与查询", "功能验证点", "P1", "重置恢复默认", 14, + [ + "填写姓名等筛选条件后点击「重 置」", + "断言筛选字段恢复默认,列表按默认规则刷新", + ], + "条件恢复默认;列表刷新。", + auto="USER-USR-006", + ), + case( + 8, "筛选与查询", "功能验证点", "P1", "展开高级筛选", 15, + [ + "点击「高级筛选」", + "断言展开后面板可见姓名/科室/角色/状态及「重 置」「筛 选」", + ], + "高级筛选面板展开;关键字段与筛选按钮可见。", + auto="USER-USR-007", + ), + case( + 9, "筛选与查询", "功能验证点", "P0", "高级筛选按状态过滤", 16, + [ + "展开高级筛选,状态下拉选择「启用」或「禁用」", + "点击「筛 选」", + "断言列表仅保留对应状态用户(无匹配则空态且总数为 0)", + ], + "按状态过滤成功;无报错。", + auto="USER-USR-008", + ), + case( + 10, "筛选与查询", "异常验证点", "P1", "无匹配结果空态(mock反例)", 22, + [ + "姓名框填入 `__NOT_EXIST_USER__99999`", + "点击「查 询」", + "断言空态或「暂无数据」,分页总数为 0", + ], + "无匹配时不报错;空态明确;总数为 0。", + auto="USER-USR-009", + ), + case( + 11, "列表与分页", "功能验证点", "P0", "列表核心列完整", 25, + [ + "断言表头含序号、姓名、登录账号、联系方式、所属科室、医院员工编号、角色、科室任职数、操作", + "断言操作列可见「编辑」「授权」「重置密码」", + ], + "核心列完整;行内三个操入口可见。", + auto="USER-USR-010", + ), + case( + 12, "列表与分页", "功能验证点", "P1", "列表不含所属医院列", 26, + [ + "断言表头不含「所属医院」", + ], + "本页无所属医院列,与医共体用户管理页区分。", + auto="USER-USR-011", + ), + case( + 13, "列表与分页", "功能验证点", "P1", "分页翻至第 2 页", 31, + [ + "记录第 1 页首行登录账号", + "点击页码「2」或「下一页」", + "断言页码高亮为 2,首行账号与第 1 页不同", + ], + "翻页成功;数据更换。", + pre="默认查询总条数大于一页,否则 skip", auto="USER-USR-012", + ), + case( + 14, "新增用户", "功能验证点", "P0", "打开新增用户弹窗", 41, + [ + "点击「新 增」", + "断言弹出用户表单,可见账号、姓名、联系方式、医院员工编号、启用/禁用、「取 消」「确 定」", + ], + "新增弹窗可见;关键字段与按钮齐全。", + auto="USER-USR-013", + ), + case( + 15, "新增用户", "功能验证点", "P1", "新增取消不落库", 43, + [ + "打开新增,账号填 `${uniqLogin}`,姓名填任意合法值", + "点击「取 消」", + "按账号查询,断言无该记录", + ], + "弹窗关闭;列表不出现 `${uniqLogin}`。", + auto="USER-USR-014", + ), + case( + 16, "新增用户", "异常验证点", "P0", "必填为空拦截", 47, + [ + "打开新增,账号或姓名留空后点击「确 定」", + "断言出现必填提示,弹窗仍打开", + ], + "确定被拦截;不落库;弹窗不关闭。", + auto="USER-USR-015", + ), + case( + 17, "新增用户", "异常验证点", "P0", "登录账号重复拦截", 48, + [ + "打开新增,账号填入已存在的 `${sampleLoginName}`,补齐其它必填后点「确 定」", + "断言保存失败并提示,弹窗仍打开", + "查询该账号,断言原用户未被覆盖", + ], + "重复账号被拒绝;不覆盖原用户。", + pre="默认查询至少 1 条且登录账号非空", auto="USER-USR-016", + ), + case( + 18, "编辑用户", "功能验证点", "P0", "打开编辑用户弹窗并回填", 54, + [ + "点击首行「编辑」", + "断言弹窗标题含「编辑用户」", + "断言账号、姓名与该行 `${sampleLoginName}`、`${sampleUserName}` 一致(允许规范化空白)", + ], + "编辑弹窗可见;回填与列表行一致。", + pre="列表至少 1 条", auto="USER-USR-017", + ), + case( + 19, "编辑用户", "功能验证点", "P1", "编辑取消不保存", 58, + [ + "打开首行编辑,记录原姓名", + "将姓名改为临时值后点「取 消」", + "再次打开编辑,断言姓名仍为原值", + ], + "取消后列表与回看均为修改前姓名。", + pre="列表至少 1 条", auto="USER-USR-018", + ), + case( + 20, "编辑用户", "边界验证点", "P1", "编辑时账号只读", 61, + [ + "打开首行编辑", + "断言登录账号不可改(disabled/readonly 或无法输入)", + ], + "编辑态登录账号只读。", + pre="列表至少 1 条", auto="USER-USR-019", + ), + case( + 21, "编辑用户", "功能验证点", "P1", "编辑弹窗可见签名图片", 56, + [ + "打开首行编辑", + "断言可见「签名图片」区域(有图则展示,无图则上传入口可见)", + "点「取 消」,不保存", + ], + "签名图片控件可见;本条不上传、不保存。", + pre="列表至少 1 条", auto="USER-USR-020", + ), + case( + 22, "授权", "功能验证点", "P0", "打开授权后取消不生效", 68, + [ + "点击首行「授权」", + "断言打开授权层(弹窗或权限树),可见可勾选菜单/功能", + "如可勾选则改一处勾选后点取消/关闭", + "再次打开同一用户授权,断言勾选与操作前一致", + ], + "授权层可打开;取消后勾选不落库。", + pre="列表至少 1 条", auto="USER-USR-021", + ), + case( + 23, "重置密码", "功能验证点", "P0", "行内重置密码确认后取消", 75, + [ + "点击首行「重置密码」", + "断言出现确认框或密码表单,含取消/关闭", + "点取消/关闭", + "断言仍停留本页且该行仍在", + ], + "重置需确认;取消后不改密。本条不点确定,避免污染账号。", + pre="列表至少 1 条", auto="USER-USR-022", + ), + ], + ), + page( + code="USER-UNI", + prefix="UNI", + name="医共体用户管理", + path="/spd/basic/user/platform/personCom", + scan="basic-user-union", + count=221, + spec="UI-SPEC-SPD-USER-002", + spec_file="basic-user-union.spec.ts", + tp_total=58, + write_note=( + "新增/编辑只打开后取消,不提交保存;授权与改密规则同用户管理,本页不重复真实写操作。" + "禁止写死医院名称。" + ), + nav_note="菜单「基础设置 → 用户管理 → 医共体用户管理」;可用 `openPath('/spd/basic/user/platform/personCom')`。", + samples=( + "查询后从首行读取 `${sampleLoginName}`、`${sampleUserName}`、`${sampleHospital}`、`${samplePhone}`;" + "禁止写死扫描到的医院名;mock:`__NOT_EXIST_USER__99999`。" + ), + skip="授权/改密持久化(引用 TP_USER_USR)、真实跨院保存、Excel 导入文件、接口失败、会话过期", + cases=[ + case( + 1, "页面进入", "功能验证点", "P0", "菜单进入医共体用户管理页", 1, + [ + "按菜单「基础设置 → 用户管理 → 医共体用户管理」进入,或 `openPath` 打开 `/spd/basic/user/platform/personCom`", + "断言 URL 含 `/spd/basic/user/platform/personCom`", + "断言所属医院筛选、工具栏、列表列头与分页可见", + ], + "进入医共体用户管理页;不误入用户管理或角色管理。", + auto="USER-UNI-001", + ), + case( + 2, "页面进入", "异常验证点", "P0", "未登录访问拦截", 5, + [ + "清除登录态后直接打开 `/spd/basic/user/platform/personCom`", + "断言跳转登录或不展示用户业务列表", + ], + "未登录不可见医共体用户数据。", + ), + case( + 3, "筛选与查询", "功能验证点", "P0", "默认查询列表有数据或空态明确", 9, + [ + "点击「查 询」并等待列表稳定", + "断言出现数据行,或空态且分页总数为 0", + ], + "查询无报错;有数据时「共 N 条」N≥1。", + auto="USER-UNI-002", + ), + case( + 4, "筛选与查询", "功能验证点", "P0", "姓名或登录账号关键字查询", 10, + [ + "从首行读取 `${sampleUserName}` 或 `${sampleLoginName}`", + "在「姓名」框填入关键字,点击「查 询」", + "断言结果行姓名或登录账号匹配", + ], + "查询结果与关键字匹配。", + pre="默认查询至少 1 条", auto="USER-UNI-003", + ), + case( + 5, "筛选与查询", "功能验证点", "P0", "所属医院筛选", 11, + [ + "记录当前默认 `${sampleHospital}` 与分页总数", + "切换「所属医院」为另一可选项(仅 1 家则 skip)", + "点击「查 询」", + "断言结果行「所属医院」均为所选值,「共 N 条」可与默认不同", + ], + "列表仅该院用户;总数随医院变化。", + auto="USER-UNI-004", + ), + case( + 6, "筛选与查询", "功能验证点", "P0", "姓名+医院+联系方式组合查询", 13, + [ + "读取 `${sampleUserName}`、`${sampleHospital}`、`${samplePhone}`(电话非法占位如 `1` 时改用姓名+医院)", + "填写后点击「查 询」", + "断言结果同时满足已填条件", + ], + "组合条件同时生效。", + pre="默认查询至少 1 条", auto="USER-UNI-005", + ), + case( + 7, "筛选与查询", "功能验证点", "P1", "重置恢复默认医院", 14, + [ + "更改所属医院或姓名后点击「重 置」", + "断言所属医院恢复进入页时的默认值,列表刷新", + ], + "医院等条件恢复默认。", + auto="USER-UNI-006", + ), + case( + 8, "筛选与查询", "功能验证点", "P1", "展开高级筛选", 15, + [ + "点击「高级筛选」", + "断言可见所属医院、联系方式、科室、角色及「筛 选」", + ], + "高级筛选含医院维度字段。", + auto="USER-UNI-007", + ), + case( + 9, "筛选与查询", "异常验证点", "P1", "无匹配空态(mock反例)", 21, + [ + "姓名框填入 `__NOT_EXIST_USER__99999`,点击「查 询」", + "断言空态且分页总数为 0", + ], + "无匹配不报错;总数为 0。", + auto="USER-UNI-008", + ), + case( + 10, "列表与分页", "功能验证点", "P0", "列表列顺序含所属医院", 24, + [ + "断言列顺序为登录账号、姓名、联系方式、所属医院、所属科室、医院员工编号、角色、科室任职数、操作", + "勿用用户管理页列断言(用户管理无所属医院且姓名在登录账号前)", + ], + "列顺序与用户管理页不同;含所属医院。", + auto="USER-UNI-009", + ), + case( + 11, "列表与分页", "功能验证点", "P1", "分页翻至第 2 页", 26, + [ + "记录第 1 页首行登录账号", + "翻到第 2 页", + "断言数据更换且所属医院筛选仍保持", + ], + "翻页成功;医院条件不丢失。", + pre="总条数大于一页,否则 skip", auto="USER-UNI-010", + ), + case( + 12, "列表与分页", "集成验证点", "P1", "与用户管理页同一用户字段一致", 33, + [ + "记录本页首行 `${sampleLoginName}`、姓名、所属科室、角色", + "打开用户管理页,按登录账号查询同一用户", + "断言姓名、角色、所属科室一致;本页仅多「所属医院」", + ], + "同一登录账号跨页字段一致。", + pre="两页都能查到同一账号,否则 skip", auto="USER-UNI-011", + ), + case( + 13, "新增与编辑", "功能验证点", "P0", "打开新增弹窗", 36, + [ + "点击「新 增」", + "断言弹出用户表单,含账号、姓名、联系方式及取消/确定", + ], + "新增弹窗可见。", + auto="USER-UNI-012", + ), + case( + 14, "新增与编辑", "功能验证点", "P1", "新增取消不落库", 38, + [ + "打开新增,账号填 `${uniqLogin}` 后点「取 消」", + "当前医院下列表查询该账号,断言不存在", + ], + "取消后当前医院列表不出现该账号。", + auto="USER-UNI-013", + ), + case( + 15, "新增与编辑", "异常验证点", "P0", "必填为空拦截", 46, + [ + "打开新增,账号或姓名留空后点「确 定」", + "断言被拦截,弹窗仍打开,不落库", + ], + "必填为空不可保存。", + auto="USER-UNI-014", + ), + case( + 16, "新增与编辑", "功能验证点", "P0", "打开编辑并回填", 39, + [ + "点击首行「编辑」", + "断言弹窗回填与该行登录账号、姓名、所属医院一致", + "点「取 消」", + ], + "编辑回填正确;本条不保存。", + pre="列表至少 1 条", auto="USER-UNI-015", + ), + ], + ), + page( + code="USER-ROL", + prefix="ROL", + name="角色管理", + path="/spd/basic/user/platform/role", + scan="basic-role", + count=1170, + spec="UI-SPEC-SPD-USER-003", + spec_file="basic-role.spec.ts", + tp_total=45, + write_note=( + "权限树勾选后点「刷 新」恢复,不点「保 存」;" + "删除只验证确认框后取消;新增只打开后取消。" + "禁止删除预置角色或真实改名。" + ), + nav_note="菜单「基础设置 → 用户管理 → 角色管理」;本页左右分栏、无独立分页。", + samples=( + "左栏读取 `${sampleRoleName}`(优先非预置自定义角色;预置用「医院管理员」若存在);" + "权限树按「模块名 + 菜单名」定位,根为「SPD内网系统PC端」;" + "mock:`__NOT_EXIST_ROLE__99999`。" + ), + skip="真实保存权限树、真实新增/改名/删除角色、未保存切角色(待评审)、角色授权后换账号登录、接口失败", + cases=[ + case( + 1, "页面进入", "功能验证点", "P0", "菜单进入角色管理页", 1, + [ + "按菜单「基础设置 → 用户管理 → 角色管理」进入,或 `openPath` 打开 `/spd/basic/user/platform/role`", + "断言 URL 含 `/spd/basic/user/platform/role`", + "断言左侧角色列表、右侧菜单权限树、「保 存」「刷 新」可见,无独立分页", + ], + "进入角色管理;左右分栏渲染完成;不误入用户管理页。", + auto="USER-ROL-001", + ), + case( + 2, "页面进入", "异常验证点", "P0", "未登录访问拦截", 5, + [ + "清除登录态后直接打开 `/spd/basic/user/platform/role`", + "断言跳转登录或不展示角色/权限树业务数据", + ], + "未登录不可见角色配置。", + ), + case( + 3, "左侧角色列表", "功能验证点", "P0", "角色列表加载并可点选", 9, + [ + "断言左栏列出角色名称(如医院管理员、护士等)", + "点击首个 `${sampleRoleName}`", + "断言该行处于选中态", + ], + "左栏有角色且可点选。", + auto="USER-ROL-002", + ), + case( + 4, "左侧角色列表", "功能验证点", "P0", "角色名称搜索过滤", 10, + [ + "在角色名称框输入 `${sampleRoleName}` 的部分关键字", + "断言左栏收敛为匹配项(即时或回车,以现网为准)", + ], + "搜索结果角色名包含关键字。", + pre="左栏至少 1 个角色", auto="USER-ROL-003", + ), + case( + 5, "左侧角色列表", "功能验证点", "P0", "选中角色刷新右侧权限树", 11, + [ + "依次点选两个不同角色", + "断言右侧勾选随当前角色变化,无上一角色残留", + ], + "权限树与当前角色配置一致。", + pre="左栏至少 2 个角色", auto="USER-ROL-004", + ), + case( + 6, "左侧角色列表", "功能验证点", "P1", "清空搜索恢复全量", 12, + [ + "先按关键字过滤,再清空搜索", + "断言左栏恢复未过滤的完整角色列表", + ], + "清空后角色列表恢复全量。", + auto="USER-ROL-005", + ), + case( + 7, "左侧角色列表", "异常验证点", "P1", "搜索无匹配空态(mock反例)", 16, + [ + "角色名称框填入 `__NOT_EXIST_ROLE__99999`", + "断言左栏空态明确", + ], + "无匹配不报错;空态明确。", + auto="USER-ROL-006", + ), + case( + 8, "右侧菜单权限树", "功能验证点", "P0", "权限树按应用与模块展开", 18, + [ + "选中任一角色", + "断言右侧树根为「SPD内网系统PC端」,可见基础设置/运维管理/厂商管理/产品管理等模块", + "展开「基础设置」,断言可见子菜单(如医院资料)", + ], + "权限树层级可见;节点可展开。", + auto="USER-ROL-007", + ), + case( + 9, "右侧菜单权限树", "功能验证点", "P0", "勾选父节点联动子节点", 19, + [ + "选中一自定义或可编辑角色(预置被锁则 skip)", + "记录「厂商管理」父节点原勾选", + "勾选「厂商管理」父节点", + "断言其子菜单/功能全部勾选", + "点击「刷 新」,不点「保 存」", + ], + "父节点勾选联动子节点;本条不保存。", + auto="USER-ROL-008", + ), + case( + 10, "右侧菜单权限树", "功能验证点", "P1", "刷新恢复未保存勾选", 23, + [ + "改一处勾选后点「刷 新」且未保存", + "断言树恢复为进入该角色时的已保存状态", + ], + "刷新丢弃未保存勾选。", + auto="USER-ROL-009", + ), + case( + 11, "角色增删改", "功能验证点", "P0", "删除需确认且取消保留", 34, + [ + "点一自定义角色的删除入口(预置角色无删除则改点自定义;都没有则 skip)", + "断言出现确认框,含取消/确定", + "点「取 消」", + "断言该角色仍在左栏", + ], + "删除需二次确认;取消后角色仍在。本条不点确定。", + auto="USER-ROL-010", + ), + case( + 12, "角色增删改", "功能验证点", "P1", "新增角色入口打开后取消", 32, + [ + "通过新增或「角色」工具按钮打开新增表单(扫描未打开则 skip)", + "不填或填写后点取消/关闭", + "断言左栏角色数量不增加", + ], + "新增入口可达;取消不落库。", + auto="USER-ROL-011", + ), + case( + 13, "角色增删改", "异常验证点", "P1", "预置角色不可删除", 41, + [ + "在左栏定位「医院管理员」(或其它预置角色,以现网为准)", + "断言删除入口不可见;若可见则确定后被拒绝,角色仍在左栏", + "若弹出确认则点取消,避免误删", + ], + "预置角色不可删除。", + pre="左栏存在可识别的预置角色,否则 skip", auto="USER-ROL-012", + ), + case( + 14, "左侧角色列表", "集成验证点", "P1", "角色名与用户管理角色下拉同源", 17, + [ + "记录本页左栏角色名称集合", + "打开用户管理,展开「角色」筛选项", + "断言两边名称集合一致(允许按权限裁剪,本页有的名称用户页应能对应到)", + ], + "角色主数据跨页一致,不串名。", + auto="USER-ROL-013", + ), + ], + ), +] + + +def tp_id(p: dict, c: dict) -> str: + return f"TP_USER_{p['prefix']}_{c['tp']:03d}" + + +def prio_csv(prio: str) -> str: + return {"P0": "高", "P1": "中", "P2": "低"}[prio] + + +def is_unlogin(c: dict) -> bool: + return "未登录" in c["title"] + + +def render_csv_rows(p: dict) -> list[dict]: + logged = ( + f"测试环境可访问;正例账号 {POSITIVE_USER} 已通过 auth.setup 写入 storageState 登录" + f"(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → {p['name']}」权限" + ) + unlogin = ( + f"测试环境可访问;使用独立 context,storageState 为空(不加载 {POSITIVE_USER} 登录态)" + ) + rows: list[dict] = [] + for c in p["cases"]: + case_id = f"{p['code']}-{c['seq']:03d}" + if is_unlogin(c): + pre_parts = [unlogin] + if c["pre"]: + pre_parts.append(c["pre"]) + steps = "\n".join( + ["1. 新建独立 BrowserContext,不加载 auth/user.json"] + + [f"{i + 1}. {s}" for i, s in enumerate(c["steps"], start=1)] + ) + else: + pre_parts = [logged] + if c["seq"] > 1: + pre_parts.append(f"已进入{p['name']}页({p['path']},无 iframe)") + if c["pre"]: + pre_parts.append(c["pre"]) + steps = "\n".join( + [ + f"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 {POSITIVE_USER}),无需在用例内重复登录" + ] + + [f"{i + 1}. {s}" for i, s in enumerate(c["steps"], start=1)] + ) + rows.append( + { + "用例编号": case_id, + "功能模块": "基础设置 · 用户管理", + "功能测试点": f"{p['name']} · {c['h3']}", + "用例标题": c["title"], + "优先级": prio_csv(c["prio"]), + "前置条件": ";".join(pre_parts), + "测试步骤": steps, + "预期结果": c["expect"], + "实际结果": "", + } + ) + return rows + + +def render_md(p: dict) -> str: + n = len(p["cases"]) + cnt = Counter(c["prio"] for c in p["cases"]) + prio_txt = " / ".join(f"{k}={cnt[k]}" for k in ("P0", "P1", "P2") if cnt[k]) + lines = [ + f"# 基础管理-用户管理-{p['name']} UI 自动化测试用例", + "", + f"**来源测试点**:`testpoint/基础管理-用户管理-{p['name']}-测试点分析-01.md` ", + f"**执行 CSV**:`testcase/基础管理-用户管理-{p['name']}-UI自动化测试用例-01.csv` ", + f"**页面 URL**:`{p['path']}` ", + f"**定位器 YAML**:`locators/spd/{p['scan']}.locator.yaml` ", + f"**元素扫描**:`data/scan/{p['scan']}-elements.json`({p['count']} 项) ", + f"**关联规范**:`knowledge-base/08-UI专项规范库/{p['spec']}.md` ", + f"**版本**:{TODAY} ", + f"**自动化框架**:Playwright + TypeScript(规划 `tests/spd/{p['spec_file']}`,依赖 `auth.setup` 登录态)", + "", + f"**通用前置**:正例账号 `{POSITIVE_USER}` 经 `auth.setup` 写入 `storageState`(密码仅存 `.env` 的 `TEST_PASS`);具备「基础设置 → 用户管理 → {p['name']}」权限。反例筛选用 mock(`__NOT_EXIST_USER__99999` / `__NOT_EXIST_ROLE__99999`)。 ", + "**页面约定**:整页渲染,**无报表 iframe**;定位均在 `page` 上。 ", + f"**菜单导航**:{p['nav_note']} ", + f"**写操作**:{p['write_note']} ", + f"**动态取数**:{p['samples']}", + "", + "---", + "", + "## 基础设置 · 用户管理", + "", + ] + current_h3 = "" + current_h4 = "" + for c in p["cases"]: + h3 = f"{p['name']} · {c['h3']}" + if h3 != current_h3: + current_h3 = h3 + current_h4 = "" + lines += [f"### {h3}", ""] + if c["h4"] != current_h4: + current_h4 = c["h4"] + lines += [f"#### {current_h4}", ""] + case_id = f"{p['code']}-{c['seq']:03d}" + tc = f"TC-UI-{case_id}" + lines += [ + f"##### 【{c['prio']}】【{tc}】【{tp_id(p, c)}】{c['title']}", + "", + f"- 前置条件:{c['pre'] or ('已进入' + p['name'] + '页')}", + "- 操作步骤:", + ] + for i, step in enumerate(c["steps"], 1): + lines.append(f" {i}. {step}") + lines.append(f"- 自动化断言:规划与 CSV `{case_id}` 及 `tests/spd/{p['spec_file']}` 一致") + lines += ["", "###### 预期结果", "", c["expect"], "", "---", ""] + while lines and lines[-1] in ("", "---"): + lines.pop() + lines += [ + "", + "", + "## 质量与覆盖说明", + "", + "| 项 | 说明 |", + "|----|------|", + f"| 用例数 | **{n}** 条({prio_txt}) |", + f"| 来源 | 从 {p['tp_total']} 条测试点选取可稳定自动化的主路径与关键反向 |", + f"| 未纳入自动化 | {p['skip']} |", + "| 单一职责 | 每条只断言一个核心业务结果 |", + "", + ] + return "\n".join(lines) + + +def main() -> None: + MD_DIR.mkdir(parents=True, exist_ok=True) + CSV_DIR.mkdir(parents=True, exist_ok=True) + total = 0 + for p in PAGES: + seqs = [c["seq"] for c in p["cases"]] + if seqs != list(range(1, len(seqs) + 1)): + raise SystemExit(f"{p['code']} seq not consecutive: {seqs}") + md_path = MD_DIR / f"基础管理-用户管理-{p['name']}-UI自动化测试用例-01.md" + csv_path = CSV_DIR / f"基础管理-用户管理-{p['name']}-UI自动化测试用例-01.csv" + md_path.write_text(render_md(p), encoding="utf-8") + rows = render_csv_rows(p) + with csv_path.open("w", encoding="utf-8", newline="") as f: + w = csv.DictWriter(f, fieldnames=CSV_FIELDS) + w.writeheader() + w.writerows(rows) + total += len(rows) + print(f"OK {p['code']}\t{len(rows):2d} cases\t{md_path.name}\t{csv_path.name}") + print(f"done pages={len(PAGES)} cases={total}") + + +if __name__ == "__main__": + main() diff --git a/scripts/generate-user-testpoints.py b/scripts/generate-user-testpoints.py new file mode 100644 index 0000000..24111e4 --- /dev/null +++ b/scripts/generate-user-testpoints.py @@ -0,0 +1,833 @@ +#!/usr/bin/env python3 +"""Generate 用户管理 testpoint MD and patch UI-SPEC scan paths from data/scan JSON.""" +from __future__ import annotations + +import json +import re +from datetime import date +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +SCAN_DIR = ROOT / "data" / "scan" +OUT_DIR = ROOT / "testpoint" +KB = ROOT / "knowledge-base" / "08-UI专项规范库" +INDEX = KB / "INDEX.md" +TODAY = date.today().isoformat() + +PAGES = [ + { + "pageKey": "basic-user", + "specId": "USER-001", + "name": "用户管理", + "prefix": "TP_USER_USR", + "file": "基础管理-用户管理-用户管理-测试点分析-01.md", + "kind": "user", + "path": "/spd/basic/user/platform/person", + }, + { + "pageKey": "basic-user-union", + "specId": "USER-002", + "name": "医共体用户管理", + "prefix": "TP_USER_UNI", + "file": "基础管理-用户管理-医共体用户管理-测试点分析-01.md", + "kind": "union", + "path": "/spd/basic/user/platform/personCom", + }, + { + "pageKey": "basic-role", + "specId": "USER-003", + "name": "角色管理", + "prefix": "TP_USER_ROL", + "file": "基础管理-用户管理-角色管理-测试点分析-01.md", + "kind": "role", + "path": "/spd/basic/user/platform/role", + }, +] + + +def load_scan(page_key: str) -> dict: + path = SCAN_DIR / f"{page_key}-elements.json" + data = json.loads(path.read_text(encoding="utf-8")) + return {"url": data["url"].split("?")[0], "elementCount": data["elementCount"]} + + +def tp(prefix: str, n: int, prio: str, title: str, check: str, spec: str) -> str: + return f"- **{prefix}_{n:03d}**({prio})|{title}|验证要点:{check}|关联需求:{spec}" + + +def section(title: str, blocks: list[tuple[str, list[str]]]) -> str: + parts = [f"### {title}", ""] + for h4, lines in blocks: + parts.append(f"#### {h4}") + parts.append("") + parts.extend(lines or ["- 无(本功能点无独立该类场景,规则已归入其它功能点)"]) + parts.append("") + return "\n".join(parts) + + +class Seq: + def __init__(self, prefix: str, spec: str): + self.prefix = prefix + self.spec = spec + self.n = 0 + + def add(self, prio: str, title: str, check: str) -> str: + self.n += 1 + return tp(self.prefix, self.n, prio, title, check, self.spec) + + +def nav_section(p: dict, url: str, seq: Seq) -> str: + name = p["name"] + path = p["path"] + kind = p["kind"] + peers = "用户管理 / 医共体用户管理 / 角色管理" + nav_note = ( + "一级菜单与叶子菜单均名「用户管理」,须点「基础设置 → 用户管理(子菜单)→ 用户管理(叶子)」进入 `/person`," + "不误入医共体用户管理或角色管理" + if kind == "user" + else f"登录后通过「基础设置 → 用户管理 → {name}」进入目标页,左侧菜单与顶栏 Tab 高亮为{name},不串到同组其它页" + ) + if kind == "role": + ready = "左右分栏渲染:左侧角色列表与右侧菜单权限树可见,「保存」「刷新」可见,无独立分页,无报错遮罩" + ctx = "当前登录人可维护角色与权限树,与顶栏登录身份不矛盾;本页无所属医院维度" + elif kind == "union": + ready = "整页单表渲染:所属医院等筛选、工具栏、列表列头与分页可见,无报错遮罩" + ctx = "顶栏院区/登录医院与本页可管理的「所属医院」范围一致" + else: + ready = "整页单表渲染:筛选区、工具栏(新增/Excel导入)、列表列头与分页可见,无报错遮罩" + ctx = "顶栏院区、登录用户与本页可管理用户范围一致" + return section( + "页面访问与导航", + [ + ( + "功能验证点", + [ + seq.add("高", f"菜单路径可达{name}", nav_note), + seq.add("高", "页面主区加载完成", ready), + seq.add( + "中", + "同组用户管理菜单可切换", + f"在 {peers} 间切换后 URL 与页面内容对应,再返回{name}状态正确", + ), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "已登录直接打开 URL", f"携带有效登录态打开 `{path}`,页面正常渲染"), + ], + ), + ( + "异常验证点", + [ + seq.add("高", "未登录访问拦截", "无登录态访问 URL 跳转登录或不展示用户/角色业务数据"), + seq.add("中", "登录态过期", "会话失效后查询/写操作被拦截并提示重新登录,不误写"), + seq.add("中", "无菜单权限", f"无{name}权限的账号不可见菜单或 URL 被拒绝"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "顶栏身份与可管理范围一致", ctx), + ], + ), + ], + ) + + +def user_body(seq: Seq) -> str: + chunks = [ + section( + "筛选与查询", + [ + ( + "功能验证点", + [ + seq.add("高", "默认条件查询", "进入后点「查询」,列表有数据或空态明确,底部分页「共 N 条」与行数逻辑一致"), + seq.add("高", "姓名/登录账号关键字", "在「姓名」框(占位「姓名、登录账号」)输入关键字后,结果姓名或登录账号匹配"), + seq.add("高", "科室下拉筛选", "选择某科室后列表「所属科室」与所选一致(未分配显示 `--` 的行不应出现在指定科室结果中)"), + seq.add("高", "角色下拉筛选", "选择某角色后列表「角色」列包含该角色"), + seq.add("高", "姓名+科室+角色组合查询", "多个已填条件同时生效,结果同时满足全部条件"), + seq.add("中", "重置恢复默认", "填写筛选后点「重置」,条件恢复默认,列表按默认规则刷新"), + seq.add("中", "展开高级筛选", "点「高级筛选」展开面板,可见姓名/科室/角色/状态及底部「重置」「筛选」"), + seq.add("高", "高级筛选按状态过滤", "高级筛选选择启用或禁用后点「筛选」,列表仅保留对应状态用户"), + seq.add("中", "高级筛选内重置", "高级筛选填写后点面板「重置」,条件清空,列表恢复默认"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "超长姓名/账号关键字", "输入达到或超过允许长度的关键字,查询被接受并按规则截断或返回匹配,页面不崩溃"), + seq.add("低", "仅空格关键字", "按空查询处理或提示,不报错"), + seq.add("低", "科室/角色下拉可搜索", "下拉内输入关键字后选项收敛为匹配项"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "查询接口失败", "列表查询失败时有错误提示,不白屏、不把过期数据展示为成功态"), + seq.add("低", "无匹配结果空态", "条件无匹配时「暂无数据」明确,分页总数为 0"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "筛选结果与分页总数一致", "查询后可见行逻辑与「共 N 条」一致"), + seq.add("高", "角色下拉与角色管理同源", "本页角色筛选项集合与「角色管理」左侧角色名称一致(允许按权限裁剪)"), + ], + ), + ], + ), + section( + "列表与分页", + [ + ( + "功能验证点", + [ + seq.add( + "高", + "列表核心列完整", + "主表展示序号、姓名、登录账号、联系方式、所属科室、医院员工编号、角色、科室任职数、操作(编辑/授权/重置密码)", + ), + seq.add("中", "列表不含所属医院列", "本页列中无「所属医院」,与医共体用户管理页区分"), + seq.add("中", "多角色列表截断展示", "用户绑定多个角色时列表「角色」列截断(如带省略号)"), + seq.add("中", "空值展示为 --", "未分配科室、未填工号、无任职时对应列显示 `--`"), + seq.add("中", "表格密度切换", "密度切换后列表仍可读,列不丢失"), + seq.add("低", "无批量操作", "序号列可勾选,但工具栏无批量启用/删除/授权;按「无批量」处理,不测批量提交"), + seq.add("中", "分页翻页", "总条数大于一页时可翻至第 2 页,数据更换且页码高亮正确"), + seq.add("中", "每页条数切换", "切换「50 条/页」等后当前页行数不超过所选值,总数不变"), + seq.add("中", "跳至指定页", "分页「跳至」输入合法页码后跳到对应页"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "首页上一页不可用", "第 1 页时「上一页」禁用或点击无效"), + seq.add("中", "末页下一页不可用", "最后一页时「下一页」禁用或点击无效"), + seq.add("低", "跳至超出总页数", "跳至大于总页数的页码时被拒绝、纠正到末页或给出提示"), + ], + ), + ( + "异常验证点", + [ + seq.add("低", "跳至非数字页码", "跳至输入非法字符时不跳转异常页,输入被忽略或提示"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "翻页不丢失当前筛选", "带筛选条件翻页后条件保持,新页数据仍满足原筛选"), + seq.add("中", "科室任职数与任职记录一致", "「科室任职数」等于该用户在各科室任职条数,无任职为 `--`"), + seq.add("中", "任职数展示或可下钻", "任职数为只读数字;若可点开明细,则明细条数与数字一致"), + ], + ), + ], + ), + section( + "新增用户", + [ + ( + "功能验证点", + [ + seq.add("高", "打开新增用户弹窗", "点「新增」弹出新增/编辑同类表单,含账号、姓名、联系方式、医院员工编号、启用/禁用、取消/确定"), + seq.add("高", "合法必填保存成功", "填写必填后确定,弹窗关闭,列表出现新记录且字段与提交一致"), + seq.add("中", "取消不落库", "填写后点「取消」或关闭,列表不新增对应账号"), + seq.add("中", "Excel 导入入口可达", "工具栏「Excel导入」可打开导入流程或选择文件"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "字段达到允许最大长度", "账号/姓名取最大允许长度可保存成功"), + seq.add("中", "联系方式合法号码", "填写合法手机号/电话可保存,列表联系方式与提交一致"), + ], + ), + ( + "异常验证点", + [ + seq.add("高", "必填为空拦截", "账号或姓名为空点确定被拦截并提示,不关闭弹窗、不落库"), + seq.add("高", "登录账号重复拦截", "使用已存在账号保存失败并提示,不覆盖原用户"), + seq.add("中", "联系方式非法拦截", "明显非法号码(过短数字)点确定被拦截并提示,不落库"), + seq.add("中", "保存接口失败", "失败有错误提示,弹窗仍在,列表不出现半成品成功态"), + seq.add("中", "重复点击确定", "连续快速点击只产生一条记录或第二次被防重"), + seq.add("中", "Excel 导入失败有提示", "导入文件失败或模板不符时有错误提示,不误导为成功"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "新用户可出现在医共体用户管理", "本页新增成功后,医共体用户管理按所属医院能查到同一登录账号(若业务归属医共体)"), + ], + ), + ], + ), + section( + "编辑用户", + [ + ( + "功能验证点", + [ + seq.add("高", "打开编辑用户弹窗并回填", "行内「编辑」打开「编辑用户」,回填账号、姓名、联系方式、医院员工编号、启用/禁用"), + seq.add("中", "编辑页可查看全部角色", "多角色用户在编辑弹窗能看到完整角色列表,不被列表截断值替代"), + seq.add("中", "签名图片可上传或展示", "编辑弹窗含签名图片,已有签名可展示,可重新上传后随确定保存"), + seq.add("高", "修改后确定落库", "修改姓名或联系方式后确定,列表与再次编辑回看均为新值"), + seq.add("中", "编辑取消不保存", "修改后点「取消」,列表与再次打开仍为修改前值"), + seq.add("高", "启用/禁用切换", "在编辑弹窗将状态改为禁用或启用并确定后,高级筛选按新状态能查到该用户"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "不改字段直接确定", "打开编辑不改内容点确定,记录保持原值且不报错"), + seq.add("中", "编辑时账号只读", "编辑弹窗中登录账号不可改"), + seq.add("低", "不传签名可保存", "签名非必填时不上传仍可确定成功"), + ], + ), + ( + "异常验证点", + [ + seq.add("高", "编辑清空必填拦截", "清空姓名后确定被拦截,原记录不被写成空"), + seq.add("中", "编辑已删除用户", "记录已被删除后再确定,提示不存在,不产生幽灵更新"), + seq.add("中", "签名非图片格式拦截", "上传非图片或超限文件时拒绝保存并提示"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "禁用后登录受限", "用户禁用后使用该账号登录被拒绝或权限收回"), + seq.add("中", "再启用保持原授权", "禁用后再启用,菜单授权仍为禁用前已保存的勾选,不额外膨胀、不自动清空"), + ], + ), + ], + ), + section( + "授权", + [ + ( + "功能验证点", + [ + seq.add("高", "打开授权", "行内「授权」打开授权层(弹窗或权限树),展示可勾选菜单/功能"), + seq.add("高", "保存后勾选持久化", "勾选后保存,关闭再打开同一用户,勾选与保存时一致"), + seq.add("中", "取消不生效", "勾选后点取消/关闭,再次打开仍为操作前勾选"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "未勾选任何权限可保存", "全部取消勾选后仍可保存成功,再次打开为全不勾选"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "授权保存失败", "接口失败有提示,再次打开不是半成功勾选"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "授权后仅可见已授权菜单", "保存后该用户登录仅能看到已授权菜单"), + seq.add("中", "授权节点与角色权限树语义一致", "用户授权层菜单节点名称与角色管理权限树对应节点一致"), + ], + ), + ], + ), + section( + "重置密码", + [ + ( + "功能验证点", + [ + seq.add("高", "行内重置密码", "点「重置密码」出现确认或密码表单,确认后该用户密码被更新"), + seq.add("中", "编辑弹窗内重置密码", "「编辑用户」内「重置密码」与行内重置效果一致"), + seq.add("中", "取消不改密", "确认框点取消后原密码仍有效"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "对已禁用用户可重置", "禁用用户允许重置密码,重置后账号仍为禁用、不误启用"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "重置接口失败", "失败有提示,原密码仍可登录"), + seq.add("中", "无二次确认不得改密", "重置须经确认框;直接关闭或点取消不得改密"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "重置后原密码不可登录", "重置成功后原密码登录失败,新密码(或初始密码规则)可登录"), + ], + ), + ], + ), + ] + return "\n---\n\n".join(chunks) + + +def union_body(seq: Seq) -> str: + chunks = [ + section( + "筛选与查询", + [ + ( + "功能验证点", + [ + seq.add("高", "默认条件查询", "进入后点「查询」,列表有数据或空态明确,「共 N 条」与行数逻辑一致"), + seq.add("高", "姓名/登录账号关键字", "「姓名」框(占位「姓名、登录账号」)输入后结果姓名或账号匹配"), + seq.add("高", "所属医院筛选", "切换「所属医院」后列表仅该院用户,「共 N 条」同步变化"), + seq.add("高", "联系方式筛选", "按「联系方式」关键字查询,结果联系方式匹配"), + seq.add("高", "姓名+医院+联系方式组合查询", "多条件同时生效,结果同时满足"), + seq.add("中", "重置恢复默认", "点「重置」后医院等条件恢复默认(含默认医院),列表刷新"), + seq.add("中", "展开高级筛选", "点「高级筛选」展开后可见所属医院、联系方式、科室、角色及「筛选」"), + seq.add("中", "高级筛选按科室过滤", "高级筛选选择科室后点「筛选」,列表「所属科室」与所选一致"), + seq.add("中", "高级筛选按角色过滤", "高级筛选选择角色后点「筛选」,列表「角色」列包含该角色"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "超长关键字", "超长姓名或联系方式关键字不崩溃,按规则截断或无匹配空态"), + seq.add("低", "仅空格联系方式", "空格按空查询处理或提示,不报错"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "查询接口失败", "失败有提示,不白屏"), + seq.add("低", "无匹配空态", "总数为 0,空态明确"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "筛选结果与分页总数一致", "当前医院+条件下可见行逻辑与「共 N 条」一致"), + seq.add("高", "默认医院与顶栏/登录医院一致", "进入页时所属医院默认值与当前登录医院或顶栏院区约定一致"), + ], + ), + ], + ), + section( + "列表与分页", + [ + ( + "功能验证点", + [ + seq.add( + "高", + "列表核心列完整且顺序正确", + "列顺序为登录账号、姓名、联系方式、所属医院、所属科室、医院员工编号、角色、科室任职数、操作(编辑/授权/重置密码);勿用用户管理页列断言", + ), + seq.add("中", "登录账号列截断", "长账号在列宽内截断;按账号搜索/断言用筛选框而非截断后的单元格文本"), + seq.add("中", "分页翻页", "总条数大于一页时可翻至第 2 页,数据更换且页码高亮正确"), + seq.add("中", "每页条数切换", "切换每页条数后当前页行数不超过所选值,总数不变"), + seq.add("中", "跳至指定页", "分页「跳至」输入合法页码后跳到对应页"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "首页上一页不可用", "第 1 页「上一页」禁用或无效"), + seq.add("中", "末页下一页不可用", "最后一页时「下一页」禁用或点击无效"), + seq.add("低", "某医院无用户", "切换到无用户医院时空态明确,总数为 0"), + ], + ), + ( + "异常验证点", + [ + seq.add("低", "跳至非数字页码", "非法页码不跳转异常页"), + ], + ), + ( + "集成验证点", + [ + seq.add( + "高", + "与用户管理页同一用户字段一致", + "同一登录账号在两页的姓名、角色、所属科室一致,本页仅多「所属医院」维度", + ), + seq.add("中", "翻页不丢失医院条件", "带所属医院筛选翻页后条件保持"), + seq.add("中", "科室任职数规则同用户管理", "本页「科室任职数」判定同 TP_USER_USR 任职数与任职记录一致,无任职为 `--`"), + ], + ), + ], + ), + section( + "新增与编辑", + [ + ( + "功能验证点", + [ + seq.add("高", "打开新增弹窗", "点「新增」弹出用户表单,含账号、姓名、联系方式等及取消/确定"), + seq.add("高", "合法保存落在当前医院", "保存成功后列表在当前「所属医院」下可见该账号"), + seq.add("中", "新增取消不落库", "填写后点取消,当前医院列表不出现该账号"), + seq.add("高", "打开编辑并回填", "行内「编辑」打开「编辑用户」,回填与列表行一致"), + seq.add("高", "修改后确定落库", "修改后确定,本页列表与再次编辑回看均为新值"), + seq.add("中", "编辑取消不保存", "修改后点取消,列表与再次打开仍为修改前值"), + seq.add("中", "启用禁用规则同用户管理", "编辑弹窗启用/禁用后,按状态筛选能命中;禁用登录受限同 TP_USER_USR"), + seq.add("中", "签名规则同用户管理", "编辑弹窗签名上传/展示规则同 TP_USER_USR,本页不重复展开"), + seq.add("中", "Excel 导入入口可达", "「Excel导入」可打开导入流程"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "字段最大长度", "账号/姓名最大长度可保存"), + ], + ), + ( + "异常验证点", + [ + seq.add("高", "必填为空拦截", "账号或姓名为空确定被拦截,不落库"), + seq.add("高", "跨院重复账号不静默覆盖", "使用他院已占用登录账号保存时拒绝并提示,不覆盖他院用户"), + seq.add("中", "Excel 导入失败有提示", "导入文件失败或模板不符时有错误提示,不误导为成功"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "本页编辑结果回写用户管理", "在医共体页修改姓名/联系方式后,用户管理页同一账号字段一致"), + seq.add("中", "Excel 导入归属当前医院", "导入成功的数据出现在当前「所属医院」列表,不写入其它院"), + ], + ), + ], + ), + section( + "授权与重置密码", + [ + ( + "功能验证点", + [ + seq.add("高", "行内授权可打开", "点「授权」打开授权层;勾选持久化规则同 TP_USER_USR,本页只校验当前行"), + seq.add("高", "行内重置密码可执行", "点「重置密码」可完成改密;改密后登录规则同 TP_USER_USR"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "对他院用户写操作不可用", "筛选他院后,对非当前医院用户的编辑/授权/重置入口不可见或拒绝"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "授权保存失败", "接口失败有提示,再次打开不是半成功勾选"), + seq.add("中", "重置密码失败", "接口失败有提示,原密码仍可登录"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "编辑不串改他院同名用户", "保存编辑后仅当前所属医院下该账号变更,其它院区同名用户不变"), + seq.add("高", "授权不串改他院账号", "授权只作用于当前行登录账号+所属医院,其它院区同账号勾选不变"), + seq.add("高", "重置密码不串改他院账号", "重置密码只改当前行,其它院区同账号原密码仍有效"), + ], + ), + ], + ), + ] + return "\n---\n\n".join(chunks) + + +def role_body(seq: Seq) -> str: + chunks = [ + section( + "左侧角色列表", + [ + ( + "功能验证点", + [ + seq.add("高", "角色列表加载", "左侧列出角色名称(如医院管理员、中心库库管、护士等),可点选"), + seq.add("高", "角色名称搜索过滤", "在角色名称框输入关键字后列表收敛为匹配项(即时或回车,以现网为准)"), + seq.add("高", "选中角色刷新右侧权限树", "点击某角色后右侧菜单权限勾选刷新为该角色配置,无上一角色残留"), + seq.add("中", "清空搜索恢复全量", "清空角色搜索后列表恢复未过滤的完整角色"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "搜索超长关键字", "超长不崩溃,无匹配时左栏空态明确"), + seq.add("低", "仅空格搜索", "按空处理或提示,不报错"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "角色列表加载失败", "失败有提示,不白屏"), + seq.add("低", "无匹配角色空态", "搜索无结果时空态明确"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "角色名与用户管理角色下拉同源", "左栏角色名称集合与用户管理/医共体用户管理「角色」筛选项一致(允许按权限裁剪)"), + ], + ), + ], + ), + section( + "右侧菜单权限树", + [ + ( + "功能验证点", + [ + seq.add( + "高", + "权限树按应用与模块展开", + "右侧为「菜单权限」树,根为「SPD内网系统PC端」,下含基础设置/运维管理/厂商管理/产品管理等模块;节点可展开/折叠,展开后可见子菜单", + ), + seq.add("高", "勾选父节点联动子节点", "勾选某模块父节点后其子菜单/功能全部勾选"), + seq.add("高", "取消父节点联动取消子节点", "取消父节点后子节点全部取消"), + seq.add("中", "部分勾选呈半选态", "只勾选部分子节点时父节点为半选"), + seq.add("高", "保存授权持久化", "点「保存」成功后刷新或重新进入,该角色勾选与保存时一致"), + seq.add("中", "刷新恢复未保存勾选", "改勾选后点「刷新」且未保存,树恢复为上次已保存状态"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "同名菜单按层级定位", "不同模块下同名菜单(若有)勾选互不影响,须按「模块名 + 菜单名」判定"), + seq.add("低", "树节点很多时可滚动勾选", "长树滚动后勾选仍对应正确节点,不点错行"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "未选角色直接保存", "未选中角色时保存被拦截或提示,不把勾选写到错误角色"), + seq.add( + "高", + "未保存勾选时切换角色", + "改勾选未保存即点另一角色:须提示未保存;确认后丢弃未保存勾选并加载新角色,不把未保存勾选静默写到原角色或新角色", + ), + seq.add("中", "保存接口失败", "失败有提示,再次进入不是半成功勾选"), + seq.add("中", "权限树加载失败", "失败有提示,左栏角色仍可选"), + ], + ), + ( + "集成验证点", + [ + seq.add( + "高", + "角色授权后用户可见对应菜单", + "为角色勾选「厂商查询」等菜单并保存后,仅绑定该角色的用户登录后能看到对应菜单", + ), + seq.add("高", "取消角色菜单后用户收回", "取消该角色已勾选菜单并保存后,仅绑定该角色的用户登录不再看到该菜单"), + ], + ), + ], + ), + section( + "角色增删改", + [ + ( + "功能验证点", + [ + seq.add("中", "新增角色入口可达", "通过新增/「角色」工具按钮打开新增表单,可填写角色名称并保存,左栏出现新角色"), + seq.add("高", "编辑角色名称", "行内编辑可改角色名称,保存后左栏显示新名"), + seq.add("高", "删除需二次确认", "点删除出现确认框,含取消/确定"), + seq.add("高", "确认删除后左栏移除", "确认后该角色从左栏消失,再搜索名称无该角色"), + seq.add("中", "取消删除保留角色", "确认框点取消后角色仍在,权限树勾选不变"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "角色名最大长度", "达到允许最大长度可保存"), + seq.add("低", "删除列表中最后一个自定义角色", "删除后左栏不报错,权限树进入空态或不可保存"), + ], + ), + ( + "异常验证点", + [ + seq.add("高", "角色名称为空拦截", "空名称保存被拦截并提示"), + seq.add("高", "角色名称重复拦截", "与已有角色重名保存失败,不覆盖"), + seq.add("高", "预置角色不可删除", "医院管理员等预置角色删除入口不可见或确定后拒绝,角色仍在左栏"), + seq.add("高", "删除仍被用户引用的角色", "若有用户绑定该自定义角色则拒绝删除或提示先解绑,不出现孤儿授权"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "新增后用户页角色下拉出现", "新增角色保存后,用户管理/医共体用户管理「角色」筛选项出现该名称"), + seq.add("高", "改名后用户页角色文案同步", "改名保存后,用户页下拉与已绑用户行「角色」列为新名"), + seq.add("高", "删除后用户页无已删角色残留", "删除自定义角色后,用户页下拉与行「角色」列不再出现该名称"), + ], + ), + ], + ), + ] + return "\n---\n\n".join(chunks) + + +def quality(p: dict, count: int, tp_count: int) -> str: + kind = p["kind"] + if kind == "user": + cover = "导航、筛选查询、列表分页、新增/编辑(含签名与启用禁用)、授权、重置密码、权限与登录异常、同模块衔接" + pending = ( + "① 新增弹窗扫描未稳定打开,字段以编辑弹窗与 UI-SPEC 为准 " + "② 授权层扫描未采集到,勾选控件以现网为准 " + "③ 编辑时登录账号按只读写点,若现网可改需改判定 " + "④ 空权限按允许保存写点,联系方式非法/签名非图片按拦截写点,禁用用户重置按允许且不启用写点 " + "⑤ UI-SPEC 截图未见「查询」,现网扫描有「查询」,测试点以扫描为准 " + "⑥ 列表序号可勾选但无批量工具栏,按无批量处理" + ) + elif kind == "union": + cover = "导航、所属医院筛选、列顺序差异、跨院不覆盖/不串改;启用禁用/签名/任职/授权持久化引用 TP_USER_USR,本页不重复展开" + pending = ( + "① 新增/授权弹窗扫描未稳定打开,以现网为准 " + "② 跨院登录账号按拒绝且不覆盖写点,若产品允许共用需改判定 " + "③ 所属医院默认值与顶栏约定以现网为准 " + "④ 签名/启用禁用/任职明细/改密后登录同用户管理对应 TP,本页只保留医院维度" + ) + else: + cover = "导航(左右分栏无分页)、左栏角色搜索、右栏权限树勾选联动/保存/未保存切角色、角色增删改与用户页下拉同步" + pending = ( + "① 新增角色弹窗扫描未打开,入口以现网工具栏/行内图标为准,对应点优先级为中 " + "② 预置角色按不可删除写点,以现网预置名单为准 " + "③ 未保存切角色按「提示后丢弃、不静默写入」写点,若自动保存需改判定 " + "④ 本页无授权弹窗、无分页、无所属医院" + ) + return f"""## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:{cover};共 **{tp_count}** 条原子测试点(未声称全覆盖,断网类与仓库其它基础管理页同样省略) | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分;无某类时保留标题并说明 | +| 去重 | 登录拦截、会话过期与同模块其它用户管理页规则相同,本页仅保留菜单/URL/字段差异;编号前缀 `{p["prefix"]}` | +| 待评审项 | {pending} | +| 下游 | 可转结构化 MD/CSV;扫描 `npm run discover:scan-user`;自动化建议 `pages/spd/{p["pageKey"]}.page.ts` | +""" + + +def render_testpoint(p: dict, scan: dict) -> tuple[str, int]: + spec = f"UI-SPEC-SPD-{p['specId']}" + seq = Seq(p["prefix"], spec) + nav = nav_section(p, scan["url"], seq) + if p["kind"] == "user": + body = user_body(seq) + elif p["kind"] == "union": + body = union_body(seq) + else: + body = role_body(seq) + md = f"""# 基础管理-用户管理-{p['name']} — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/{spec}.md` +**页面 URL**:`{scan["url"]}` +**版本 / 定稿日期**:{TODAY} +**元素扫描**:`data/scan/{p["pageKey"]}-elements.json`({scan["elementCount"]} 项) +**定位器**:`locators/spd/{p["pageKey"]}.locator.yaml` + +--- + +## 基础设置 · 用户管理 · {p['name']} + +{nav} +--- + +{body} + +--- + +{quality(p, scan["elementCount"], seq.n)} +""" + return md, seq.n + + +def patch_ui_spec(p: dict, scan: dict, tp_file: str) -> None: + path = KB / f"UI-SPEC-SPD-{p['specId']}.md" + text = path.read_text(encoding="utf-8") + text = re.sub(r'update_time: "[^"]*"', f'update_time: "{TODAY}"', text, count=1) + text = re.sub(r'related_tc: "[^"]*"', f'related_tc: "testpoint/{tp_file}"', text, count=1) + url = scan["url"] + count = scan["elementCount"] + page_key = p["pageKey"] + extra = "" + if p["kind"] == "user": + extra = "| 浏览器页签标题 | `用户管理` |\n" + elif p["kind"] == "union": + extra = "| 浏览器页签标题 | `医共体用户管理` |\n" + table = f"""| 项 | 值 | +|----|-----| +| 主框架 URL | `{url}` | +{extra}| iframe 选择器 | 无(整页渲染,非 iframe 报表) | +| 元素扫描 JSON | `data/scan/{page_key}-elements.json`({count} 项) | +| 定位器 YAML | `locators/spd/{page_key}.locator.yaml` | +| 测试环境示例 | `http://spdtest.cmic.com.cn:8080/spd/form/` |""" + text = re.sub( + r"\| 项 \| 值 \|\n\|----\|-----\|\n(?:\| .+\|\n)+", + table + "\n", + text, + count=1, + ) + text = text.replace( + "1. 完成元素扫描后,补充 `data/scan/`、`locators/spd/` 与 Page Object(建议 `pages/spd/" + + page_key + + ".page.ts`)。", + f"1. 元素扫描已完成({count} 项):`data/scan/{page_key}-elements.json`;定位器 `locators/spd/{page_key}.locator.yaml`;下一步补充 Page Object 与用例。", + ) + path.write_text(text, encoding="utf-8") + + +def patch_index(counts: dict[str, int]) -> None: + text = INDEX.read_text(encoding="utf-8") + text = re.sub(r'update_time: "[^"]*"', f'update_time: "{TODAY}"', text, count=1) + repl = { + "USER-001": ( + "| 用户管理 | USER-001 | `/spd/basic/user/platform/person` | " + f"已扫描 · `basic-user` · **219** 项 · 测试点 **{counts['USER-001']}** 条 · `TP_USER_USR` |" + ), + "USER-002": ( + "| 医共体用户管理 | USER-002 | `/spd/basic/user/platform/personCom` | " + f"已扫描 · `basic-user-union` · **221** 项 · 测试点 **{counts['USER-002']}** 条 · `TP_USER_UNI` |" + ), + "USER-003": ( + "| 角色管理 | USER-003 | `/spd/basic/user/platform/role` | " + f"已扫描 · `basic-role` · **1170** 项(含权限树) · 测试点 **{counts['USER-003']}** 条 · `TP_USER_ROL` |" + ), + } + text = re.sub( + r"\| 用户管理 \| USER-001 \| `/spd/basic/user/platform/person` \| .+ \|", + repl["USER-001"], + text, + count=1, + ) + text = re.sub( + r"\| 医共体用户管理 \| USER-002 \| `/spd/basic/user/platform/personCom` \| .+ \|", + repl["USER-002"], + text, + count=1, + ) + text = re.sub( + r"\| 角色管理 \| USER-003 \| `/spd/basic/user/platform/role` \| .+ \|", + repl["USER-003"], + text, + count=1, + ) + if "generate:testpoints-user" not in text: + text = text.replace( + "npm run discover:scan-user\n", + "npm run discover:scan-user\nnpm run generate:testpoints-user\n", + ) + INDEX.write_text(text, encoding="utf-8") + + +def main() -> None: + OUT_DIR.mkdir(parents=True, exist_ok=True) + counts: dict[str, int] = {} + for p in PAGES: + scan = load_scan(p["pageKey"]) + md, n = render_testpoint(p, scan) + out = OUT_DIR / p["file"] + out.write_text(md, encoding="utf-8") + patch_ui_spec(p, scan, p["file"]) + counts[p["specId"]] = n + print(f"OK {p['specId']}\t{p['name']}\t{scan['elementCount']} el\t{n} TP\t{out.name}") + patch_index(counts) + print("done", counts) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate-vendor-md-csv.py b/scripts/generate-vendor-md-csv.py new file mode 100644 index 0000000..3d95fc4 --- /dev/null +++ b/scripts/generate-vendor-md-csv.py @@ -0,0 +1,560 @@ +#!/usr/bin/env python3 +"""生成基础管理-厂商管理 10 页结构化 MD(testcases/)与同源 CSV(testcase/)。""" +from __future__ import annotations + +import csv +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +MD_DIR = ROOT / "testcases" +CSV_DIR = ROOT / "testcase" + +# case: (seq, h3, h4, prio P0/P1/P2, title, tp_suffix or full tp, pre, steps_list, expect, auto_id optional) +# tp_suffix: used as TP_VENDOR_{PREFIX}_{suffix:03d} if int, else literal TP string + +PAGES: list[dict] = [] + + +def page(**kwargs) -> dict: + return kwargs + + +def case( + seq: int, + h3: str, + h4: str, + prio: str, + title: str, + tp: str | int, + steps: list[str], + expect: str, + pre: str = "", + auto: str | None = None, +) -> dict: + return { + "seq": seq, + "h3": h3, + "h4": h4, + "prio": prio, + "title": title, + "tp": tp, + "steps": steps, + "expect": expect, + "pre": pre, + "auto": auto, + } + + +PAGES = [ + page( + code="VENDOR-QRY", + prefix="QRY", + name="厂商查询", + path="/spd/basic/mfrs/paltform/mfrsName/factory", + scan="vendor-query", + count=47, + spec="UI-SPEC-SPD-VENDOR-001", + spec_file="vendor-query.spec.ts", + iframe=False, + write_note="本页只读查询,无新增/编辑提交。", + samples="从器械注册人 Tab 首行读取 `${sampleMfrsName}`;禁止写死厂商名称;mock:`__NOT_EXIST_MFRS__99999`。", + cases=[ + case(1, "页面进入", "功能验证点", "P0", "菜单进入厂商查询页", 1, + ["按菜单「基础设置 → 厂商管理 → 厂商查询」进入", + "断言 URL 含 `/spd/basic/mfrs/paltform/mfrsName/factory`", + "断言可见「器械注册人」「经销商」Tab、厂商名称搜索框及列表表头"], + "进入厂商查询页;不误入供应商资质管理。", + pre="已登录且具备厂商管理权限", auto="VENDOR-QRY-001"), + case(2, "Tab 与搜索", "功能验证点", "P0", "默认器械注册人列表有数据或空态明确", 9, + ["切换或停留「器械注册人」", "断言出现数据行,或「暂无数据」且分页总数为 0"], + "器械注册人列表稳定;有数据时分页「共 N 条」N≥1。", auto="VENDOR-QRY-002"), + case(3, "Tab 与搜索", "功能验证点", "P0", "切换经销商 Tab", 10, + ["点击「经销商」", "断言 Tab 激活,列表有数据或空态明确"], + "经销商列表加载成功;总数可与器械注册人不同。", auto="VENDOR-QRY-003"), + case(4, "Tab 与搜索", "功能验证点", "P0", "按厂商名称搜索", 11, + ["从首行读取 `${sampleMfrsName}` 取关键字", "在厂商名称搜索框填入关键字并回车", + "断言结果行「厂商名称」包含关键字"], + "厂商名称列与关键字匹配。", + pre="器械注册人至少 1 条且厂商名称非空", auto="VENDOR-QRY-004"), + case(5, "Tab 与搜索", "功能验证点", "P1", "清空搜索恢复", 13, + ["在已过滤状态下清空搜索框并回车", "断言分页总数不少于过滤后"], + "恢复更宽结果集。", pre="已按厂商名称过滤", auto="VENDOR-QRY-005"), + case(6, "Tab 与搜索", "异常验证点", "P1", "无匹配空态(mock反例)", 17, + ["厂商名称填入 `__NOT_EXIST_MFRS__99999` 并回车", "断言空态且分页总数为 0"], + "无匹配时不报错;总数为 0。", auto="VENDOR-QRY-006"), + case(7, "Tab 与搜索", "功能验证点", "P0", "列表核心列完整", 12, + ["断言表头含序号、厂商编号、厂商名称、产地、详细地址、简称、开户银行、银行账号"], + "核心列可见。", auto="VENDOR-QRY-007"), + case(8, "Tab 与搜索", "功能验证点", "P1", "分页翻至第 2 页", 13, + ["总数足够时翻到第 2 页", "断言列表可加载;不足两页则 skip"], + "第 2 页可加载。", pre="器械注册人总数足够翻页", auto="VENDOR-QRY-008"), + case(9, "Tab 与搜索", "边界验证点", "P2", "仅空格关键字搜索", 15, + ["厂商名称仅填空格并回车", "断言按空查询处理或提示,不白屏"], + "不报错;行为明确。"), + case(10, "页面进入", "异常验证点", "P0", "未登录访问拦截", 5, + ["清除登录态后直接打开厂商查询 URL", "断言跳转登录或不展示业务列表"], + "未登录不可见厂商数据。"), + case(11, "Tab 与搜索", "集成验证点", "P1", "厂商名称与资质管理进院供应商可对照", 18, + ["记录厂商查询中某厂商名称(若有)", "打开供应商资质管理,用同源关键字检索或目视对照", + "断言不出现明显主数据串名(允许一方无记录)"], + "厂商主数据跨菜单可对照,不串名。"), + ], + ), + page( + code="VENDOR-QUL", + prefix="QUL", + name="供应商资质管理", + path="/spd/basic/mfrs/basic/qcManage", + scan="vendor-qual", + count=99, + spec="UI-SPEC-SPD-VENDOR-002", + spec_file="vendor-qual.spec.ts", + iframe=False, + write_note="本批只读:查看打开后关闭;不确认导出下载、不改证件。", + samples="证件效期提醒 Tab 首行读取 `${sampleHospitalSupplier}`;mock:`__NOT_EXIST_SUPPLIER__99999`。", + cases=[ + case(1, "页面进入", "功能验证点", "P0", "菜单进入供应商资质管理页", 1, + ["按菜单「基础设置 → 厂商管理 → 供应商资质管理」进入", + "断言 URL 含 `/spd/basic/mfrs/basic/qcManage`", + "断言可见「查 询」「重 置」及证件效期提醒相关入口"], + "进入供应商资质管理;不误入厂商查询。", + pre="已登录且具备厂商管理权限", auto="VENDOR-QUL-001"), + case(2, "筛选与 Tab", "功能验证点", "P0", "默认查询列表有数据或空态明确", 9, + ["点击「查 询」并等待列表稳定", "断言出现数据行或空态明确"], + "查询无报错;有数据时分页 N≥1。", auto="VENDOR-QUL-002"), + case(3, "筛选与 Tab", "功能验证点", "P0", "切换授权链路效期预警 Tab", 9, + ["点击「授权链路效期预警」", "断言列表有数据或空态明确"], + "Tab 切换成功,列表字段可随 Tab 变化。", auto="VENDOR-QUL-003"), + case(4, "筛选与 Tab", "功能验证点", "P0", "近三个月到期计数一致", 10, + ["切换「近三个月到期」(或等价文案)", + "若可读到括号/冒号计数,断言等于分页「共 N 条」"], + "计数与分页一致(可读时)。", auto="VENDOR-QUL-004"), + case(5, "筛选与 Tab", "功能验证点", "P0", "按进院供应商筛选", 11, + ["从首行读取 `${sampleHospitalSupplier}` 取关键字", + "在进院供应商名称填入关键字并「查 询」", + "断言结果行「进院供应商」包含关键字"], + "进院供应商列与条件一致。", + pre="证件效期提醒至少 1 条且进院供应商非空", auto="VENDOR-QUL-005"), + case(6, "筛选与 Tab", "功能验证点", "P1", "重置恢复默认", 11, + ["填写筛选条件后点击「重 置」", "断言列表按默认规则刷新"], + "筛选恢复默认。", auto="VENDOR-QUL-006"), + case(7, "筛选与 Tab", "异常验证点", "P1", "无匹配空态(mock反例)", 16, + ["进院供应商名称填入 `__NOT_EXIST_SUPPLIER__99999`", "点击「查 询」", + "断言空态且分页总数为 0"], + "无匹配时不报错;总数为 0。", auto="VENDOR-QUL-007"), + case(8, "筛选与 Tab", "功能验证点", "P0", "证件效期提醒核心列完整", 12, + ["处于证件效期提醒 Tab", + "断言表头含序号、数据来源、进院供应商、有效期至、证件名称、证件类型、效期提醒、操作"], + "核心列可见。", auto="VENDOR-QUL-008"), + case(9, "查看与导出", "功能验证点", "P0", "打开查看后关闭", 14, + ["点击首行「查 看」", "断言详情层可见", "关闭/返回,断言回到列表"], + "详情可打开;关闭后回到列表。", + pre="列表至少 1 行含「查看」", auto="VENDOR-QUL-009"), + case(10, "查看与导出", "功能验证点", "P1", "导出按钮可见", 15, + ["断言「导出」或「导出明细」按钮可见(本批不确认下载)"], + "导出入口可见。", auto="VENDOR-QUL-010"), + case(11, "筛选与 Tab", "边界验证点", "P2", "制单/效期日期起止同一天", 15, + ["若存在日期区间筛选,起止选同一天并查询", "断言仅返回该日数据或空态明确,不报错"], + "同日区间行为明确。"), + case(12, "筛选与 Tab", "集成验证点", "P1", "效期提醒与有效期至抽样一致", 18, + ["在近三个月到期列表抽样一行", "核对「效期提醒」文案与「有效期至」日期逻辑一致"], + "提醒文案与有效期至不矛盾。"), + ], + ), +] + + +def audit_page(code: str, prefix: str, name: str, path: str, scan: str, count: int, spec: str, kind: str) -> dict: + """kind: auth | cert""" + peer = "终审" if "初审" in name else "初审" + filter_label = "进院供应商" if kind == "auth" else "注册证号(新)" + cols = ( + "进院供应商名称、单号、状态、制单时间、制单人、变更类型、原供应商、预转供应商、操作" + if kind == "auth" + else "进院供应商名称、单号、状态、制单时间、制单人、变更类型、注册证号(新)、操作" + ) + return page( + code=code, + prefix=prefix, + name=name, + path=path, + scan=scan, + count=count, + spec=spec, + spec_file="vendor-audit.spec.ts", + iframe=False, + write_note="审批写操作只打开详情后返回,不确认通过/驳回/提交。", + samples=f"默认查询后从首行读取 `${{sampleOrderNo}}`;禁止写死单号;mock:`__NOT_EXIST_ORDER__99999`。", + cases=[ + case(1, "页面进入", "功能验证点", "P0", f"菜单进入{name}页", 1, + [f"按菜单「基础设置 → 厂商管理 → {name}」进入", + f"断言 URL 含 `{path}`", + "断言可见「查 询」「重 置」及制单日期筛选"], + f"进入{name}页;不误入其它变更审批菜单。", + pre="已登录且具备厂商管理权限", auto=f"{code}-001"), + case(2, "筛选与列表", "功能验证点", "P0", "默认查询列表有数据或空态明确", 9, + ["点击「查 询」并等待列表稳定", "断言出现数据行或空态明确"], + "查询无报错;有数据时分页 N≥1。", auto=f"{code}-002"), + case(3, "筛选与列表", "功能验证点", "P0", "按单号筛选", 10, + ["从首行读取 `${sampleOrderNo}` 取关键字", "单号填入关键字并「查 询」", + "断言结果行「单号」包含关键字"], + "单号列与条件一致。", + pre="列表至少 1 条且单号非空", auto=f"{code}-003"), + case(4, "筛选与列表", "功能验证点", "P0", f"按{filter_label}筛选", 11, + [f"按样本填写或选择「{filter_label}」并查询", "断言结果列与条件匹配"], + "筛选条件生效。", + pre="列表至少 1 条", auto=f"{code}-004"), + case(5, "筛选与列表", "功能验证点", "P1", "重置恢复默认", 12, + ["填写筛选项后点击「重 置」", "断言文本条件清空、列表按默认规则刷新"], + "重置成功。", auto=f"{code}-005"), + case(6, "筛选与列表", "异常验证点", "P1", "无匹配空态(mock反例)", 16, + ["单号填入 `__NOT_EXIST_ORDER__99999` 并查询", "断言空态且分页总数为 0"], + "无匹配时不报错;总数为 0。", auto=f"{code}-006"), + case(7, "筛选与列表", "功能验证点", "P0", "列表核心列完整", 13, + [f"断言表头含 {cols}", "断言存在「详情」操作"], + "核心列与详情入口可见。", auto=f"{code}-007"), + case(8, "详情", "功能验证点", "P0", "打开详情后返回", 14, + ["点击行内「详 情」", "断言详情层可见且含单号片段", "返回/关闭,不提交审批"], + "详情只读可返回;列表状态不变。", + pre="存在含「详情」的行", auto=f"{code}-008"), + case(9, "筛选与列表", "边界验证点", "P2", "制单日期起止同一天", 15, + ["制单日期起止选同一天并查询", "断言仅返回该日单据或空态明确"], + "同日区间行为明确。"), + case(10, "详情", "集成验证点", "P0", f"与{peer}单据衔接不串单", 18, + [f"记录本页某单号后切换到对应{peer}菜单", "断言同业务单据状态流转合理、不串到非本业务菜单"], + f"初审/终审衔接正确。"), + ], + ) + + +PAGES.extend( + [ + audit_page("VENDOR-AF1", "AF1", "授权变更初审", "/spd/basic/mfrs/paltform/mfrsName/authAudit", "vendor-auth-first", 40, "UI-SPEC-SPD-VENDOR-003", "auth"), + audit_page("VENDOR-AF2", "AF2", "授权变更终审", "/spd/basic/mfrs/paltform/mfrsName/authAuditFinal", "vendor-auth-final", 40, "UI-SPEC-SPD-VENDOR-004", "auth"), + audit_page("VENDOR-CF1", "CF1", "注册证变更初审", "/spd/basic/mfrs/paltform/mfrsName/regCertAudit", "vendor-cert-first", 40, "UI-SPEC-SPD-VENDOR-005", "cert"), + audit_page("VENDOR-CF2", "CF2", "注册证变更终审", "/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal", "vendor-cert-final", 40, "UI-SPEC-SPD-VENDOR-006", "cert"), + ] +) + +PAGES.append( + page( + code="VENDOR-CDF", + prefix="CDF", + name="注册证差异对比", + path="/spd/basic/mfrs/scjs/yc/certCompare", + scan="vendor-cert-diff", + count=54, + spec="UI-SPEC-SPD-VENDOR-007", + spec_file="vendor-cert-diff.spec.ts", + iframe=True, + write_note="主体在 scjs iframe;导出本批只断言按钮可见,不确认下载。", + samples="iframe 内首行读取 `${sampleMaterialName}`;mock:`__NOT_EXIST_MFRS__99999`。", + cases=[ + case(1, "页面进入", "功能验证点", "P0", "菜单进入注册证差异对比页", 1, + ["按菜单「基础设置 → 厂商管理 → 注册证差异对比」进入", + "断言 URL 含 `certCompare`", + "断言外壳或 iframe 内可见物料名称/全部/一致/不一致等主区"], + "进入差异对比页;iframe 或主区加载成功。", + pre="已登录且具备厂商管理权限", auto="VENDOR-CDF-001"), + case(2, "筛选与对比", "功能验证点", "P0", "默认查询列表有数据或空态明确", 9, + ["在对比页执行查询(若有查询按钮)", "断言列表有数据或空态明确"], + "查询无报错。", auto="VENDOR-CDF-002"), + case(3, "筛选与对比", "功能验证点", "P0", "切换不一致 Tab", 13, + ["点击「不一致」", "断言列表稳定"], + "不一致 Tab 切换成功。", auto="VENDOR-CDF-003"), + case(4, "筛选与对比", "功能验证点", "P1", "切换一致 Tab", 13, + ["点击「一致」", "断言列表稳定"], + "一致 Tab 切换成功。", auto="VENDOR-CDF-004"), + case(5, "筛选与对比", "功能验证点", "P0", "按物料名称筛选", 10, + ["从首行读取 `${sampleMaterialName}` 取关键字", "物料名称填入并查询", + "断言结果行物料名称包含关键字"], + "物料名称匹配。", + pre="对比列表至少 1 条", auto="VENDOR-CDF-005"), + case(6, "筛选与对比", "异常验证点", "P1", "无匹配空态(mock反例)", 16, + ["物料名称填 mock 关键字并查询", "断言空态或总数为 0"], + "无匹配时不报错。", auto="VENDOR-CDF-006"), + case(7, "筛选与对比", "功能验证点", "P0", "列表核心列完整", 14, + ["断言表头含物料编号/物料名称/规格型号/生产厂家等关键列"], + "核心对比列可见。", auto="VENDOR-CDF-007"), + case(8, "导出", "功能验证点", "P1", "导出按钮可见", 15, + ["断言「导出」按钮可见(不确认下载)"], + "导出入口可见。", auto="VENDOR-CDF-008"), + case(9, "筛选与对比", "边界验证点", "P2", "仅空格物料名称搜索", 15, + ["物料名称仅填空格并查询", "断言按空查询处理或提示,不白屏"], + "空格关键字不导致页面异常。"), + case(10, "筛选与对比", "集成验证点", "P0", "一致行两侧字段抽样一致", 17, + ["在「一致」Tab 抽样一行", "核对望海与 FLI+ 注册证号/效期关键字段一致"], + "标记一致的行两侧关键字段抽样相同。"), + case(11, "筛选与对比", "集成验证点", "P0", "不一致行至少一侧差异", 18, + ["在「不一致」Tab 抽样一行", "断言望海与 FLI+ 至少一处字段不同"], + "不一致结论与字段差异相符。"), + ], + ) +) + +PAGES.append( + page( + code="VENDOR-EVS", + prefix="EVS", + name="评价方案维护", + path="/spd/basic/mfrs/paltform/mfrsName/evaluationPlan", + scan="vendor-eval-scheme", + count=57, + spec="UI-SPEC-SPD-VENDOR-008", + spec_file="vendor-eval-scheme.spec.ts", + iframe=False, + write_note="新增只打开后取消,不提交保存。", + samples="有数据时从首行读取 `${sampleSchemeCode}`;mock:`__NOT_EXIST_ORDER__99999`。", + cases=[ + case(1, "页面进入", "功能验证点", "P0", "菜单进入评价方案维护页", 1, + ["按菜单进入评价方案维护", "断言 URL 含 `evaluationPlan`", "断言可见查询与新增"], + "进入评价方案维护页。", + pre="已登录且具备厂商管理权限", auto="VENDOR-EVS-001"), + case(2, "筛选与维护", "功能验证点", "P0", "默认查询列表有数据或空态明确", 9, + ["点击查询", "断言列表有数据或「暂无数据」空态"], + "查询无报错。", auto="VENDOR-EVS-002"), + case(3, "筛选与维护", "功能验证点", "P0", "按方案编码筛选", 10, + ["取样本方案编码筛选并查询", "断言方案编码列匹配"], + "方案编码匹配。", + pre="列表有数据", auto="VENDOR-EVS-003"), + case(4, "筛选与维护", "功能验证点", "P1", "重置恢复默认", 11, + ["填写筛选后重置", "断言条件清空、列表刷新"], + "重置成功。", auto="VENDOR-EVS-004"), + case(5, "筛选与维护", "异常验证点", "P1", "无匹配空态(mock反例)", 15, + ["方案编码填 mock 后查询", "断言空态总数 0"], + "无匹配不报错。", auto="VENDOR-EVS-005"), + case(6, "筛选与维护", "功能验证点", "P0", "列表核心列完整", 12, + ["断言表头含方案编码、方案名称、创建人、创建时间、状态、操作"], + "核心列可见。", auto="VENDOR-EVS-006"), + case(7, "新增", "功能验证点", "P0", "打开新增后取消", 13, + ["记录总数", "点击新增打开编辑层", "取消/返回不提交", "断言总数不变"], + "取消不落库。", auto="VENDOR-EVS-007"), + case(8, "新增", "异常验证点", "P0", "必填为空提交拦截", 16, + ["打开新增不填必填项尝试提交(若可点)", "断言拦截提示,页面不关闭或总数不变"], + "空提交被拦截。"), + case(9, "筛选与维护", "边界验证点", "P2", "方案名称超长输入", 15, + ["打开新增,方案名称输入超长字符串(如 200+ 字符)", "断言前端截断/校验提示或后端拒绝,不白屏"], + "超长输入被约束且页面可用。"), + case(10, "筛选与维护", "集成验证点", "P1", "方案与评价指标可关联", 18, + ["在方案配置中查看指标选择(只读打开)", "断言可选指标来源于评价指标维护启用项或规则允许集合"], + "方案与指标主数据衔接正确。"), + ], + ) +) + +PAGES.append( + page( + code="VENDOR-EVI", + prefix="EVI", + name="评价指标维护", + path="/spd/basic/mfrs/paltform/mfrsName/indicators", + scan="vendor-eval-indicator", + count=151, + spec="UI-SPEC-SPD-VENDOR-009", + spec_file="vendor-eval-indicator.spec.ts", + iframe=False, + write_note="详情只读返回;不落库新增/编辑提交。", + samples="右表首行读取 `${sampleIndicatorCode}`;列名可能为指标编码或指标编号;mock:`__NOT_EXIST_ORDER__99999`。", + cases=[ + case(1, "页面进入", "功能验证点", "P0", "菜单进入评价指标维护页", 1, + ["按菜单进入评价指标维护", "断言 URL 含 `indicators`", "断言可见「指标分类」与查询"], + "进入评价指标维护页。", + pre="已登录且具备厂商管理权限", auto="VENDOR-EVI-001"), + case(2, "树与列表", "功能验证点", "P0", "默认查询列表有数据或空态明确", 9, + ["点击查询", "断言右表有数据或空态"], + "查询无报错。", auto="VENDOR-EVI-002"), + case(3, "树与列表", "功能验证点", "P0", "点击左侧分类树", 10, + ["点击左侧首个分类节点", "断言右侧列表刷新"], + "分类切换成功。", auto="VENDOR-EVI-003"), + case(4, "树与列表", "功能验证点", "P0", "按指标编码筛选", 11, + ["取样本指标编码/编号筛选并查询", "断言编码列匹配"], + "指标编码匹配。", + pre="右表有数据", auto="VENDOR-EVI-004"), + case(5, "树与列表", "异常验证点", "P1", "无匹配空态(mock反例)", 15, + ["指标编码填 mock 后查询", "断言空态"], + "无匹配不报错。", auto="VENDOR-EVI-005"), + case(6, "树与列表", "功能验证点", "P0", "列表核心列完整", 12, + ["断言表头含评价类型、指标编码或指标编号、指标名称、状态、操作"], + "核心列可见。", auto="VENDOR-EVI-006"), + case(7, "详情", "功能验证点", "P0", "打开详情后返回", 14, + ["点击行内详情", "断言详情层可见", "返回不保存"], + "详情只读可返回。", + pre="存在详情操作行", auto="VENDOR-EVI-007"), + case(8, "树与列表", "边界验证点", "P2", "分类下无指标空态", 15, + ["选择无指标的分类(若有)", "断言右表空态明确"], + "空分类下列表空态正确。"), + case(9, "树与列表", "集成验证点", "P1", "启用指标可供方案使用", 18, + ["确认状态为启用的指标", "在评价方案维护中核对其可选(只读核对或抽样)"], + "启用指标与方案引用衔接正确。"), + ], + ) +) + +PAGES.append( + page( + code="VENDOR-HLD", + prefix="HLD", + name="供应商节假日管理", + path="/spd/basic/mfrs/basic/prov/holiday", + scan="vendor-holiday", + count=80, + spec="UI-SPEC-SPD-VENDOR-010", + spec_file="vendor-holiday.spec.ts", + iframe=False, + write_note="左侧新增只打开后取消;不批量删除真实数据。", + samples="左侧首行读取 `${sampleHolidayName}`/年度;mock:`__NOT_EXIST_ORDER__99999`。", + cases=[ + case(1, "页面进入", "功能验证点", "P0", "菜单进入供应商节假日管理页", 1, + ["按菜单进入供应商节假日管理", "断言 URL 含 `prov/holiday`", + "断言可见节假日主区与新增"], + "进入供应商节假日管理页。", + pre="已登录且具备厂商管理权限", auto="VENDOR-HLD-001"), + case(2, "左右分栏", "功能验证点", "P0", "选中左侧节假日展示详情", 9, + ["点击左侧首行", "断言默认假日详情或自定义列表区可见"], + "详情联动成功。", + pre="左侧有数据", auto="VENDOR-HLD-002"), + case(3, "左右分栏", "异常验证点", "P1", "左侧搜索无匹配空态(mock反例)", 15, + ["左侧搜索框填 mock 并回车", "断言左侧列表空态"], + "无匹配空态明确。", auto="VENDOR-HLD-003"), + case(4, "维护", "功能验证点", "P0", "左侧新增取消", 11, + ["点击新增", "取消/关闭不保存", "断言仍停留本页且无异常"], + "取消不落库。", auto="VENDOR-HLD-004"), + case(5, "左右分栏", "功能验证点", "P1", "自定义节假日列表区可见", 12, + ["选中左侧行", "断言「供应商自定义节假日列表」区域可见(可为空态)"], + "自定义区可见。", auto="VENDOR-HLD-005"), + case(6, "维护", "异常验证点", "P0", "结束早于开始日期拦截", 16, + ["打开新增/编辑,结束日期早于开始日期尝试保存", "断言拒绝保存并提示"], + "非法日期被拦截。"), + case(7, "维护", "边界验证点", "P2", "开始结束为同一天", 15, + ["打开新增/编辑,开始与结束日期选同一天尝试保存(或仅校验控件)", + "断言允许保存或明确提示规则(按产品规则),不报错白屏"], + "同日区间规则明确。"), + case(8, "左右分栏", "集成验证点", "P1", "进院供应商主数据一致", 18, + ["在自定义列表抽样进院供应商(若有数据)", "断言可在厂商/供应商主数据中找到对应记录"], + "自定义节假日供应商与主数据一致。"), + ], + ) +) + + +def tp_id(p: dict, c: dict) -> str: + tp = c["tp"] + if isinstance(tp, int): + return f"TP_VENDOR_{p['prefix']}_{tp:03d}" + return tp + + +def prio_csv(p: str) -> str: + return {"P0": "高", "P1": "中", "P2": "低"}[p] + + +def render_md(p: dict) -> str: + lines = [ + f"# 基础管理-厂商管理-{p['name']} UI 自动化测试用例", + "", + f"**来源测试点**:`testpoint/基础管理-厂商管理-{p['name']}-测试点分析-01.md` ", + f"**执行 CSV**:`testcase/基础管理-厂商管理-{p['name']}-UI自动化测试用例-01.csv` ", + f"**页面 URL**:`{p['path']}` ", + f"**定位器 YAML**:`locators/spd/{p['scan']}.locator.yaml` ", + f"**元素扫描**:`data/scan/{p['scan']}-elements.json`({p['count']} 项) ", + f"**关联规范**:`knowledge-base/08-UI专项规范库/{p['spec']}.md` ", + f"**版本**:2026-09-30 ", + f"**自动化框架**:Playwright + TypeScript(`tests/spd/{p['spec_file']}`,依赖 `auth.setup` 登录态)", + "", + f"**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 厂商管理 → {p['name']}」权限。 ", + f"**页面约定**:{'主体在 scjs **iframe**。' if p['iframe'] else '整页渲染,**无报表 iframe**。'} ", + f"**写操作**:{p['write_note']} ", + f"**动态取数**:{p['samples']}", + "", + "---", + "", + "## 基础设置 · 厂商管理", + "", + ] + current_h3 = "" + current_h4 = "" + for c in p["cases"]: + h3 = f"{p['name']} · {c['h3']}" + if h3 != current_h3: + current_h3 = h3 + current_h4 = "" + lines += [f"### {h3}", ""] + if c["h4"] != current_h4: + current_h4 = c["h4"] + lines += [f"#### {current_h4}", ""] + case_id = f"{p['code']}-{c['seq']:03d}" + tc = f"TC-UI-{case_id}" + lines += [ + f"##### 【{c['prio']}】【{tc}】【{tp_id(p, c)}】{c['title']}", + "", + f"- 前置条件:{c['pre'] or ('已进入' + p['name'] + '页')}", + "- 操作步骤:", + ] + for i, step in enumerate(c["steps"], 1): + lines.append(f" {i}. {step}") + if c.get("auto"): + lines.append(f"- 自动化断言:与 CSV `{c['auto']}` 及 `tests/spd/{p['spec_file']}` 一致") + else: + lines.append("- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖") + lines += ["", "###### 预期结果", "", c["expect"], "", "---", ""] + # trim trailing --- + while lines and lines[-1] in ("", "---"): + lines.pop() + lines.append("") + return "\n".join(lines) + + +def render_csv_rows(p: dict) -> list[dict]: + rows = [] + # 账号密码仅存 .env / auth.setup;CSV 不写明文凭据 + account = "测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限" + for c in p["cases"]: + case_id = c.get("auto") or f"{p['code']}-{c['seq']:03d}" + pre_parts = [account] + if c["seq"] > 1: + pre_parts.append(f"已进入{p['name']}页({p['path']})") + if c["pre"]: + pre_parts.append(c["pre"]) + steps = "\n".join( + [f"1. 确认 chromium 已加载 storageState(auth.setup),无需在用例内重复登录"] + + [f"{i + 1}. {s}" for i, s in enumerate(c["steps"], start=1)] + ) + rows.append( + { + "用例编号": case_id, + "功能模块": "基础设置 · 厂商管理", + "功能测试点": f"{p['name']} · {c['h3']}", + "用例标题": c["title"], + "优先级": prio_csv(c["prio"]), + "前置条件": ";".join(pre_parts), + "测试步骤": steps, + "预期结果": c["expect"], + "实际结果": "", + } + ) + return rows + + +def main() -> None: + MD_DIR.mkdir(parents=True, exist_ok=True) + CSV_DIR.mkdir(parents=True, exist_ok=True) + total = 0 + for p in PAGES: + md_path = MD_DIR / f"基础管理-厂商管理-{p['name']}-UI自动化测试用例-01.md" + csv_path = CSV_DIR / f"基础管理-厂商管理-{p['name']}-UI自动化测试用例-01.csv" + md_path.write_text(render_md(p), encoding="utf-8") + rows = render_csv_rows(p) + with csv_path.open("w", encoding="utf-8", newline="") as f: + w = csv.DictWriter( + f, + fieldnames=["用例编号", "功能模块", "功能测试点", "用例标题", "优先级", "前置条件", "测试步骤", "预期结果", "实际结果"], + ) + w.writeheader() + w.writerows(rows) + total += len(rows) + print(f"OK {p['code']}\t{len(rows):2d} cases\t{md_path.name}") + print(f"done pages={len(PAGES)} cases={total}") + + +if __name__ == "__main__": + main() diff --git a/scripts/generate-vendor-testpoints.py b/scripts/generate-vendor-testpoints.py new file mode 100644 index 0000000..5c2e468 --- /dev/null +++ b/scripts/generate-vendor-testpoints.py @@ -0,0 +1,577 @@ +#!/usr/bin/env python3 +"""Generate vendor (厂商管理) testpoint MD + patch UI-SPEC URLs from scan summary.""" +from __future__ import annotations + +import json +import re +from datetime import date +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +SCAN_DIR = ROOT / "data" / "scan" +OUT_DIR = ROOT / "testpoint" +KB = ROOT / "knowledge-base" / "08-UI专项规范库" +TODAY = date.today().isoformat() + +PAGES = [ + { + "pageKey": "vendor-query", + "specId": "VENDOR-001", + "name": "厂商查询", + "prefix": "TP_VENDOR_QRY", + "file": "基础管理-厂商管理-厂商查询-测试点分析-01.md", + "kind": "query", + }, + { + "pageKey": "vendor-qual", + "specId": "VENDOR-002", + "name": "供应商资质管理", + "prefix": "TP_VENDOR_QUL", + "file": "基础管理-厂商管理-供应商资质管理-测试点分析-01.md", + "kind": "qual", + }, + { + "pageKey": "vendor-auth-first", + "specId": "VENDOR-003", + "name": "授权变更初审", + "prefix": "TP_VENDOR_AF1", + "file": "基础管理-厂商管理-授权变更初审-测试点分析-01.md", + "kind": "audit", + "peer": "授权变更终审", + "doc_type": "授权变更", + }, + { + "pageKey": "vendor-auth-final", + "specId": "VENDOR-004", + "name": "授权变更终审", + "prefix": "TP_VENDOR_AF2", + "file": "基础管理-厂商管理-授权变更终审-测试点分析-01.md", + "kind": "audit", + "peer": "授权变更初审", + "doc_type": "授权变更", + }, + { + "pageKey": "vendor-cert-first", + "specId": "VENDOR-005", + "name": "注册证变更初审", + "prefix": "TP_VENDOR_CF1", + "file": "基础管理-厂商管理-注册证变更初审-测试点分析-01.md", + "kind": "audit", + "peer": "注册证变更终审", + "doc_type": "注册证变更", + }, + { + "pageKey": "vendor-cert-final", + "specId": "VENDOR-006", + "name": "注册证变更终审", + "prefix": "TP_VENDOR_CF2", + "file": "基础管理-厂商管理-注册证变更终审-测试点分析-01.md", + "kind": "audit", + "peer": "注册证变更初审", + "doc_type": "注册证变更", + }, + { + "pageKey": "vendor-cert-diff", + "specId": "VENDOR-007", + "name": "注册证差异对比", + "prefix": "TP_VENDOR_CDF", + "file": "基础管理-厂商管理-注册证差异对比-测试点分析-01.md", + "kind": "diff", + }, + { + "pageKey": "vendor-eval-scheme", + "specId": "VENDOR-008", + "name": "评价方案维护", + "prefix": "TP_VENDOR_EVS", + "file": "基础管理-厂商管理-评价方案维护-测试点分析-01.md", + "kind": "scheme", + }, + { + "pageKey": "vendor-eval-indicator", + "specId": "VENDOR-009", + "name": "评价指标维护", + "prefix": "TP_VENDOR_EVI", + "file": "基础管理-厂商管理-评价指标维护-测试点分析-01.md", + "kind": "indicator", + }, + { + "pageKey": "vendor-holiday", + "specId": "VENDOR-010", + "name": "供应商节假日管理", + "prefix": "TP_VENDOR_HLD", + "file": "基础管理-厂商管理-供应商节假日管理-测试点分析-01.md", + "kind": "holiday", + }, +] + + +def load_summary() -> dict[str, dict]: + path = SCAN_DIR / "vendor-scan-summary.json" + rows = json.loads(path.read_text(encoding="utf-8")) + return {r["page"]: r for r in rows if r.get("ok")} + + +def tp(prefix: str, n: int, prio: str, title: str, check: str, spec: str) -> str: + return f"- **{prefix}_{n:03d}**({prio})|{title}|验证要点:{check}|关联需求:{spec}" + + +def section(title: str, blocks: list[tuple[str, list[str]]]) -> str: + parts = [f"### {title}", ""] + for h4, lines in blocks: + parts.append(f"#### {h4}") + parts.append("") + parts.extend(lines or ["- 无"]) + parts.append("") + return "\n".join(parts) + + +def nav_section(p: dict, url: str, spec: str) -> str: + pref = p["prefix"] + name = p["name"] + n = 1 + lines_f = [ + tp(pref, n, "高", f"菜单路径可达{name}", f"登录后通过「基础设置 → 厂商管理 → {name}」进入,菜单与 Tab 高亮正确", spec), + ] + n += 1 + lines_f.append( + tp(pref, n, "高", "页面主区加载完成", "整页渲染,筛选/主表/分页或左右分栏关键区可见", spec) + ) + n += 1 + lines_f.append( + tp(pref, n, "中", "同组厂商管理菜单可切换", f"切到其它厂商管理页再返回{name},URL 与内容对应", spec) + ) + lines_b = [ + tp(pref, 4, "低", "已登录直接打开 URL", f"携带有效登录态打开 `{url}`,页面正常渲染", spec), + ] + lines_e = [ + tp(pref, 5, "高", "未登录访问拦截", "无登录态访问跳转登录或不展示业务数据", spec), + tp(pref, 6, "中", "登录态过期", "会话失效后查询/写操作被拦截并提示重新登录", spec), + tp(pref, 7, "中", "无菜单权限", "无权限账号不可见菜单或 URL 被拒绝", spec), + ] + lines_i = [ + tp(pref, 8, "中", "顶栏院区与厂商范围一致", "列表/筛选中的院区或供应商范围与顶栏院区不矛盾", spec), + ] + return section( + "页面访问与导航", + [ + ("功能验证点", lines_f), + ("边界验证点", lines_b), + ("异常验证点", lines_e), + ("集成验证点", lines_i), + ], + ), 8 + + +def body_for(p: dict, spec: str, start: int) -> tuple[str, int]: + kind = p["kind"] + pref = p["prefix"] + n = start + + def nxt(prio: str, title: str, check: str) -> str: + nonlocal n + n += 1 + return tp(pref, n, prio, title, check, spec) + + chunks: list[str] = [] + + if kind == "query": + chunks.append( + section( + "Tab、搜索与列表", + [ + ( + "功能验证点", + [ + nxt("高", "器械注册人 Tab", "切换「器械注册人」后列表为注册人数据,分页「共 N 条」与结果一致"), + nxt("高", "经销商 Tab", "切换「经销商」后列表为经销商数据,总数与器械注册人可不同"), + nxt("高", "按厂商名称搜索", "输入厂商名称关键字后结果名称匹配,无匹配时空态明确"), + nxt("高", "列表核心列", "展示序号、厂商编号、厂商名称、产地、详细地址、简称、开户银行、银行账号"), + nxt("中", "分页与每页条数", "翻页与每页条数切换正确,切换 Tab 后分页重置合理"), + ], + ), + ( + "边界验证点", + [ + nxt("中", "超长厂商名称关键字", "超长输入可查询或被截断并提示,不白屏"), + nxt("低", "仅空格关键字", "按空查询处理或提示,不报错"), + ], + ), + ( + "异常验证点", + [ + nxt("中", "查询接口失败", "失败有错误提示,列表不白屏"), + nxt("低", "无匹配空态", "总数为 0,空态文案明确"), + ], + ), + ( + "集成验证点", + [ + nxt("中", "Tab 与分页总数一致", "当前 Tab 下可见行逻辑与「共 N 条」一致"), + ], + ), + ], + ) + ) + elif kind == "qual": + chunks.append( + section( + "证件 Tab、效期与列表", + [ + ( + "功能验证点", + [ + nxt("高", "证件分类 Tab 切换", "证件效期提醒/授权链路效期预警/供应商三证/器械注册人/注册证号/授权书/承诺书/法人委托书/二级供应商三证可切换且列表字段随 Tab 变化"), + nxt("高", "效期快捷筛选", "近三个月到期、近一个月到期、已过期切换后列表与 Tab 计数一致"), + nxt("高", "证件效期提醒筛选", "按进院供应商名称、证件类型查询,结果匹配"), + nxt("高", "授权链路预警筛选", "按授权书名称、授权单位、被授权单位查询,结果匹配"), + nxt("中", "重置筛选", "重置后条件恢复默认,列表按默认规则刷新"), + nxt("高", "行内查看", "点击「查看」打开证件/授权详情,关键字段与列表行一致"), + nxt("中", "导出", "导出/导出明细与当前 Tab+筛选条件一致"), + ], + ), + ( + "边界验证点", + [ + nxt("中", "某效期 Tab 计数为 0", "列表空态明确,总数为 0"), + nxt("低", "效期临界日", "即将到期与已过期边界日分类正确(以业务规则为准)"), + ], + ), + ( + "异常验证点", + [ + nxt("中", "查询失败", "接口失败有提示,不白屏"), + nxt("中", "导出失败", "导出失败有提示,不误导为成功"), + ], + ), + ( + "集成验证点", + [ + nxt("中", "效期提醒文案与有效期至一致", "「N 天后过期/即将过期」与有效期至抽样一致"), + ], + ), + ], + ) + ) + elif kind == "audit": + doc = p.get("doc_type", "变更") + peer = p.get("peer", "") + is_auth = "授权" in doc + filter_points = [ + nxt("高", "制单日期查询", "按制单日期区间查询,结果制单时间落在区间内"), + ] + if is_auth: + filter_points.extend( + [ + nxt("高", "单号与供应商筛选", "按单号、进院供应商、预转/原供应商查询,结果匹配"), + nxt("高", "状态与变更类型", "状态下拉与变更类型筛选后列表状态/类型匹配"), + ] + ) + if "初审" in p["name"]: + filter_points.append(nxt("中", "医院ID/医院名称筛选", "有该字段时按医院条件查询结果匹配")) + filter_points.append(nxt("中", "组合查询与重置", "多条件同时生效;重置恢复默认")) + filter_points.extend( + [ + nxt( + "高", + "列表核心列", + f"展示进院供应商名称、单号、状态、制单时间、制单人、变更类型及{doc}相关关键列、详情", + ), + nxt("高", "打开详情", "行内「详情」展示单据头与明细,单号与列表一致"), + ] + ) + else: + filter_points.extend( + [ + nxt("高", "单号与注册证号筛选", "按单号、注册证号(新)、变更类型、状态查询,结果匹配"), + nxt("中", "变更情况说明筛选", "有该字段时按说明关键字查询或精确匹配"), + nxt("中", "组合查询与重置", "多条件同时生效;重置恢复默认;收起后再展开值保留"), + nxt( + "高", + "列表核心列", + "展示进院供应商名称、单号、状态、制单时间、制单人、变更类型、注册证号(新)、详情", + ), + nxt("高", "打开详情", "行内「详情」展示变更前后注册证信息,单号与列表一致"), + ] + ) + chunks.append( + section( + f"{doc}列表与详情", + [ + ("功能验证点", filter_points), + ( + "边界验证点", + [ + nxt("中", "制单日期起止同一天", "仅返回该日单据或空态明确"), + nxt("低", "无匹配单据", "总数为 0,空态明确"), + ], + ), + ( + "异常验证点", + [ + nxt("中", "日期开始晚于结束", "拒绝查询或自动纠正并提示"), + nxt("中", "查询失败", "失败有提示,不白屏"), + ], + ), + ( + "集成验证点", + [ + nxt( + "高", + f"与{peer}单据衔接", + f"同一业务单在初审/终审列表状态流转一致,不串到非{doc}菜单", + ), + ], + ), + ], + ) + ) + elif kind == "diff": + chunks.append( + section( + "差异筛选与对比结果", + [ + ( + "功能验证点", + [ + nxt("高", "默认查询", "进入后查询有数据或空态明确,分页总数正确"), + nxt("高", "物料/规格/厂家筛选", "按物料名称、规格型号、生产厂家查询,结果匹配"), + nxt("高", "望海与 FLI+ 注册证号筛选", "按两侧注册证号查询,结果匹配"), + nxt("中", "是否医疗器械与状态", "状态下拉与是否医疗器械条件生效"), + nxt("高", "一致/不一致 Tab", "全部、一致、不一致切换后对比结论匹配,计数合理"), + nxt("中", "立即更新", "点击立即更新后时间戳刷新或提示成功,列表可刷新"), + nxt("中", "导出", "导出与当前筛选+Tab 条件一致"), + nxt("高", "对比列完整", "展示物料编号/名称、规格、厂家、医保编码及望海/FLI+注册证号与效期字段"), + ], + ), + ( + "边界验证点", + [ + nxt("中", "不一致 Tab 无数据", "空态明确,总数为 0"), + nxt("低", "包含长期有效勾选项", "勾选「包含FLI+长期有效数据」后结果集变化符合规则"), + ], + ), + ( + "异常验证点", + [ + nxt("中", "查询或更新失败", "失败有提示,不白屏"), + nxt("中", "iframe/内嵌页加载失败", "内嵌对比页失败有提示,外壳不假成功"), + ], + ), + ( + "集成验证点", + [ + nxt("高", "一致行两侧关键字段抽样一致", "标记为一致的行望海与 FLI+ 注册证号/效期抽样相同"), + nxt("高", "不一致行至少一侧差异", "标记为不一致的行两侧字段至少一处不同"), + ], + ), + ], + ) + ) + elif kind == "scheme": + chunks.append( + section( + "方案筛选与维护", + [ + ( + "功能验证点", + [ + nxt("高", "按创建/启用日期筛选", "按日期区间查询结果落在区间内"), + nxt("高", "方案编码/名称筛选", "按编码、名称查询结果匹配"), + nxt("中", "重置", "重置后条件恢复默认"), + nxt("高", "打开新增", "点击「新增」进入方案编辑,含方案编码/名称等必填区"), + nxt("高", "列表核心列", "展示方案编码、名称、创建人/时间、启用人/时间、停用人/时间、状态、备注、操作"), + nxt("中", "空列表态", "无数据时「暂无数据」空态明确"), + ], + ), + ( + "边界验证点", + [ + nxt("中", "起止同一天", "仅返回该日创建/启用的方案"), + ], + ), + ( + "异常验证点", + [ + nxt("高", "必填为空提交拦截", "方案编码或名称为空时提交失败并提示"), + nxt("中", "查询失败", "失败有提示"), + ], + ), + ( + "集成验证点", + [ + nxt("中", "方案与评价指标可关联", "方案引用的指标在评价指标维护中存在或可配置"), + ], + ), + ], + ) + ) + elif kind == "indicator": + chunks.append( + section( + "指标分类树与指标列表", + [ + ( + "功能验证点", + [ + nxt("高", "左树加载", "指标分类树可见,可搜索分类名称"), + nxt("高", "选中分类刷新右表", "点击分类后右侧列表为该分类指标"), + nxt("高", "指标编码/名称筛选", "右侧按编码、名称查询结果匹配"), + nxt("高", "列表核心列", "展示评价类型、指标编码、指标名称、状态、操作(编辑/详情/日志)"), + nxt("高", "新增分类", "左侧「新增」可创建分类,树中出现新节点"), + nxt("高", "新增指标", "右侧「新增」可创建指标并回填到列表"), + nxt("中", "编辑/详情/日志", "行内编辑打开回填;详情只读;日志可打开"), + ], + ), + ( + "边界验证点", + [ + nxt("中", "分类下无指标", "右表空态明确,总数为 0"), + nxt("低", "分类名超长", "超长被拦截或截断并提示"), + ], + ), + ( + "异常验证点", + [ + nxt("高", "必填为空拦截", "指标编码/名称为空提交失败并提示"), + nxt("中", "删除或停用受引用指标", "若被方案引用则拒绝或提示(以产品规则为准)"), + ], + ), + ( + "集成验证点", + [ + nxt("中", "状态启用后可供方案使用", "状态为启用的指标可在评价方案中被选择"), + ], + ), + ], + ) + ) + elif kind == "holiday": + chunks.append( + section( + "默认假日与供应商自定义", + [ + ( + "功能验证点", + [ + nxt("高", "左列表加载", "供应商节假日设置列表展示年度、名称,可按名称/年度搜索"), + nxt("高", "选中行展示默认详情", "点击左侧行后右侧默认假日详情名称/年度/起止/创建人时间与选中行一致"), + nxt("高", "左侧新增/编辑", "新增或编辑默认假日设置后列表与详情刷新"), + nxt("高", "供应商自定义新增", "右侧「新增」可添加进院供应商自定义节假日"), + nxt("中", "批量删除", "勾选自定义行后批量删除,列表条数减少"), + nxt("中", "自定义列表列", "展示进院供应商编码/名称、起止、备注、创建人时间、操作"), + ], + ), + ( + "边界验证点", + [ + nxt("中", "起止同一天", "默认或自定义假日起止同一天可保存或按规则提示"), + nxt("低", "自定义列表空态", "无自定义数据时「暂无数据」明确"), + ], + ), + ( + "异常验证点", + [ + nxt("高", "结束早于开始", "日期非法时拒绝保存并提示"), + nxt("中", "未勾选批量删除", "无勾选时批量删除不可用或提示"), + ], + ), + ( + "集成验证点", + [ + nxt("中", "进院供应商主数据一致", "自定义列表中的进院供应商可在厂商/供应商主数据中找到"), + ], + ), + ], + ) + ) + else: + chunks.append("### 业务功能\n\n#### 功能验证点\n\n- 无\n") + + return "\n".join(chunks), n + + +def render_testpoint(p: dict, summary: dict) -> str: + row = summary[p["pageKey"]] + url = row["url"] + count = row["elementCount"] + spec = f"UI-SPEC-SPD-{p['specId']}" + nav, last = nav_section(p, url, spec) + body, last = body_for(p, spec, last) + return f"""# 基础管理-厂商管理-{p['name']} — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/{spec}.md` +**页面 URL**:`{url}` +**版本 / 定稿日期**:{TODAY} +**元素扫描**:`data/scan/{p['pageKey']}-elements.json`({count} 项) +**定位器**:`locators/spd/{p['pageKey']}.locator.yaml` + +--- + +## 基础设置 · 厂商管理 · {p['name']} + +{nav} +--- + +{body} +--- + +## 质量自检摘要 + +- 覆盖:页面访问、筛选/Tab、列表分页、关键写操作或详情、权限与登录异常、同模块衔接。 +- 颗粒度:一条测试点只验证一件独立事;优先级已标高/中/低。 +- 待评审项:审批通过/驳回写操作是否自动化(建议只读详情冒烟);注册证差异对比 iframe 断言策略。 +- 下游:可转结构化 MD/CSV;自动化需先补 Page Object(`pages/spd/{p['pageKey']}.page.ts`)。 +""" + + +def patch_ui_spec(p: dict, summary: dict) -> None: + row = summary[p["pageKey"]] + path = KB / f"UI-SPEC-SPD-{p['specId']}.md" + text = path.read_text(encoding="utf-8") + text = re.sub(r'update_time: "[^"]*"', f'update_time: "{TODAY}"', text, count=1) + text = re.sub( + r'related_tc: "[^"]*"', + f'related_tc: "testpoint/{p["file"]}"', + text, + count=1, + ) + url = row["url"] + count = row["elementCount"] + page_key = p["pageKey"] + table = f"""| 项 | 值 | +|----|-----| +| 主框架 URL | `{url}` | +| iframe 选择器 | {"内嵌 scjs iframe(对比页)" if p["kind"] == "diff" else "无(整页渲染,非 iframe 报表)"} | +| 元素扫描 JSON | `data/scan/{page_key}-elements.json`({count} 项) | +| 定位器 YAML | `locators/spd/{page_key}.locator.yaml` | +| 测试环境示例 | `http://spdtest.cmic.com.cn:8080/spd/form/` |""" + text = re.sub( + r"\| 项 \| 值 \|\n\|----\|-----\|\n(?:\| .+\|\n)+", + table + "\n", + text, + count=1, + ) + # 自动化建议首条改为已扫描 + text = text.replace( + "1. 完成专项元素扫描后,补充 `data/scan/`、`locators/spd/` 与 Page Object。", + f"1. 元素扫描已完成({count} 项):`data/scan/{page_key}-elements.json`;下一步补充 Page Object 与用例。", + ) + path.write_text(text, encoding="utf-8") + + +def main() -> None: + summary = load_summary() + OUT_DIR.mkdir(parents=True, exist_ok=True) + for p in PAGES: + if p["pageKey"] not in summary: + raise SystemExit(f"missing scan summary for {p['pageKey']}") + md = render_testpoint(p, summary) + out = OUT_DIR / p["file"] + out.write_text(md, encoding="utf-8") + patch_ui_spec(p, summary) + print(f"OK {p['specId']}\t{p['name']}\t{summary[p['pageKey']]['elementCount']} el\t{out.name}") + print("done", len(PAGES)) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate-wh-flow-md.py b/scripts/generate-wh-flow-md.py new file mode 100644 index 0000000..94f7d9b --- /dev/null +++ b/scripts/generate-wh-flow-md.py @@ -0,0 +1,930 @@ +#!/usr/bin/env python3 +"""生成库房管理 / 流程设置结构化 MD(testcases/)与同源 CSV(testcase/)。 + +正例登录账号可写入 CSV;密码只放 .env TEST_PASS / auth.setup,禁止入库。 +反例筛选用 mock(__NOT_EXIST_ZONE__/PROBE/FLOW)。 +""" +from __future__ import annotations + +import csv +from collections import Counter +from datetime import date +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +MD_DIR = ROOT / "testcases" +CSV_DIR = ROOT / "testcase" +TODAY = date.today().isoformat() +POSITIVE_USER = "test_1" +CSV_FIELDS = [ + "用例编号", + "功能模块", + "功能测试点", + "用例标题", + "优先级", + "前置条件", + "测试步骤", + "预期结果", + "实际结果", +] + + +def page(**kwargs) -> dict: + return kwargs + + +def case( + seq: int, + h3: str, + h4: str, + prio: str, + title: str, + tp: int, + steps: list[str], + expect: str, + pre: str = "", +) -> dict: + return { + "seq": seq, + "h3": h3, + "h4": h4, + "prio": prio, + "title": title, + "tp": tp, + "steps": steps, + "expect": expect, + "pre": pre, + } + + +PAGES = [ + page( + code="WH-ZON", + tp_prefix="TP_WH_ZON", + group="库房管理", + name="库区设置", + path="/spd/basic/storehouse/platform/storehouse/area", + scan="warehouse-zone", + count=106, + spec="UI-SPEC-SPD-WH-001", + spec_file="warehouse-zone.spec.ts", + tp_total=74, + nav_note=( + "菜单「基础设置 → 库房管理 → 库区设置」;建议 " + "`openPath('/spd/basic/storehouse/platform/storehouse/area')` 并断言 URL,勿误入探针绑定。" + ), + write_note=( + "新增/编辑只打开后取消或点确定做必填拦截,不提交成功保存;" + "不停用/启用落库;人员管理/货位管理只打开后关闭;同步仅点一次并断言树仍在。" + ), + samples=( + "从左侧树读取 `${sampleWhName}`,从右侧首行读取 `${sampleZoneNo}`、`${sampleZoneName}`;" + "mock:`__NOT_EXIST_ZONE__99999`。" + ), + skip="真实保存新增/编辑、停用启用持久化、接口失败注入、会话过期、无菜单权限、人员/货位子页字段细节", + cases=[ + case( + 1, "页面进入", "功能验证点", "P0", "菜单进入库区设置页", 1, + [ + "按菜单「基础设置 → 库房管理 → 库区设置」进入,或 `openPath` 打开 `/spd/basic/storehouse/platform/storehouse/area`", + "断言 URL 含 `/spd/basic/storehouse/platform/storehouse/area`", + "断言左侧「库房信息」树、右侧库区列表、「新 增」与分页可见", + ], + "进入库区设置页;不误入探针绑定;无报错遮罩。", + ), + case( + 2, "页面进入", "异常验证点", "P0", "未登录访问拦截", 5, + [ + "清除登录态后直接打开 `/spd/basic/storehouse/platform/storehouse/area`", + "断言跳转登录或不展示库房/库区业务数据", + ], + "未登录不可见库区数据。", + ), + case( + 3, "左侧库房树", "功能验证点", "P0", "库房树默认有选中项", 9, + [ + "断言左侧「库房信息」至少 1 个节点且有选中项", + "读取选中节点 `${sampleWhName}`", + "断言右侧列表为该库房库区,或空态且「共 0 条」", + ], + "树有选中;右侧对应当前库房。", + ), + case( + 4, "左侧库房树", "功能验证点", "P0", "按库房名称过滤树", 10, + [ + "读取当前选中 `${sampleWhName}` 取关键字(可截取前若干字)", + "在左侧「库房名称」输入关键字", + "断言可见节点名称均包含该关键字", + ], + "树过滤结果与关键字匹配。", + pre="库房树至少 1 个节点", + ), + case( + 5, "左侧库房树", "功能验证点", "P1", "清空库房名称恢复全量树", 11, + [ + "记录过滤前节点数", + "输入关键字过滤后再清空", + "断言节点数恢复为过滤前", + ], + "清空后树恢复全量。", + ), + case( + 6, "左侧库房树", "功能验证点", "P0", "切换库房刷新右侧列表", 12, + [ + "记录当前库房 `${sampleWhName}` 与右侧首行库区编号(若有)", + "点选另一库房节点(仅 1 个则 skip)", + "断言「共 N 条」可变化;若有行则库区编号/名称属于新库房", + ], + "切换库房后右侧列表刷新,不残留上一库房库区。", + ), + case( + 7, "左侧库房树", "异常验证点", "P1", "无匹配库房空树(mock反例)", 15, + [ + "库房名称填入 `__NOT_EXIST_ZONE__99999`", + "断言树为空或明确无数据,右侧不展示上一库房库区为成功态", + ], + "无匹配不报错;不残留上一库房列表当成功。", + ), + case( + 8, "右侧库区列表与分页", "功能验证点", "P0", "列表核心列完整", 18, + [ + "断言表头含库区编号、库区名称、库区地址、是否专项库区、操作", + "若有数据行,断言操作列可见「编辑」「人员管理」「货位管理」及「停用」或「启用」", + ], + "核心列完整;行内操入口可见。", + ), + case( + 9, "右侧库区列表与分页", "功能验证点", "P0", "默认库区以标签展示", 19, + [ + "在当前库房列表中查找「默认库区」标签", + "断言该标签出现在库区名称旁,不是独立列", + "断言当前库房至多 1 个默认库区标签(无默认则本条只断言不是独立列)", + ], + "默认库区为名称旁标签;同一库房至多一个。", + ), + case( + 10, "库区搜索", "功能验证点", "P0", "按库区编号搜索", 29, + [ + "从首行读取 `${sampleZoneNo}`", + "在「库区编号/名称」填入该编号并触发搜索", + "断言结果包含该编号行,且均属于当前选中库房", + ], + "搜索命中该编号;不串其它库房。", + pre="当前库房至少 1 条库区且编号非空", + ), + case( + 11, "库区搜索", "功能验证点", "P0", "按库区名称搜索", 30, + [ + "从首行读取 `${sampleZoneName}` 取关键字", + "在「库区编号/名称」填入关键字并搜索", + "断言结果行名称匹配", + ], + "名称关键字命中;无未捕获异常。", + pre="当前库房至少 1 条且名称非空", + ), + case( + 12, "库区搜索", "异常验证点", "P1", "无匹配库区空态(mock反例)", 34, + [ + "「库区编号/名称」填入 `__NOT_EXIST_ZONE__99999` 并搜索", + "断言列表空且分页为 0", + ], + "无匹配不报错;总数为 0。", + ), + case( + 13, "新增库区", "功能验证点", "P0", "打开新增库区弹窗", 36, + [ + "点击「新 增」", + "断言弹出「新增库区」,可见库区编号、库区名称、库区地址、联系人、联系方式及「取 消」「确 定」", + ], + "新增弹窗可见;关键字段与按钮齐全。", + ), + case( + 14, "新增库区", "功能验证点", "P1", "新增取消不落库", 38, + [ + "打开新增,编号填 `AUTO${timestamp}`,名称填任意合法值", + "点击「取 消」", + "按该编号搜索,断言无该记录", + ], + "弹窗关闭;列表不出现该编号。", + ), + case( + 15, "新增库区", "异常验证点", "P0", "必填为空拦截", 41, + [ + "打开新增,库区编号或名称留空后点击「确 定」", + "断言出现必填提示,弹窗仍打开", + "点「取 消」", + ], + "确定被拦截;不落库;弹窗不关闭。", + ), + case( + 16, "编辑库区", "功能验证点", "P0", "打开编辑并回填", 46, + [ + "点击首行「编辑」", + "断言弹窗标题含「编辑库区信息」", + "断言库区编号、名称与该行 `${sampleZoneNo}`、`${sampleZoneName}` 一致(允许规范化空白)", + "点「取 消」", + ], + "编辑弹窗可见;回填与列表行一致。", + pre="当前库房至少 1 条库区", + ), + case( + 17, "编辑库区", "功能验证点", "P1", "编辑取消不保存", 48, + [ + "打开首行编辑,记录原名称", + "将名称改为临时值后点「取 消」", + "再次打开编辑,断言名称仍为原值", + ], + "取消后列表与回看均为修改前名称。", + pre="当前库房至少 1 条库区", + ), + case( + 18, "编辑库区", "异常验证点", "P0", "编辑清空必填拦截", 52, + [ + "打开首行编辑,清空库区名称后点「确 定」", + "断言被拦截并提示,弹窗仍开", + "点「取 消」,断言列表名称未被写成空", + ], + "清空必填不能保存;原记录名称保持。", + pre="当前库房至少 1 条库区", + ), + case( + 19, "人员管理", "功能验证点", "P0", "打开人员管理后关闭", 61, + [ + "点击首行「人员管理」", + "断言打开弹层或子页,标题含人员管理或对应当前库区", + "关闭后断言仍在库区设置,当前库房选中保持", + ], + "人员管理可打开;关闭后回到当前库房列表。本条不改人员。", + pre="当前库房至少 1 条库区", + ), + case( + 20, "货位管理", "功能验证点", "P0", "打开货位管理后关闭", 66, + [ + "点击首行「货位管理」", + "断言打开弹层或子页,标题含货位且对应当前库区", + "关闭后断言库房选中与库区列表保持", + ], + "货位管理可打开;关闭后列表状态保持。本条不改货位。", + pre="当前库房至少 1 条库区", + ), + ], + ), + page( + code="WH-PRB", + tp_prefix="TP_WH_PRB", + group="库房管理", + name="探针绑定", + path="/spd/basic/storehouse/storehouse/probe", + scan="warehouse-probe", + count=78, + spec="UI-SPEC-SPD-WH-002", + spec_file="warehouse-probe.spec.ts", + tp_total=49, + nav_note=( + "菜单「基础设置 → 库房管理 → 探针绑定」;建议 " + "`openPath('/spd/basic/storehouse/storehouse/probe')` 并断言 URL。" + ), + write_note="绑定货位只打开弹窗后取消,不点确定保存;不改绑定关系。", + samples=( + "查询后从首行读取 `${sampleProbeId}`、`${sampleHostCode}`、`${sampleBindFlag}`、`${sampleBindCount}`;" + "mock:`__NOT_EXIST_PROBE__99999`。" + ), + skip="真实勾选确定绑定、换绑/占用规则落库、接口失败、会话过期、无菜单权限", + cases=[ + case( + 1, "页面进入", "功能验证点", "P0", "菜单进入探针绑定页", 1, + [ + "按菜单「基础设置 → 库房管理 → 探针绑定」进入,或 `openPath` 打开 `/spd/basic/storehouse/storehouse/probe`", + "断言 URL 含 `/spd/basic/storehouse/storehouse/probe`", + "断言探针ID、主机编码、是否绑定货位、查询、重置、列表与分页可见", + ], + "进入探针绑定页;不误入库区设置。", + ), + case( + 2, "页面进入", "异常验证点", "P0", "未登录访问拦截", 5, + [ + "清除登录态后直接打开 `/spd/basic/storehouse/storehouse/probe`", + "断言跳转登录或不展示探针业务列表", + ], + "未登录不可见探针数据。", + ), + case( + 3, "筛选与查询", "功能验证点", "P0", "默认查询列表有数据或空态明确", 9, + [ + "点击「查 询」并等待列表稳定", + "断言出现数据行,或「暂无数据」且分页总数为 0", + ], + "查询无报错;有数据时「共 N 条」N≥1。", + ), + case( + 4, "筛选与查询", "功能验证点", "P0", "按探针ID查询", 10, + [ + "从首行读取 `${sampleProbeId}`", + "探针ID填入该值,点击「查 询」", + "断言结果探针ID匹配,精确命中时行数合理", + ], + "探针ID条件生效。", + pre="默认查询至少 1 条且探针ID非空", + ), + case( + 5, "筛选与查询", "功能验证点", "P0", "按主机编码查询", 11, + [ + "从首行读取 `${sampleHostCode}`", + "主机编码填入该值,点击「查 询」", + "断言结果行「主机编码」与条件一致", + ], + "主机编码条件生效;允许多探针共用同一主机。", + pre="默认查询至少 1 条且主机编码非空", + ), + case( + 6, "筛选与查询", "功能验证点", "P0", "按是否绑定货位查询", 12, + [ + "是否绑定货位选择「已绑定」,点击「查 询」", + "断言结果「是否绑定货位」均为已绑定,且「绑定的货位」>0", + "再选「未绑定」查询,断言均为未绑定且数量为 0(无数据则空态)", + ], + "已绑定数量大于 0;未绑定数量为 0。", + ), + case( + 7, "筛选与查询", "功能验证点", "P0", "探针ID+主机编码组合查询", 13, + [ + "填写 `${sampleProbeId}` 与 `${sampleHostCode}` 后点击「查 询」", + "断言结果同时满足已填条件", + ], + "组合条件同时生效。", + pre="默认查询至少 1 条", + ), + case( + 8, "筛选与查询", "功能验证点", "P1", "重置恢复默认", 14, + [ + "填写探针ID后点击「重 置」", + "断言条件恢复默认,列表按默认规则刷新", + ], + "条件恢复默认;列表刷新。", + ), + case( + 9, "筛选与查询", "异常验证点", "P1", "无匹配空态(mock反例)", 18, + [ + "探针ID填入 `__NOT_EXIST_PROBE__99999`,点击「查 询」", + "断言空态且分页总数为 0", + ], + "无匹配不报错;总数为 0。", + ), + case( + 10, "列表与分页", "功能验证点", "P0", "列表核心列与数量规则", 20, + [ + "断言表头含序号、探针ID、主机编码、是否绑定货位、绑定的货位、操作", + "抽样已绑定行:绑定数量为数字且 >0;抽样未绑定行:数量为 0", + "断言「绑定的货位」列不是货位号格式", + ], + "核心列完整;数量列是数字不是货位号。", + ), + case( + 11, "打开绑定货位弹窗", "功能验证点", "P0", "行内打开货位绑定探针", 30, + [ + "点击首行「绑定货位」", + "断言弹出「货位绑定探针」,可见当前库房/库区/货位查询及货位列表", + ], + "弹窗标题正确;筛选与列表可见。", + pre="列表至少 1 条", + ), + case( + 12, "打开绑定货位弹窗", "功能验证点", "P1", "取消关闭不改主列表", 31, + [ + "记录主列表首行绑定数量与当前筛选", + "打开绑定弹窗后点「取 消」", + "断言主列表绑定数量与筛选条件保持", + ], + "取消不改绑定关系。", + pre="列表至少 1 条", + ), + case( + 13, "打开绑定货位弹窗", "边界验证点", "P1", "未选货位时确定计数为 0", 32, + [ + "打开绑定弹窗,不勾选货位", + "断言底部按钮文案为「确定(0)」或等价", + "点「取 消」,不点确定保存", + ], + "未选时计数为 0;本条不保存绑定。", + pre="列表至少 1 条", + ), + case( + 14, "打开绑定货位弹窗", "集成验证点", "P1", "弹窗分页独立于主列表", 34, + [ + "记录主列表「共 N 条」", + "打开绑定弹窗,记录弹窗分页「共 M 条」", + "断言不得用 M 断言主列表 N(货位量级可远大于探针条数)", + "点「取 消」", + ], + "弹窗总条数与主列表探针条数相互独立。", + pre="列表至少 1 条", + ), + case( + 15, "弹窗筛选货位", "功能验证点", "P0", "按当前库房筛选货位", 35, + [ + "打开绑定弹窗,记录默认 `${sampleModalWh}`", + "点「查 询」", + "断言可见行「当前库房」与所选库房一致(有数据时)", + "点「取 消」", + ], + "弹窗库房条件生效。", + pre="列表至少 1 条", + ), + case( + 16, "弹窗筛选货位", "功能验证点", "P1", "弹窗内重置", 38, + [ + "打开绑定弹窗,填写货位查询关键字后点「重 置」", + "断言货位查询条件恢复,列表按弹窗默认规则刷新", + "点「取 消」", + ], + "弹窗重置不影响主列表筛选。", + pre="列表至少 1 条", + ), + case( + 17, "勾选货位并保存绑定", "功能验证点", "P1", "勾选后确定计数增加再取消勾选", 43, + [ + "打开绑定弹窗,勾选 1 个货位", + "断言确定按钮计数为 1", + "取消该勾选,断言计数回到 0", + "点「取 消」,不保存", + ], + "计数随勾选变化;取消勾选不落库。", + pre="弹窗货位列表至少 1 条", + ), + ], + ), + page( + code="FLOW-DEF", + tp_prefix="TP_FLOW_DEF", + group="流程设置", + name="审批流程设置", + path="/spd/basic/flowSet/workflow/flowSet", + scan="flow-def", + count=43, + spec="UI-SPEC-SPD-FLOW-001", + spec_file="flow-def.spec.ts", + tp_total=41, + nav_note=( + "菜单「基础设置 → 流程设置 → 审批流程设置」;建议 " + "`openPath('/spd/basic/flowSet/workflow/flowSet')`,与「审批流实例」用 exact 区分。" + ), + write_note=( + "禁止点「保 存」提交线上审批路由。新增流程/改节点后必须「重 置」或取消条件配置;" + "必填拦截若需点保存,仅空行拦截成功后立刻重置。" + ), + samples=( + "从左侧读取 `${sampleBizType}`(如采购计划);自定义行读取 `${sampleFlowName}`、`${sampleCondText}`;" + "mock:`__NOT_EXIST_FLOW__99999`。" + ), + skip="真实保存审批链、自定义流持久化、条件保存落库、历史实例快照、接口失败、会话过期", + cases=[ + case( + 1, "页面进入", "功能验证点", "P0", "菜单进入审批流程设置页", 1, + [ + "按菜单「基础设置 → 流程设置 → 审批流程设置」进入,或 `openPath` 打开 `/spd/basic/flowSet/workflow/flowSet`", + "断言 URL 含 `/spd/basic/flowSet/workflow/flowSet`", + "断言左侧业务类型、右侧默认审批流与自定义审批流、「保 存」「重 置」可见", + ], + "进入审批流程设置;不误入审批流实例。", + ), + case( + 2, "页面进入", "异常验证点", "P0", "未登录访问拦截", 5, + [ + "清除登录态后直接打开 `/spd/basic/flowSet/workflow/flowSet`", + "断言跳转登录或不展示审批流配置", + ], + "未登录不可见审批流配置。", + ), + case( + 3, "左侧业务类型", "功能验证点", "P0", "业务类型列表可见", 9, + [ + "断言左侧可见采购计划等类型且有当前高亮项", + "读取 `${sampleBizType}`", + ], + "类型列表可见;当前项高亮。", + ), + case( + 4, "左侧业务类型", "功能验证点", "P0", "按流程名称过滤类型", 10, + [ + "在「流程名称」输入 `${sampleBizType}` 关键字", + "断言可见类型名称均匹配关键字", + ], + "类型过滤与关键字匹配。", + ), + case( + 5, "左侧业务类型", "功能验证点", "P1", "清空关键字恢复全量", 11, + [ + "记录过滤前类型数量", + "输入关键字后再清空", + "断言数量恢复", + ], + "清空后类型列表恢复全量。", + ), + case( + 6, "左侧业务类型", "功能验证点", "P0", "切换类型刷新右侧配置", 12, + [ + "记录当前类型默认流节点文案", + "点选另一业务类型(仅 1 个则 skip)", + "断言右侧默认流/自定义流已刷新,不残留上一类型节点人员", + ], + "切换类型后右侧与该类型已保存配置一致。", + ), + case( + 7, "左侧业务类型", "异常验证点", "P1", "无匹配类型(mock反例)", 15, + [ + "流程名称填入 `__NOT_EXIST_FLOW__99999`", + "断言左侧无匹配或空,右侧不把上一类型配置展示为当前成功配置", + ], + "无匹配不报错。", + ), + case( + 8, "默认审批流", "功能验证点", "P0", "默认审批流节点可见", 17, + [ + "选中「采购计划」(或当前默认类型)", + "断言「默认审批流」区域可见节点卡片,含节点/账号/人员三行信息(按卡片容器取文本)", + ], + "默认流节点结构可见;不点保存。", + ), + case( + 9, "自定义审批流", "功能验证点", "P0", "新增流程增加一行后重置", 22, + [ + "记录当前自定义流行数", + "点击「新增流程」", + "断言行数 +1,新行含流程名称、条件、审批流程、删除", + "点击「重 置」,断言行数回到操作前,不点「保 存」", + ], + "可新增行;重置放弃未保存行。", + ), + case( + 10, "自定义审批流", "异常验证点", "P0", "空行保存被拦截后重置", 28, + [ + "点击「新增流程」得到空行", + "点击「保 存」", + "断言出现必填拦截提示,配置未按成功态关闭", + "点击「重 置」,放弃该空行", + ], + "空名称/条件/节点不能落库;拦截后重置恢复。", + ), + case( + 11, "条件配置", "功能验证点", "P0", "打开条件配置后取消", 31, + [ + "若无自定义行则 skip", + "点击首条自定义流条件列「编辑」", + "断言打开「条件配置」", + "点「取 消」,断言行内条件文案与打开前一致", + ], + "条件配置可打开;取消不改行内条件。", + pre="当前类型至少 1 条自定义流", + ), + case( + 12, "保存与重置", "功能验证点", "P0", "重置放弃未保存改动", 38, + [ + "点击「新增流程」增加一行", + "点击「重 置」", + "断言自定义流行回到上次保存结果", + ], + "重置不提交;未保存行消失。", + ), + case( + 13, "左侧业务类型", "集成验证点", "P1", "类型与审批流实例业务类型同源", 16, + [ + "记录本页左侧类型名称集合(可见范围)", + "打开审批流实例,展开「业务类型」下拉", + "断言选项与本页类型同源(允许按权限裁剪,本页有的关键类型如采购计划应能对应)", + ], + "两页业务类型同源。", + ), + ], + ), + page( + code="FLOW-INS", + tp_prefix="TP_FLOW_INS", + group="流程设置", + name="审批流实例", + path="/spd/basic/flowSet/workflow/flowCase", + scan="flow-inst", + count=62, + spec="UI-SPEC-SPD-FLOW-002", + spec_file="flow-inst.spec.ts", + tp_total=41, + nav_note=( + "菜单「基础设置 → 流程设置 → 审批流实例」;建议 " + "`openPath('/spd/basic/flowSet/workflow/flowCase')`,不要点到审批流程设置。" + ), + write_note="本页只读。详情只打开后返回,不改审批人。断言「共 N 条」前先收窄日期。", + samples=( + "查询后从首行读取 `${sampleOrderNo}`、`${sampleBizType}`、`${sampleStatus}`、`${sampleSummary}`;" + "mock:`__NOT_EXIST_FLOW__99999`。" + ), + skip="改审批人、接口失败、会话过期、无菜单权限、详情与流程设置节点逐条对照(可另开集成专项)", + cases=[ + case( + 1, "页面进入", "功能验证点", "P0", "菜单进入审批流实例页", 1, + [ + "按菜单「基础设置 → 流程设置 → 审批流实例」进入,或 `openPath` 打开 `/spd/basic/flowSet/workflow/flowCase`", + "断言 URL 含 `/spd/basic/flowSet/workflow/flowCase`", + "断言单据编号、审批状态、发起时间、业务类型、摘要、查询、重置、列表与分页可见", + ], + "进入审批流实例;不误入审批流程设置。", + ), + case( + 2, "页面进入", "异常验证点", "P0", "未登录访问拦截", 5, + [ + "清除登录态后直接打开 `/spd/basic/flowSet/workflow/flowCase`", + "断言跳转登录或不展示审批单据列表", + ], + "未登录不可见审批实例。", + ), + case( + 3, "筛选与查询", "功能验证点", "P0", "默认日期区间查询", 9, + [ + "点击「查 询」并等待列表稳定", + "断言出现数据行或空态明确,「共 N 条」与结果逻辑一致", + ], + "默认查询无报错;有数据时 N≥1。", + ), + case( + 4, "筛选与查询", "功能验证点", "P0", "单据编号精确查询", 10, + [ + "从首行读取 `${sampleOrderNo}`", + "单据编号填入完整单号,点击「查 询」", + "断言至多 1 条,且编号、业务类型与该行一致", + ], + "精确单号命中当前行。", + pre="默认查询至少 1 条且单据编号非空", + ), + case( + 5, "筛选与查询", "功能验证点", "P0", "按审批状态筛选", 11, + [ + "审批状态下拉选择「审批通过」(无该选项则选列表中已出现的状态)", + "点击「查 询」", + "断言结果「审批状态」列与所选一致", + ], + "状态筛选生效。", + ), + case( + 6, "筛选与查询", "功能验证点", "P0", "按业务类型筛选", 12, + [ + "从首行读取 `${sampleBizType}`", + "业务类型选择该值,点击「查 询」", + "断言结果「业务类型」均为该类型", + ], + "业务类型筛选生效。", + pre="默认查询至少 1 条", + ), + case( + 7, "筛选与查询", "功能验证点", "P0", "按发起时间区间筛选", 13, + [ + "记录当前默认起止日期", + "点击「查 询」", + "抽样结果行「发起时间」落在起止日内(含边界)", + ], + "发起时间落在区间内。", + ), + case( + 8, "筛选与查询", "功能验证点", "P1", "按摘要关键字筛选", 14, + [ + "找到摘要非 `--` 的行读取 `${sampleSummary}` 关键字;若全为 `--` 则填 mock 关键字", + "点击「查 询」", + "有关键字命中时摘要包含该关键字;mock 无命中时空态且 `--` 行不被命中", + ], + "非空摘要可检索;`--` 不被非空关键字命中。", + ), + case( + 9, "筛选与查询", "功能验证点", "P0", "状态+类型组合查询", 15, + [ + "填写 `${sampleStatus}` 与 `${sampleBizType}` 后点击「查 询」", + "断言抽样行同时满足全部已填条件", + ], + "组合条件同时生效。", + pre="默认查询至少 1 条", + ), + case( + 10, "筛选与查询", "功能验证点", "P1", "重置恢复默认", 16, + [ + "填写单据编号等条件后点击「重 置」", + "断言日期回到默认区间,其余条件清空,列表刷新", + ], + "重置后日期与其它条件恢复默认。", + ), + case( + 11, "筛选与查询", "异常验证点", "P1", "无匹配空态(mock反例)", 21, + [ + "单据编号填入 `__NOT_EXIST_FLOW__99999`,点击「查 询」", + "断言空态且分页总数为 0", + ], + "无匹配不报错;总数为 0。", + ), + case( + 12, "列表与分页", "功能验证点", "P0", "列表核心列完整", 24, + [ + "断言表头含序号、单据编号、业务类型、发起人、发起部门、发起时间、摘要、审批状态、操作", + "断言行内可见「详情」", + ], + "核心列完整;详情入口可见。", + ), + case( + 13, "列表与分页", "功能验证点", "P1", "单据编号前缀与业务类型对应", 25, + [ + "抽样 `PLAN`/`DB`/`GC`/`BUY` 前缀行(有则断言)", + "断言 PLAN→采购计划、DB→二级库调拨、GC→产品信息变更、BUY→请领单(以现网枚举为准,缺前缀 skip 该项)", + ], + "前缀与类型对应。", + pre="默认查询有数据", + ), + case( + 14, "列表与分页", "功能验证点", "P1", "分页翻至第 2 页", 28, + [ + "记录第 1 页首行单据编号", + "翻到第 2 页", + "断言数据更换;筛选条件保持", + ], + "翻页成功;筛选不丢失。", + pre="总条数大于一页,否则 skip", + ), + case( + 15, "审批详情", "功能验证点", "P0", "打开详情对应当前行", 36, + [ + "记录首行 `${sampleOrderNo}`", + "点击「详情」", + "断言打开该单审批过程,标题或单号含 `${sampleOrderNo}`", + ], + "详情单号与当前行一致。", + pre="列表至少 1 条", + ), + case( + 16, "审批详情", "功能验证点", "P1", "关闭详情保留列表状态", 38, + [ + "填写单据编号查询后打开详情", + "点返回/关闭", + "断言筛选条件与列表仍是打开前的查询结果", + ], + "返回后条件与页码保持。", + pre="列表至少 1 条", + ), + case( + 17, "审批详情", "功能验证点", "P1", "详情只读", 37, + [ + "打开首行详情", + "断言无保存审批人/改条件的提交按钮,或此类控件不可用", + "返回列表", + ], + "详情只读;改配置须到审批流程设置。", + pre="列表至少 1 条", + ), + ], + ), +] + + +def tp_id(p: dict, c: dict) -> str: + return f"{p['tp_prefix']}_{c['tp']:03d}" + + +def prio_csv(prio: str) -> str: + return {"P0": "高", "P1": "中", "P2": "低"}[prio] + + +def is_unlogin(c: dict) -> bool: + return "未登录" in c["title"] + + +def render_csv_rows(p: dict) -> list[dict]: + logged = ( + f"测试环境可访问;正例账号 {POSITIVE_USER} 已通过 auth.setup 写入 storageState 登录" + f"(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → {p['group']} → {p['name']}」权限" + ) + unlogin = ( + f"测试环境可访问;使用独立 context,storageState 为空(不加载 {POSITIVE_USER} 登录态)" + ) + rows: list[dict] = [] + for c in p["cases"]: + case_id = f"{p['code']}-{c['seq']:03d}" + if is_unlogin(c): + pre_parts = [unlogin] + if c["pre"]: + pre_parts.append(c["pre"]) + steps = "\n".join( + ["1. 新建独立 BrowserContext,不加载 auth/user.json"] + + [f"{i + 1}. {s}" for i, s in enumerate(c["steps"], start=1)] + ) + else: + pre_parts = [logged] + if c["seq"] > 1: + pre_parts.append(f"已进入{p['name']}页({p['path']},无 iframe)") + if c["pre"]: + pre_parts.append(c["pre"]) + steps = "\n".join( + [ + f"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 {POSITIVE_USER}),无需在用例内重复登录" + ] + + [f"{i + 1}. {s}" for i, s in enumerate(c["steps"], start=1)] + ) + rows.append( + { + "用例编号": case_id, + "功能模块": f"基础设置 · {p['group']}", + "功能测试点": f"{p['name']} · {c['h3']}", + "用例标题": c["title"], + "优先级": prio_csv(c["prio"]), + "前置条件": ";".join(pre_parts), + "测试步骤": steps, + "预期结果": c["expect"], + "实际结果": "", + } + ) + return rows + + +def render_md(p: dict) -> str: + n = len(p["cases"]) + cnt = Counter(c["prio"] for c in p["cases"]) + prio_txt = " / ".join(f"{k}={cnt[k]}" for k in ("P0", "P1", "P2") if cnt[k]) + lines = [ + f"# 基础管理-{p['group']}-{p['name']} UI 自动化测试用例", + "", + f"**来源测试点**:`testpoint/基础管理-{p['group']}-{p['name']}-测试点分析-01.md` ", + f"**执行 CSV**:`testcase/基础管理-{p['group']}-{p['name']}-UI自动化测试用例-01.csv` ", + f"**页面 URL**:`{p['path']}` ", + f"**定位器 YAML**:`locators/spd/{p['scan']}.locator.yaml` ", + f"**元素扫描**:`data/scan/{p['scan']}-elements.json`({p['count']} 项) ", + f"**关联规范**:`knowledge-base/08-UI专项规范库/{p['spec']}.md` ", + f"**版本**:{TODAY} ", + f"**自动化框架**:Playwright + TypeScript(规划 `tests/spd/{p['spec_file']}`,依赖 `auth.setup` 登录态)", + "", + f"**通用前置**:正例账号 `{POSITIVE_USER}` 经 `auth.setup` 写入 `storageState`(密码仅存 `.env` 的 `TEST_PASS`);具备「基础设置 → {p['group']} → {p['name']}」权限。反例筛选用 mock。 ", + "**页面约定**:整页渲染,**无报表 iframe**;定位均在 `page` 上。 ", + f"**菜单导航**:{p['nav_note']} ", + f"**写操作**:{p['write_note']} ", + f"**动态取数**:{p['samples']}", + "", + "---", + "", + f"## 基础设置 · {p['group']}", + "", + ] + current_h3 = "" + current_h4 = "" + for c in p["cases"]: + h3 = f"{p['name']} · {c['h3']}" + if h3 != current_h3: + current_h3 = h3 + current_h4 = "" + lines += [f"### {h3}", ""] + if c["h4"] != current_h4: + current_h4 = c["h4"] + lines += [f"#### {current_h4}", ""] + case_id = f"{p['code']}-{c['seq']:03d}" + tc = f"TC-UI-{case_id}" + lines += [ + f"##### 【{c['prio']}】【{tc}】【{tp_id(p, c)}】{c['title']}", + "", + f"- 前置条件:{c['pre'] or ('已进入' + p['name'] + '页')}", + "- 操作步骤:", + ] + for i, step in enumerate(c["steps"], 1): + lines.append(f" {i}. {step}") + lines.append(f"- 自动化断言:规划与 CSV `{case_id}` 及 `tests/spd/{p['spec_file']}` 一致") + lines += ["", "###### 预期结果", "", c["expect"], "", "---", ""] + while lines and lines[-1] in ("", "---"): + lines.pop() + lines += [ + "", + "", + "## 质量与覆盖说明", + "", + "| 项 | 说明 |", + "|----|------|", + f"| 用例数 | **{n}** 条({prio_txt}) |", + f"| 来源 | 从 {p['tp_total']} 条测试点选取可稳定自动化的主路径与关键反向 |", + f"| 未纳入自动化 | {p['skip']} |", + "| 单一职责 | 每条只断言一个核心业务结果 |", + "| 独立性 | 每条独立 `open()`,不复用 BrowserContext;不依赖上一条写结果 |", + "", + ] + return "\n".join(lines) + + +def main() -> None: + MD_DIR.mkdir(parents=True, exist_ok=True) + CSV_DIR.mkdir(parents=True, exist_ok=True) + total = 0 + for p in PAGES: + seqs = [c["seq"] for c in p["cases"]] + if seqs != list(range(1, len(seqs) + 1)): + raise SystemExit(f"{p['code']} seq not consecutive: {seqs}") + md_path = MD_DIR / f"基础管理-{p['group']}-{p['name']}-UI自动化测试用例-01.md" + csv_path = CSV_DIR / f"基础管理-{p['group']}-{p['name']}-UI自动化测试用例-01.csv" + md_path.write_text(render_md(p), encoding="utf-8") + rows = render_csv_rows(p) + with csv_path.open("w", encoding="utf-8", newline="") as f: + w = csv.DictWriter(f, fieldnames=CSV_FIELDS) + w.writeheader() + w.writerows(rows) + total += len(rows) + print(f"OK {p['code']}\t{len(rows):2d} cases\t{md_path.name}\t{csv_path.name}") + print(f"done pages={len(PAGES)} cases={total}") + + +if __name__ == "__main__": + main() diff --git a/scripts/generate-wh-flow-testpoints.py b/scripts/generate-wh-flow-testpoints.py new file mode 100644 index 0000000..340619f --- /dev/null +++ b/scripts/generate-wh-flow-testpoints.py @@ -0,0 +1,1124 @@ +#!/usr/bin/env python3 +"""Generate 库房管理 / 流程设置 testpoint MD and patch UI-SPEC / INDEX.""" +from __future__ import annotations + +import json +import re +from datetime import date +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +SCAN_DIR = ROOT / "data" / "scan" +OUT_DIR = ROOT / "testpoint" +KB = ROOT / "knowledge-base" / "08-UI专项规范库" +INDEX = KB / "INDEX.md" +TODAY = date.today().isoformat() + +PAGES = [ + { + "pageKey": "warehouse-zone", + "specId": "WH-001", + "group": "库房管理", + "name": "库区设置", + "prefix": "TP_WH_ZON", + "file": "基础管理-库房管理-库区设置-测试点分析-01.md", + "kind": "zone", + "path": "/spd/basic/storehouse/platform/storehouse/area", + "po": "pages/spd/warehouse-zone.page.ts", + "scan_cmd": "discover:scan-warehouse", + }, + { + "pageKey": "warehouse-probe", + "specId": "WH-002", + "group": "库房管理", + "name": "探针绑定", + "prefix": "TP_WH_PRB", + "file": "基础管理-库房管理-探针绑定-测试点分析-01.md", + "kind": "probe", + "path": "/spd/basic/storehouse/storehouse/probe", + "po": "pages/spd/warehouse-probe.page.ts", + "scan_cmd": "discover:scan-warehouse", + }, + { + "pageKey": "flow-def", + "specId": "FLOW-001", + "group": "流程设置", + "name": "审批流程设置", + "prefix": "TP_FLOW_DEF", + "file": "基础管理-流程设置-审批流程设置-测试点分析-01.md", + "kind": "def", + "path": "/spd/basic/flowSet/workflow/flowSet", + "po": "pages/spd/flow-def.page.ts", + "scan_cmd": "discover:scan-flow", + }, + { + "pageKey": "flow-inst", + "specId": "FLOW-002", + "group": "流程设置", + "name": "审批流实例", + "prefix": "TP_FLOW_INS", + "file": "基础管理-流程设置-审批流实例-测试点分析-01.md", + "kind": "inst", + "path": "/spd/basic/flowSet/workflow/flowCase", + "po": "pages/spd/flow-inst.page.ts", + "scan_cmd": "discover:scan-flow", + }, +] + + +def load_scan(page_key: str) -> dict: + path = SCAN_DIR / f"{page_key}-elements.json" + data = json.loads(path.read_text(encoding="utf-8")) + return {"url": data["url"].split("?")[0], "elementCount": data["elementCount"]} + + +def tp(prefix: str, n: int, prio: str, title: str, check: str, spec: str) -> str: + return f"- **{prefix}_{n:03d}**({prio})|{title}|验证要点:{check}|关联需求:{spec}" + + +def section(title: str, blocks: list[tuple[str, list[str]]]) -> str: + parts = [f"### {title}", ""] + for h4, lines in blocks: + parts.append(f"#### {h4}") + parts.append("") + parts.extend(lines or ["- 无(本功能点无独立该类场景,规则已归入其它功能点)"]) + parts.append("") + return "\n".join(parts) + + +class Seq: + def __init__(self, prefix: str, spec: str): + self.prefix = prefix + self.spec = spec + self.n = 0 + + def add(self, prio: str, title: str, check: str) -> str: + self.n += 1 + return tp(self.prefix, self.n, prio, title, check, self.spec) + + +def nav_section(p: dict, seq: Seq) -> str: + name = p["name"] + group = p["group"] + path = p["path"] + if p["kind"] in ("zone", "probe"): + peers = "库区设置 / 探针绑定" + ready = ( + "左右分栏:左侧「库房信息」树、右侧库区列表、新增与分页可见,无报错遮罩" + if p["kind"] == "zone" + else "整页单表:探针ID/主机编码/是否绑定货位筛选、查询重置、列表与分页可见,无报错遮罩" + ) + ctx = "顶栏当前库房/院区与本页可维护库房范围不矛盾" + else: + peers = "审批流程设置 / 审批流实例" + ready = ( + "左右分栏:左侧业务类型、右侧默认审批流与自定义审批流、保存/重置可见,无报错遮罩" + if p["kind"] == "def" + else "整页单表:单据编号/审批状态/发起时间/业务类型/摘要筛选、查询重置、列表与分页可见,无报错遮罩" + ) + ctx = "顶栏登录身份与本页可查看的审批单据范围不矛盾" + return section( + "页面访问与导航", + [ + ( + "功能验证点", + [ + seq.add( + "高", + f"菜单路径可达{name}", + f"登录后通过「基础设置 → {group} → {name}」进入 `{path}`,左侧菜单与顶栏 Tab 高亮为{name},不串到同组其它页", + ), + seq.add("高", "页面主区加载完成", ready), + seq.add( + "中", + f"同组{group}菜单可切换", + f"在 {peers} 间切换后 URL 与页面内容对应,再返回{name}状态正确", + ), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "已登录直接打开 URL", f"携带有效登录态打开 `{path}`,页面正常渲染"), + ], + ), + ( + "异常验证点", + [ + seq.add("高", "未登录访问拦截", "无登录态访问 URL 跳转登录或不展示业务数据"), + seq.add("中", "登录态过期", "会话失效后查询/写操作被拦截并提示重新登录,不误写"), + seq.add("中", "无菜单权限", f"无{name}权限的账号不可见菜单或 URL 被拒绝"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "顶栏身份与可管理范围一致", ctx), + ], + ), + ], + ) + + +def zone_body(seq: Seq) -> str: + chunks = [ + section( + "左侧库房树", + [ + ( + "功能验证点", + [ + seq.add("高", "库房树默认有选中项", "进入后左侧「库房信息」有节点且一项处于选中,右侧为该库房的库区"), + seq.add("高", "按库房名称过滤树", "在左侧「库房名称」输入关键字后,仅保留名称匹配的库房节点"), + seq.add("中", "清空库房名称恢复全量树", "清空关键字后树恢复为过滤前的全量节点"), + seq.add("高", "切换库房刷新右侧列表", "点选另一库房后,右侧库区编号/名称/地址属于该库房,「共 N 条」同步变化"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "超长库房名称关键字", "输入达到或超过允许长度,过滤被接受或截断,页面不崩溃"), + seq.add("低", "仅空格库房名称", "按空过滤处理或提示,不报错"), + ], + ), + ( + "异常验证点", + [ + seq.add("低", "无匹配库房空树", "关键字无匹配时树为空或明确无数据,右侧列表不残留上一库房库区"), + seq.add("中", "库房树加载失败", "库房接口失败有错误提示,不把过期树展示为成功态"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "库房节点与院区主数据同源", "树中库房名称与院区/库房主数据一致(允许按权限裁剪)"), + ], + ), + ], + ), + section( + "右侧库区列表与分页", + [ + ( + "功能验证点", + [ + seq.add( + "高", + "列表核心列完整", + "主表展示库区编号、库区名称、库区地址、是否专项库区、操作(编辑/人员管理/货位管理/停用或启用)", + ), + seq.add("高", "默认库区以标签展示", "默认库区在名称旁显示「默认库区」标签,不是独立列;同一库房至多一个默认库区"), + seq.add("中", "专项库区取值", "「是否专项库区」仅为「是」或「否」"), + seq.add("中", "分页翻页", "总条数大于一页时可翻至第 2 页,数据更换且页码高亮正确"), + seq.add("中", "每页条数切换", "切换「50 条/页」等后当前页行数不超过所选值,总数不变"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "首页上一页不可用", "第 1 页时「上一页」禁用或点击无效"), + seq.add("中", "末页下一页不可用", "最后一页时「下一页」禁用或点击无效"), + seq.add("低", "库房无库区时分页为 0", "选中尚无库区的库房时列表空态明确,「共 0 条」"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "库区列表加载失败", "失败有错误提示,不白屏、不把上一库房数据展示为当前库房成功态"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "翻页不串库房", "带当前库房选中翻页后,新页库区仍属于该库房"), + seq.add("中", "列表条数与共 N 条一致", "可见行逻辑与底部分页「共 N 条」一致"), + ], + ), + ], + ), + section( + "库区搜索", + [ + ( + "功能验证点", + [ + seq.add("高", "按库区编号搜索", "在「库区编号/名称」输入已存在编号,结果包含该编号行"), + seq.add("高", "按库区名称搜索", "输入已存在名称关键字,结果名称匹配"), + seq.add("中", "搜索范围限于当前库房", "搜索结果全部属于左侧当前选中库房"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "超长库区关键字", "超长输入可检索或被截断,页面不崩溃"), + seq.add("低", "仅空格库区关键字", "按空搜索处理或提示,不报错"), + ], + ), + ( + "异常验证点", + [ + seq.add("低", "无匹配库区空态", "无命中时列表为空且分页为 0"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "切换库房后搜索条件不串结果", "换库房后结果属于新库房;若保留关键字则在新库房范围内过滤"), + ], + ), + ], + ), + section( + "新增库区", + [ + ( + "功能验证点", + [ + seq.add("高", "打开新增库区弹窗", "点「新增」弹出「新增库区」,含库区编号、库区名称、库区地址、联系人、联系方式及取消/确定"), + seq.add("高", "合法必填保存成功", "填写必填后确定,弹窗关闭,当前库房列表出现新记录且字段与提交一致"), + seq.add("中", "取消不落库", "填写后点「取消」或关闭,列表不新增对应库区"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "编号名称达最大允许长度", "库区编号/名称取最大允许长度可保存成功"), + seq.add("低", "地址可与编号或名称相同", "库区地址填写与编号或名称相同的文本可保存"), + ], + ), + ( + "异常验证点", + [ + seq.add("高", "必填为空拦截", "库区编号或名称为空点确定被拦截并提示,不关闭弹窗、不落库"), + seq.add("高", "同一库房库区编号重复拦截", "使用当前库房已存在编号保存失败并提示,不覆盖原库区"), + seq.add("中", "保存接口失败", "失败有错误提示,弹窗仍在,列表不出现半成品成功态"), + seq.add("中", "重复点击确定", "连续快速点击只产生一条记录或第二次被防重"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "新增归属当前选中库房", "新增成功后该库区仅出现在当前库房列表,切换其它库房不可见"), + ], + ), + ], + ), + section( + "编辑库区", + [ + ( + "功能验证点", + [ + seq.add("高", "打开编辑并回填", "行内「编辑」打开「编辑库区信息」,回填库区编号、名称、地址、联系人、联系方式"), + seq.add("高", "修改后确定落库", "修改名称或地址后确定,列表与再次编辑回看均为新值"), + seq.add("中", "编辑取消不保存", "修改后点「取消」,列表与再次打开仍为修改前值"), + seq.add("中", "专项库区回填一致", "列表「是否专项库区」与编辑页回填一致(若编辑页含该字段)"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "不改字段直接确定", "打开编辑不改内容点确定,记录保持原值且不报错"), + seq.add("中", "默认库区编辑后仍唯一", "编辑保存后该库房仍至多一个「默认库区」标签"), + ], + ), + ( + "异常验证点", + [ + seq.add("高", "编辑清空必填拦截", "清空库区名称后确定被拦截,原记录不被写成空"), + seq.add("中", "编辑已停用库区", "停用行仍可打开编辑;若禁止编辑则入口禁用并提示,不以成功态改数"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "编辑不影响其它库房同名库区", "仅更新当前库房该行,其它库房同编号/名称库区保持原值"), + ], + ), + ], + ), + section( + "停用与启用", + [ + ( + "功能验证点", + [ + seq.add("高", "停用后操作变为启用", "启用行点「停用」后该行操作变为「启用」,状态持久化"), + seq.add("高", "启用后操作变为停用", "停用行点「启用」后该行操作变为「停用」,再次进入仍保持"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "默认库区停用限制", "默认库区若不允许停用则被拦截并提示;若允许停用则该库房须仍满足「至多一个默认库区」"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "停用/启用失败有提示", "接口失败有错误提示,行操作文案不翻转"), + seq.add("中", "停用须确认才生效", "若有确认框,取消后行仍为启用、操作仍为「停用」"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "停用库区不可再被探针绑定为新货位", "停用后探针绑定弹窗按货位状态/启用过滤时不应再选出该库区下可绑定货位(以现网过滤规则为准)"), + ], + ), + ], + ), + section( + "人员管理", + [ + ( + "功能验证点", + [ + seq.add("高", "打开人员管理", "行内「人员管理」打开弹层或子页,标题表明为人员管理且对应当前库区"), + seq.add("中", "关闭后回到库区列表", "关闭人员管理后仍停留在当前库房的库区列表,选中库房不变"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "无人员时列表空态", "未配置人员时空态明确,不报错"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "人员管理加载失败", "失败有错误提示,不把其它库区人员展示为当前库区"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "人员与用户管理账号同源", "可选人员来自用户/角色主数据(允许按权限裁剪)"), + ], + ), + ], + ), + section( + "货位管理", + [ + ( + "功能验证点", + [ + seq.add("高", "打开货位管理", "行内「货位管理」打开弹层或子页,标题表明为货位且对应当前库区"), + seq.add("中", "关闭后回到库区列表", "关闭货位管理后当前库房选中与库区列表保持"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "无货位时空态", "库区无货位时列表空态明确"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "货位管理加载失败", "失败有提示,不串其它库区货位"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "货位可被探针绑定弹窗检索到", "本库区启用货位应出现在探针绑定「货位绑定探针」弹窗对应库房/库区筛选结果中"), + ], + ), + ], + ), + section( + "同步库房信息", + [ + ( + "功能验证点", + [ + seq.add("中", "同步刷新库房树", "点「同步」后树与列表按最新主数据刷新,不丢当前可维护库房"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "同步期间不可重复提交", "同步进行中按钮禁用或再次点击被忽略,不并发写坏树"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "同步失败有提示", "失败有错误提示,树不空白、不把失败展示为成功"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "同步结果与院区库房主数据一致", "同步后新增/更名的库房节点与主数据一致"), + ], + ), + ], + ), + ] + return "\n---\n\n".join(chunks) + + +def probe_body(seq: Seq) -> str: + chunks = [ + section( + "筛选与查询", + [ + ( + "功能验证点", + [ + seq.add("高", "默认条件查询", "进入后点「查询」,列表有数据或空态明确,底部分页「共 N 条」与行数逻辑一致"), + seq.add("高", "按探针ID查询", "输入已存在探针ID后结果探针ID匹配,精确命中时至多对应行"), + seq.add("高", "按主机编码查询", "输入主机编码后结果「主机编码」与条件一致(多探针可共用同一主机)"), + seq.add("高", "按是否绑定货位查询", "选择「已绑定」仅保留已绑定行;选择「未绑定」仅保留未绑定行"), + seq.add("高", "探针ID+主机编码+是否绑定组合查询", "多个已填条件同时生效,结果同时满足全部条件"), + seq.add("中", "重置恢复默认", "填写筛选后点「重置」,条件恢复默认,列表按默认规则刷新"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "超长探针ID或主机编码", "超长输入可查询或被截断,页面不崩溃"), + seq.add("低", "仅空格关键字", "按空查询处理或提示,不报错"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "查询接口失败", "失败有错误提示,不白屏、不把过期数据展示为成功态"), + seq.add("低", "无匹配结果空态", "条件无匹配时「暂无数据」明确,分页总数为 0"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "筛选结果与分页总数一致", "查询后可见行逻辑与「共 N 条」一致"), + ], + ), + ], + ), + section( + "列表与分页", + [ + ( + "功能验证点", + [ + seq.add( + "高", + "列表核心列完整", + "主表展示序号、探针ID、主机编码、是否绑定货位、绑定的货位、操作(绑定货位)", + ), + seq.add("高", "已绑定行数量大于 0", "「是否绑定货位」为已绑定时,「绑定的货位」数量大于 0"), + seq.add("高", "未绑定行数量为 0", "未绑定行「绑定的货位」为 `0`"), + seq.add("中", "绑定的货位列为数量不是货位号", "主表该列为数字;货位号只在弹窗「货位号」列"), + seq.add("中", "分页翻页", "总条数大于一页时可翻页,数据更换且页码正确"), + seq.add("中", "每页条数切换", "切换每页条数后当前页行数不超过所选值,总数不变"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "首页上一页不可用", "第 1 页时「上一页」禁用或点击无效"), + seq.add("中", "末页下一页不可用", "最后一页时「下一页」禁用或点击无效"), + ], + ), + ( + "异常验证点", + [ + seq.add("低", "跳至非数字页码", "非法页码不跳转异常页"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "翻页不丢失当前筛选", "带筛选翻页后条件保持,新页仍满足原筛选"), + ], + ), + ], + ), + section( + "打开绑定货位弹窗", + [ + ( + "功能验证点", + [ + seq.add("高", "行内打开货位绑定探针", "点「绑定货位」弹出「货位绑定探针」,含库房/库区/货位筛选与货位列表"), + seq.add("中", "取消关闭不改主列表", "未确定即点「取消」或关闭,主列表绑定数量与筛选条件保持"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "未选货位时确定计数为 0", "底部按钮文案为「确定(0)」或等价,未选时确定不新增绑定"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "弹窗加载失败", "货位列表失败有提示,不把主列表「共 N 条」与弹窗总条数混用"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "弹窗分页独立于主列表", "弹窗「共 N 条」(货位量级)不得用于断言主列表探针条数"), + ], + ), + ], + ), + section( + "弹窗筛选货位", + [ + ( + "功能验证点", + [ + seq.add("高", "按当前库房筛选", "选择库房后列表「当前库房」与所选一致"), + seq.add("高", "按当前库区筛选", "选择库区后列表「当前库区」与所选一致"), + seq.add("高", "按货位号查询", "「货位查询」输入货位号关键字后结果货位号匹配"), + seq.add("中", "弹窗内重置", "填写弹窗筛选后点「重置」,条件恢复,列表按弹窗默认规则刷新"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "超长货位号关键字", "超长输入可查询或被截断,弹窗不崩溃"), + seq.add("低", "库区下拉随库房收敛", "切换库房后库区选项属于该库房,不残留上一库房库区"), + ], + ), + ( + "异常验证点", + [ + seq.add("低", "弹窗无匹配空态", "条件无命中时货位列表空、弹窗分页为 0"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "库房库区与库区设置同源", "弹窗库房/库区选项来自库区设置中已维护且可用的库房与库区"), + ], + ), + ], + ), + section( + "勾选货位并保存绑定", + [ + ( + "功能验证点", + [ + seq.add("高", "勾选后确定计数增加", "勾选 N 个货位后确定按钮计数为 N"), + seq.add("高", "确定后主列表绑定数量增加", "为未绑定探针勾选货位并确定后,该行变为已绑定,数量等于本次保存的货位数(或按产品规则累加)"), + seq.add("中", "已绑货位行展示探针ID", "弹窗中已被该探针绑定的货位「探针ID」列显示该探针号,未绑定为 `--`"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "取消勾选后计数回退", "勾选后再取消,确定计数回到实际已选数"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "绑定保存失败", "失败有提示,主列表绑定数量不增加,再次打开不是半成功勾选"), + seq.add("中", "货位已被其它探针占用", "占用行再绑定时按换绑或拒绝执行;以保存后主列表数量与弹窗探针ID列为准,不得静默双绑"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "绑定结果与货位管理一致", "保存后在对应库区货位侧能看到该探针占用;解绑/换绑后两侧同步"), + ], + ), + ], + ), + ] + return "\n---\n\n".join(chunks) + + +def def_body(seq: Seq) -> str: + chunks = [ + section( + "左侧业务类型", + [ + ( + "功能验证点", + [ + seq.add("高", "业务类型列表可见", "左侧列出采购计划、请领单、产品准入等类型,当前项高亮"), + seq.add("高", "按流程名称过滤类型", "输入关键字后仅保留名称匹配的业务类型"), + seq.add("中", "清空关键字恢复全量", "清空后类型列表恢复过滤前全量"), + seq.add("高", "切换类型刷新右侧配置", "点选另一类型后,默认审批流与自定义审批流与该类型已保存配置一致,不残留上一类型节点"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "超长流程名称关键字", "超长输入可过滤或被截断,页面不崩溃"), + seq.add("低", "仅空格关键字", "按空过滤处理或提示,不报错"), + ], + ), + ( + "异常验证点", + [ + seq.add("低", "无匹配类型", "无命中时左侧为空或明确无数据,右侧不展示上一类型配置为当前配置"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "类型与审批流实例业务类型同源", "左侧类型集合与审批流实例「业务类型」下拉选项一致(允许按权限裁剪)"), + ], + ), + ], + ), + section( + "默认审批流", + [ + ( + "功能验证点", + [ + seq.add("高", "默认审批流节点可见", "右侧「默认审批流」展示节点卡片,含节点类型、账号、人员"), + seq.add("中", "可追加默认流节点", "点节点旁「+」可增加审批节点(保存前仅在当前页可见)"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "未配置自定义流时走默认链", "该类型无自定义流时,新单据按默认审批人链流转(以审批流实例抽样为准)"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "默认流节点人员为空拦截", "保存时默认链缺少人员被拦截并提示,不写入空节点"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "只改自定义流时默认链不变", "仅增删自定义行并保存后,默认审批人节点保持原值"), + ], + ), + ], + ), + section( + "自定义审批流", + [ + ( + "功能验证点", + [ + seq.add("高", "新增流程增加一行", "点「新增流程」自定义流增加一行,含流程名称、条件、审批流程、删除"), + seq.add("中", "删除行未保存不落库", "删除自定义行后不点保存,重新进入该类型行仍在"), + seq.add("高", "保存后自定义流持久化", "填写名称/条件/节点后保存,重新进入该类型配置与保存时一致"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "流程名称必填", "名称空时保存被拦截"), + seq.add("中", "条件必填", "条件空时保存被拦截"), + seq.add("中", "审批流程必填", "无审批节点时保存被拦截"), + ], + ), + ( + "异常验证点", + [ + seq.add("高", "必填为空拦截", "流程名称/条件/审批流程任一为空点保存被拦截并提示,不关闭、不落库"), + seq.add("中", "保存接口失败", "失败有提示,再次进入不是半成功配置"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "自定义条件命中后新单走该链", "保存后新产生的符合条件单据在审批流实例中按该自定义链流转,而非默认链"), + ], + ), + ], + ), + section( + "条件配置", + [ + ( + "功能验证点", + [ + seq.add("高", "打开条件配置", "条件列「编辑」打开「条件配置」,可添加条件行并保存/取消"), + seq.add("高", "保存条件回写行内文案", "配置如「是否超限:是」或「科室:中心库(洪山)」后保存,行内条件展示与配置一致"), + seq.add("中", "取消不改行内条件", "修改后点取消,行内条件仍为打开前的值"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "可删除条件行", "条件配置内「删除」去掉一行,保存后不再出现该条件"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "条件不完整拦截", "条件类型或取值空时保存被拦截"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "科室条件与库房/科室主数据同源", "条件选科室时选项来自主数据,与库区设置/用户所属科室不矛盾"), + ], + ), + ], + ), + section( + "保存与重置", + [ + ( + "功能验证点", + [ + seq.add("高", "保存写入当前业务类型", "点「保存」只持久化当前选中类型的默认流与自定义流"), + seq.add("高", "重置放弃未保存改动", "改节点或自定义行后点「重置」,页面回到上次保存结果"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "无改动点保存不报错", "未修改直接保存成功或提示无变更,配置不变"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "未保存切换类型", "有未保存改动时切换业务类型须提示后丢弃或不允许切换,不得把上一类型的改动写入下一类型"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "保存不影响历史审批实例", "改审批人后,已存在的审批流实例详情仍为发起时的快照或按产品规则说明;不得无说明地改写历史已通过单"), + ], + ), + ], + ), + ] + return "\n---\n\n".join(chunks) + + +def inst_body(seq: Seq) -> str: + chunks = [ + section( + "筛选与查询", + [ + ( + "功能验证点", + [ + seq.add("高", "默认日期区间查询", "进入后带默认发起时间区间,点「查询」列表有数据或空态明确,「共 N 条」与结果逻辑一致"), + seq.add("高", "单据编号精确查询", "输入完整单号后至多一条,且编号、业务类型与该行一致"), + seq.add("高", "按审批状态筛选", "选择「审批通过」或「审批中」后列表状态列与所选一致"), + seq.add("高", "按业务类型筛选", "选择某类型后列表「业务类型」均为该类型"), + seq.add("高", "按发起时间区间筛选", "结果行「发起时间」落在起止日期内(含边界日)"), + seq.add("中", "按摘要关键字筛选", "输入摘要关键字后结果摘要包含该关键字;摘要为 `--` 的行不被非空关键字命中"), + seq.add("高", "多条件组合查询", "状态、业务类型、发起时间、摘要同时生效,抽样行满足全部已填条件"), + seq.add("中", "重置恢复默认", "填写后点「重置」,日期回到默认区间,其余条件清空,「共 N 条」恢复"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "超长单据编号或摘要", "超长输入可查询或被截断,页面不崩溃"), + seq.add("中", "开始日期晚于结束日期", "非法区间被拒绝或自动纠正,不返回错乱时间结果"), + seq.add("低", "仅空格摘要", "按空查询处理或提示,不报错"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "查询接口失败", "失败有错误提示,不白屏、不把过期列表展示为成功态"), + seq.add("低", "无匹配空态", "总数为 0,空态明确"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "筛选结果与分页总数一致", "查询后可见行逻辑与「共 N 条」一致"), + seq.add("高", "业务类型与审批流程设置同源", "「业务类型」下拉与审批流程设置左侧类型一致(允许按权限裁剪)"), + ], + ), + ], + ), + section( + "列表与分页", + [ + ( + "功能验证点", + [ + seq.add( + "高", + "列表核心列完整", + "主表展示序号、单据编号、业务类型、发起人、发起部门、发起时间、摘要、审批状态、操作(详情)", + ), + seq.add("中", "单据编号前缀与业务类型对应", "`PLAN` 对应采购计划、`DB` 对应二级库调拨、`GC` 对应产品信息变更、`BUY` 对应请领单(以现网枚举为准)"), + seq.add("中", "空摘要展示为 --", "无摘要行显示 `--`,长摘要可截断"), + seq.add("中", "窄列截断不影响查询", "发起人/部门/时间截断展示时,按完整单号搜索仍能命中"), + seq.add("中", "分页翻页", "总条数大于一页时可翻页,数据更换"), + seq.add("中", "每页条数切换", "切换后当前页行数不超过所选值,总数不变"), + seq.add("中", "跳至指定页", "输入合法页码后跳到对应页"), + ], + ), + ( + "边界验证点", + [ + seq.add("中", "首页上一页不可用", "第 1 页时「上一页」禁用或点击无效"), + seq.add("中", "末页下一页不可用", "最后一页时「下一页」禁用或点击无效"), + seq.add("低", "跳至超出总页数", "大于总页数时被拒绝、纠正到末页或提示"), + ], + ), + ( + "异常验证点", + [ + seq.add("低", "跳至非数字页码", "非法输入不跳转异常页"), + ], + ), + ( + "集成验证点", + [ + seq.add("中", "翻页不丢失当前筛选", "带筛选翻页后条件保持,新页数据仍满足原筛选"), + ], + ), + ], + ), + section( + "审批详情", + [ + ( + "功能验证点", + [ + seq.add("高", "打开详情对应当前行", "点「详情」打开该单据审批过程,标题/单号与当前行单据编号一致"), + seq.add("中", "详情只读", "详情内不能改审批人或条件;改配置须回到审批流程设置"), + seq.add("中", "关闭详情保留列表状态", "返回后筛选条件与页码保持,列表仍是打开前的查询结果"), + ], + ), + ( + "边界验证点", + [ + seq.add("低", "审批中与已通过均可打开详情", "两种状态行都能进入详情,节点展示与状态相符"), + ], + ), + ( + "异常验证点", + [ + seq.add("中", "详情加载失败", "失败有提示,不把其它单据内容展示为当前单"), + ], + ), + ( + "集成验证点", + [ + seq.add("高", "详情审批链与流程设置一致", "新单据详情中的节点/人员与该业务类型在审批流程设置中命中的默认或自定义链一致"), + ], + ), + ], + ), + ] + return "\n---\n\n".join(chunks) + + +def quality(p: dict, el_count: int, tp_count: int) -> str: + kind = p["kind"] + if kind == "zone": + cover = "导航、库房树过滤/切换、库区列表分页、库区搜索、新增/编辑弹窗、停用启用、人员/货位入口、同步" + pending = ( + "① 人员管理/货位管理扫描未打开面板,入口以行内按钮为准,子页字段待补扫 " + "② 默认库区如何指定(编辑字段或单独接口)以现网为准 " + "③ 停用默认库区是否允许待产品确认 " + "④ 新增弹窗扫描未见「是否专项库区」控件,列表有该列,编辑是否可改为待评审" + ) + elif kind == "probe": + cover = "导航、探针筛选查询、列表数量规则、绑定弹窗筛选分页、勾选确定/取消、与库区货位集成" + pending = ( + "① 同一货位已有其它探针ID时是换绑还是拒绝,以保存后主列表数量与弹窗探针ID列为准 " + "② 确定按钮文案为「确定(N)」,未选为 0 " + "③ 主表无新增探针入口,探针主数据来源不在本页维护" + ) + elif kind == "def": + cover = "导航、业务类型过滤/切换、默认审批流、自定义流增删、条件配置、保存重置、与审批流实例类型同源" + pending = ( + "① 保存会改线上审批路由,自动化冒烟只读结构、不提交 " + "② 未保存切类型按「提示后丢弃、不串写」写点,若自动保存需改判定 " + "③ 历史已通过实例是否快照冻结待产品确认" + ) + else: + cover = "导航、单据编号/状态/类型/时间/摘要筛选、列表列与前缀规则、分页、只读详情、与流程设置类型同源" + pending = ( + "① 默认发起时间跨度大,断言「共 N 条」须先收窄日期 " + "② 本页无新增/导出/改审批人;写配置只在审批流程设置 " + "③ 详情扫描打开「采购计划详情」,其它业务类型详情标题以现网为准" + ) + return f"""## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:{cover};共 **{tp_count}** 条原子测试点(未声称全覆盖,断网类与仓库其它基础管理页同样省略) | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分;无某类时保留标题并说明 | +| 去重 | 登录拦截、会话过期与同模块其它页规则相同,本页仅保留菜单/URL/字段差异;编号前缀 `{p["prefix"]}` | +| 待评审项 | {pending} | +| 下游 | 可转结构化 MD/CSV;扫描 `npm run {p["scan_cmd"]}`;自动化建议 `{p["po"]}` | +""" + + +def render_testpoint(p: dict, scan: dict) -> tuple[str, int]: + spec = f"UI-SPEC-SPD-{p['specId']}" + seq = Seq(p["prefix"], spec) + nav = nav_section(p, seq) + if p["kind"] == "zone": + body = zone_body(seq) + elif p["kind"] == "probe": + body = probe_body(seq) + elif p["kind"] == "def": + body = def_body(seq) + else: + body = inst_body(seq) + md = f"""# 基础管理-{p['group']}-{p['name']} — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/{spec}.md` +**页面 URL**:`{scan["url"]}` +**版本 / 定稿日期**:{TODAY} +**元素扫描**:`data/scan/{p["pageKey"]}-elements.json`({scan["elementCount"]} 项) +**定位器**:`locators/spd/{p["pageKey"]}.locator.yaml` + +--- + +## 基础设置 · {p['group']} · {p['name']} + +{nav} +--- + +{body} + +--- + +{quality(p, scan["elementCount"], seq.n)} +""" + return md, seq.n + + +def patch_ui_spec(p: dict, scan: dict, tp_file: str) -> None: + path = KB / f"UI-SPEC-SPD-{p['specId']}.md" + text = path.read_text(encoding="utf-8") + text = re.sub(r'update_time: "[^"]*"', f'update_time: "{TODAY}"', text, count=1) + text = re.sub(r'related_tc: "[^"]*"', f'related_tc: "testpoint/{tp_file}"', text, count=1) + url = scan["url"] + count = scan["elementCount"] + page_key = p["pageKey"] + extra = f"| 浏览器页签标题 | `{p['name']}` |\n" + table = f"""| 项 | 值 | +|----|-----| +| 主框架 URL | `{url}` | +{extra}| iframe 选择器 | 无(整页渲染,非 iframe 报表) | +| 元素扫描 JSON | `data/scan/{page_key}-elements.json`({count} 项) | +| 定位器 YAML | `locators/spd/{page_key}.locator.yaml` | +| 测试环境示例 | `http://spdtest.cmic.com.cn:8080/spd/form/` |""" + text = re.sub( + r"\| 项 \| 值 \|\n\|----\|-----\|\n(?:\| .+\|\n)+", + table + "\n", + text, + count=1, + ) + path.write_text(text, encoding="utf-8") + + +def patch_index(counts: dict[str, int], scans: dict[str, dict]) -> None: + text = INDEX.read_text(encoding="utf-8") + text = re.sub(r'update_time: "[^"]*"', f'update_time: "{TODAY}"', text, count=1) + rows = { + "WH-001": ( + "| 库区设置 | WH-001 | `/spd/basic/storehouse/platform/storehouse/area` | " + f"已扫描 · `warehouse-zone` · **{scans['WH-001']['elementCount']}** 项 · " + f"测试点 **{counts['WH-001']}** 条 · `TP_WH_ZON` |" + ), + "WH-002": ( + "| 探针绑定 | WH-002 | `/spd/basic/storehouse/storehouse/probe` | " + f"已扫描 · `warehouse-probe` · **{scans['WH-002']['elementCount']}** 项 · " + f"测试点 **{counts['WH-002']}** 条 · `TP_WH_PRB` |" + ), + "FLOW-001": ( + "| 审批流程设置 | FLOW-001 | `/spd/basic/flowSet/workflow/flowSet` | " + f"已扫描 · `flow-def` · **{scans['FLOW-001']['elementCount']}** 项 · " + f"测试点 **{counts['FLOW-001']}** 条 · `TP_FLOW_DEF` |" + ), + "FLOW-002": ( + "| 审批流实例 | FLOW-002 | `/spd/basic/flowSet/workflow/flowCase` | " + f"已扫描 · `flow-inst` · **{scans['FLOW-002']['elementCount']}** 项 · " + f"测试点 **{counts['FLOW-002']}** 条 · `TP_FLOW_INS` |" + ), + } + text = re.sub( + r"\| 库区设置 \| WH-001 \| `/spd/basic/storehouse/platform/storehouse/area` \| .+ \|", + rows["WH-001"], + text, + count=1, + ) + text = re.sub( + r"\| 探针绑定 \| WH-002 \| `/spd/basic/storehouse/storehouse/probe` \| .+ \|", + rows["WH-002"], + text, + count=1, + ) + text = re.sub( + r"\| 审批流程设置 \| FLOW-001 \| `/spd/basic/flowSet/workflow/flowSet` \| .+ \|", + rows["FLOW-001"], + text, + count=1, + ) + text = re.sub( + r"\| 审批流实例 \| FLOW-002 \| `/spd/basic/flowSet/workflow/flowCase` \| .+ \|", + rows["FLOW-002"], + text, + count=1, + ) + if "generate:testpoints-warehouse" not in text: + text = text.replace( + "npm run discover:scan-warehouse\n", + "npm run discover:scan-warehouse\nnpm run generate:testpoints-warehouse\n", + ) + if "generate:testpoints-flow" not in text: + text = text.replace( + "npm run discover:scan-flow\n", + "npm run discover:scan-flow\nnpm run generate:testpoints-flow\n", + ) + INDEX.write_text(text, encoding="utf-8") + + +def main() -> None: + import sys + + filter_keys = set(sys.argv[1:]) + pages = [p for p in PAGES if not filter_keys or p["pageKey"] in filter_keys] + if not pages: + raise SystemExit("No pages matched. Available: " + ", ".join(p["pageKey"] for p in PAGES)) + + OUT_DIR.mkdir(parents=True, exist_ok=True) + counts: dict[str, int] = {} + scans: dict[str, dict] = {} + for p in pages: + scan = load_scan(p["pageKey"]) + md, n = render_testpoint(p, scan) + out = OUT_DIR / p["file"] + out.write_text(md, encoding="utf-8") + patch_ui_spec(p, scan, p["file"]) + counts[p["specId"]] = n + scans[p["specId"]] = scan + print(f"OK {p['specId']}\t{p['name']}\t{scan['elementCount']} el\t{n} TP\t{out.name}") + # INDEX 四行一起补全:缺页时用已落盘测试点文件行数占位,避免半更新 + if len(counts) < len(PAGES): + for p in PAGES: + if p["specId"] in counts: + continue + scans[p["specId"]] = load_scan(p["pageKey"]) + tp_file = OUT_DIR / p["file"] + n = 0 + if tp_file.exists(): + n = len(re.findall(r"\*\*TP_[A-Z0-9_]+_\d{3}\*\*", tp_file.read_text(encoding="utf-8"))) + counts[p["specId"]] = n + patch_index(counts, scans) + print("done", counts) + + +if __name__ == "__main__": + main() diff --git a/scripts/json-to-locator-yaml.mjs b/scripts/json-to-locator-yaml.mjs index 9ef76c2..b070383 100644 --- a/scripts/json-to-locator-yaml.mjs +++ b/scripts/json-to-locator-yaml.mjs @@ -5,6 +5,8 @@ * Usage: * node scripts/json-to-locator-yaml.mjs data/scan/warn-strand-elements.json * node scripts/json-to-locator-yaml.mjs --all-extended + * node scripts/json-to-locator-yaml.mjs --all-basic + * node scripts/json-to-locator-yaml.mjs --all-new */ import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -13,6 +15,8 @@ import { writeYamlFromScanJson } from './scan-interactive-elements.mjs'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ROOT = path.resolve(__dirname, '..'); +const SCAN_DIR = path.join(ROOT, 'data/scan'); +const LOC_DIR = path.join(ROOT, 'locators/spd'); const EXTENDED_KEYS = [ 'warn-strand', @@ -35,23 +39,106 @@ const EXTENDED_KEYS = [ 'reag-entry', ]; +const NEW_PAGES = { + 'basic-notice': { pageTitle: '基础设置 > 医院资料 > 公告管理', module: 'spd-basic' }, + 'basic-hospital-info': { pageTitle: '基础设置 > 医院资料 > 医院信息登记', module: 'spd-basic' }, + 'basic-finance-month': { pageTitle: '基础设置 > 医院资料 > 财务月份设置', module: 'spd-basic' }, + 'basic-branch': { pageTitle: '基础设置 > 医院资料 > 院区管理', module: 'spd-basic' }, + 'basic-user': { pageTitle: '基础设置 > 用户管理 > 用户管理', module: 'spd-basic' }, + 'basic-user-union': { pageTitle: '基础设置 > 用户管理 > 医共体用户管理', module: 'spd-basic' }, + 'basic-role': { pageTitle: '基础设置 > 用户管理 > 角色管理', module: 'spd-basic' }, + 'admit-material': { pageTitle: '基础设置 > 准入管理 > 物料准入管理', module: 'spd-basic' }, + 'admit-product-apply': { pageTitle: '基础设置 > 准入管理 > 产品准入申请', module: 'spd-basic' }, + 'admit-material-list': { pageTitle: '基础设置 > 准入管理 > 物料准入单列表', module: 'spd-basic' }, + 'admit-product-list': { pageTitle: '基础设置 > 准入管理 > 产品准入单列表', module: 'spd-basic' }, + 'admit-supply': { pageTitle: '基础设置 > 准入管理 > 供货目录申请', module: 'spd-basic' }, + 'admit-task': { pageTitle: '基础设置 > 准入管理 > 准入任务分配', module: 'spd-basic' }, + 'vendor-query': { pageTitle: '基础设置 > 厂商管理 > 厂商查询', module: 'spd-basic' }, + 'vendor-qual': { pageTitle: '基础设置 > 厂商管理 > 供应商资质管理', module: 'spd-basic' }, + 'vendor-auth-first': { pageTitle: '基础设置 > 厂商管理 > 授权变更初审', module: 'spd-basic' }, + 'vendor-auth-final': { pageTitle: '基础设置 > 厂商管理 > 授权变更终审', module: 'spd-basic' }, + 'vendor-cert-first': { pageTitle: '基础设置 > 厂商管理 > 注册证变更初审', module: 'spd-basic' }, + 'vendor-cert-final': { pageTitle: '基础设置 > 厂商管理 > 注册证变更终审', module: 'spd-basic' }, + 'vendor-cert-diff': { pageTitle: '基础设置 > 厂商管理 > 注册证差异对比', module: 'spd-basic' }, + 'vendor-eval-scheme': { pageTitle: '基础设置 > 厂商管理 > 评价方案维护', module: 'spd-basic' }, + 'vendor-eval-indicator': { pageTitle: '基础设置 > 厂商管理 > 评价指标维护', module: 'spd-basic' }, + 'vendor-holiday': { pageTitle: '基础设置 > 厂商管理 > 供应商节假日管理', module: 'spd-basic' }, + 'surgery-base-pkg': { pageTitle: '基础设置 > 手术套包设置 > 基础包维护', module: 'spd-basic' }, + 'surgery-oper-type': { pageTitle: '基础设置 > 手术套包设置 > 术种维护', module: 'spd-basic' }, + 'surgery-std-pkg': { pageTitle: '基础设置 > 手术套包设置 > 标准套包维护', module: 'spd-basic' }, + 'warehouse-zone': { pageTitle: '基础设置 > 库房管理 > 库区设置', module: 'spd-basic' }, + 'warehouse-probe': { pageTitle: '基础设置 > 库房管理 > 探针绑定', module: 'spd-basic' }, + 'flow-def': { pageTitle: '基础设置 > 流程设置 > 审批流程设置', module: 'spd-basic' }, + 'flow-inst': { pageTitle: '基础设置 > 流程设置 > 审批流实例', module: 'spd-basic' }, +}; + +const GROUPS = { + '--all-extended': EXTENDED_KEYS, + '--all-basic': ['basic-notice', 'basic-hospital-info', 'basic-finance-month', 'basic-branch'], + '--all-user': ['basic-user', 'basic-user-union', 'basic-role'], + '--all-admit': [ + 'admit-material', + 'admit-product-apply', + 'admit-material-list', + 'admit-product-list', + 'admit-supply', + 'admit-task', + ], + '--all-vendor': [ + 'vendor-query', + 'vendor-qual', + 'vendor-auth-first', + 'vendor-auth-final', + 'vendor-cert-first', + 'vendor-cert-final', + 'vendor-cert-diff', + 'vendor-eval-scheme', + 'vendor-eval-indicator', + 'vendor-holiday', + ], + '--all-surgery': ['surgery-base-pkg', 'surgery-oper-type', 'surgery-std-pkg'], + '--all-warehouse': ['warehouse-zone', 'warehouse-probe'], + '--all-flow': ['flow-def', 'flow-inst'], + '--all-new': Object.keys(NEW_PAGES), +}; + +function jsonPathFor(key) { + return path.join(SCAN_DIR, `${key}-elements.json`); +} + +function writeOne(jsonPath, pageKey) { + const meta = NEW_PAGES[pageKey] || {}; + const result = writeYamlFromScanJson(jsonPath, { + pageKey: pageKey || path.basename(jsonPath, '-elements.json'), + pageTitle: meta.pageTitle, + module: meta.module || (pageKey && NEW_PAGES[pageKey] ? 'spd-basic' : undefined), + jsonSourceName: path.basename(jsonPath), + iframeMatch: '', + yamlOut: path.join(LOC_DIR, `${pageKey || path.basename(jsonPath, '-elements.json')}.locator.yaml`), + }); + console.log(`YAML: ${result.yamlOut} (${result.elementCount} elements)`); + return result; +} + async function main() { const args = process.argv.slice(2); - const targets = - args[0] === '--all-extended' - ? EXTENDED_KEYS.map((key) => path.join(ROOT, 'data/scan', `${key}-elements.json`)) - : args.length - ? [path.resolve(args[0])] - : []; - - if (!targets.length) { - console.error('Usage: node scripts/json-to-locator-yaml.mjs '); + if (!args.length) { + console.error( + 'Usage: node scripts/json-to-locator-yaml.mjs ', + ); process.exit(1); } - for (const jsonPath of targets) { - const result = writeYamlFromScanJson(jsonPath); - console.log(`YAML: ${result.yamlOut} (${result.elementCount} elements)`); + for (const arg of args) { + if (GROUPS[arg]) { + for (const key of GROUPS[arg]) { + writeOne(jsonPathFor(key), key); + } + continue; + } + const jsonPath = path.resolve(arg); + const pageKey = path.basename(jsonPath).replace(/-elements\.json$/, ''); + writeOne(jsonPath, pageKey); } } diff --git a/scripts/run-ci.mjs b/scripts/run-ci.mjs index 131d6f8..f37ad28 100644 --- a/scripts/run-ci.mjs +++ b/scripts/run-ci.mjs @@ -20,6 +20,8 @@ import { readdirSync, readFileSync, rmSync, + statSync, + unlinkSync, writeFileSync, } from 'node:fs'; import path from 'node:path'; @@ -30,6 +32,9 @@ import { formatSuiteList, getSuite } from '../config/suites.config.mjs'; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const PLAYWRIGHT_CLI = path.join(ROOT, 'node_modules/playwright/cli.js'); +/** --keep-archives=N 的命令行值;undefined 时退回环境变量 / 默认全留。 */ +let KEEP_ARCHIVES_ARG; + function parseArgs(argv) { const out = { suite: process.env.SUITE || 'full', @@ -40,6 +45,7 @@ function parseArgs(argv) { skipTypecheck: process.env.SKIP_TYPECHECK === '1', shards: Number(process.env.SHARDS || 1), shard: process.env.PW_SHARD || '', + keepArchives: undefined, }; for (const arg of argv) { if (arg === '--list') out.list = true; @@ -49,13 +55,14 @@ function parseArgs(argv) { else if (arg.startsWith('--suite=')) out.suite = arg.slice(8); else if (arg.startsWith('--shards=')) out.shards = Math.max(1, Number(arg.slice(9)) || 1); else if (arg.startsWith('--shard=')) out.shard = arg.slice(8); + else if (arg.startsWith('--keep-archives=')) out.keepArchives = arg.slice(16); else out.extra.push(arg); } return out; } function printHelp() { - console.log(`Usage: node scripts/run-ci.mjs [--suite=smoke] [--shards=2] [--shard=1/2] [--skip-preflight] [--list] + console.log(`Usage: node scripts/run-ci.mjs [--suite=smoke] [--shards=2] [--shard=1/2] [--keep-archives=N] [--skip-preflight] [--list] Suites: ${formatSuiteList()} @@ -64,6 +71,7 @@ ${formatSuiteList()} 默认按「文件」并行(PW_WORKERS),同一 spec 内 serial 组不拆开。 --shards=N 本机拉起 N 个 Playwright 进程(先登录一次再分片) --shard=i/N 只跑第 i 片(Jenkins 多 Agent 矩阵用) + --keep-archives=N 只保留最近 N 份 reports/ci-*(0=跑完即删本次归档,等价 PW_KEEP_ARCHIVES=0) Env: SUITE / PW_WORKERS / PW_RETRIES / RUN_FULL / BASE_URL / TEST_USER / TEST_PASS @@ -71,7 +79,11 @@ Env: RUN_QUARANTINE=1 只跑 config/quarantine.json 隔离项 SKIP_PREFLIGHT=1 跳过站点探活 SKIP_TYPECHECK=1 跳过 tsc --noEmit - ALLURE=0 关闭 Allure + ALLURE=1 额外生成 Allure(默认关,避免与 HTML 重复保存 trace/截图) + PW_JUNIT_COMPACT=0 保留 JUnit 完整堆栈(默认 1:只留 message) + PW_KEEP_TRACES=N 归档内只留最大 N 个 trace/截图附件(默认全留) + PW_KEEP_ARCHIVES=N 本机只保留最近 N 份 ci-(默认全留;0=跑完即删) + PW_KEEP_INTERMEDIATE=1 保留分片目录与 blob-report(默认合并后删除) INCLUDE_DEBUG_SPECS=1 纳入 *.stat-*.spec.ts 调试用例 `); } @@ -105,6 +117,161 @@ function appendFileIfExists(src, dest) { return true; } +/** 旧归档保留份数:-1=全留(默认,保持原行为),0=跑完即删,N=保留最新 N 份。 */ +function resolveKeepArchives() { + const raw = process.env.PW_KEEP_ARCHIVES ?? KEEP_ARCHIVES_ARG; + if (raw === undefined || raw === '') return -1; + const n = Number(raw); + return Number.isFinite(n) && n >= 0 ? Math.trunc(n) : -1; +} + +function dirSizeBytes(dir) { + let total = 0; + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) total += dirSizeBytes(full); + else if (entry.isFile()) { + try { + total += statSync(full).size; + } catch { + // 文件被占用/已删除则跳过 + } + } + } + return total; +} + +/** 删除本次归档内的中间目录(分片产物、blob),它们只在合并阶段有用。 */ +function dropIntermediateDirs(archiveDir, blobDir) { + if (process.env.PW_KEEP_INTERMEDIATE === '1') return; + rmSync(blobDir, { recursive: true, force: true }); + for (const item of readdirSync(archiveDir, { withFileTypes: true })) { + if (item.isDirectory() && /^shard-\d+$/.test(item.name)) { + rmSync(path.join(archiveDir, item.name), { recursive: true, force: true }); + } + } +} + +function pruneArchives(currentDir, keep) { + if (keep === 0) { + rmSync(currentDir, { recursive: true, force: true }); + console.log(`[ci] PW_KEEP_ARCHIVES=0,已删除本次归档(latest/ 仍保留最近一次)`); + return; + } + const base = path.join(ROOT, 'reports'); + const archives = readdirSync(base, { withFileTypes: true }) + .filter((item) => item.isDirectory() && /^ci-/.test(item.name)) + .map((item) => { + const full = path.join(base, item.name); + return { full, name: item.name, mtime: statSync(full).mtimeMs }; + }) + .sort((a, b) => b.mtime - a.mtime); + for (const item of archives.slice(Math.max(keep, 1))) { + try { + rmSync(item.full, { recursive: true, force: true }); + console.log(`[ci] 清理旧归档 ${item.name}`); + } catch (err) { + console.warn(`[ci] 清理 ${item.name} 失败(可能被占用):${err.message}`); + } + } +} + +/** 附件保留份数:-1=全留(默认),0=全部删除,N=仅留体积最大的 N 个。 */ +function resolveKeepTraces() { + const raw = process.env.PW_KEEP_TRACES; + if (raw === undefined || raw === '') return -1; + const n = Number(raw); + return Number.isFinite(n) && n >= -1 ? Math.trunc(n) : -1; +} + +/** mergeAllureResults 会给文件名加 `<分片>-` 前缀,但 result.json 里仍是原始 uuid,比对时要剥掉前缀。 */ +function attachmentUuid(fileName) { + return fileName.replace(/^shard-\d+-/, ''); +} + +/** + * Allure 与 HTML 各自保存一份 trace.zip / 截图(同一失败用例 2 份)。 + * 保留一份 HTML 报告即可:默认不再把二进制附件复制进 allure-results, + * 并同步删掉 result.json 里的附件条目,避免 Allure 页面出现坏链接。 + */ +function pruneAllureBinaryAttachments(resultsDir, keep) { + if (!existsSync(resultsDir)) return; + const binaries = readdirSync(resultsDir, { withFileTypes: true }) + .filter((item) => item.isFile() && /-attachment\.(zip|png|jpg|jpeg|webm|mp4)$/i.test(item.name)) + .map((item) => { + const full = path.join(resultsDir, item.name); + return { name: item.name, uuid: attachmentUuid(item.name), full, size: statSync(full).size }; + }); + if (!binaries.length) return; + binaries.sort((a, b) => b.size - a.size); + const dropped = new Set(); + let freed = 0; + for (const item of binaries.slice(Math.max(keep, 0))) { + try { + unlinkSync(item.full); + dropped.add(item.uuid); + freed += item.size; + } catch { + // 删除失败则保留引用 + } + } + if (!dropped.size) return; + for (const item of readdirSync(resultsDir, { withFileTypes: true })) { + if (!item.isFile() || !item.name.endsWith('-result.json')) continue; + const full = path.join(resultsDir, item.name); + try { + writeFileSync(full, stripAttachmentRefs(readFileSync(full, 'utf8'), dropped)); + } catch { + // 单个 result 解析失败不影响其余文件 + } + } + console.log( + `[ci] allure 二进制附件瘦身:删除 ${dropped.size} 个,释放 ${(freed / 1024 / 1024).toFixed(0)} MB`, + ); +} + +function stripAttachmentRefs(raw, dropped) { + const isMarked = (ref) => typeof ref === 'string' && dropped.has(ref); + const clean = (node) => { + if (!node || typeof node !== 'object') return; + if (Array.isArray(node)) { + for (let i = node.length - 1; i >= 0; i -= 1) { + if (isMarked(node[i]?.source)) node.splice(i, 1); + else clean(node[i]); + } + return; + } + if (Array.isArray(node.attachments)) { + node.attachments = node.attachments.filter((entry) => !isMarked(entry?.source)); + } + for (const value of Object.values(node)) clean(value); + }; + const json = JSON.parse(raw); + clean(json); + return JSON.stringify(json); +} + +/** 紧凑 JUnit:去掉每条 failure/error 的整段 /堆栈,只留 message 属性。 */ +function compactJunit(file) { + const xml = readFileSync(file, 'utf8'); + const compact = xml.replace(/<(failure|error)\b([^>]*)>[\s\S]*?<\/\1>/g, (match, tag, attrs) => + attrs.includes('message=') ? `<${tag}${attrs} />` : match, + ); + writeFileSync(file, compact); +} + +/** 归档瘦身:产物只供 Jenkins 浏览,可重建的中间文件与重复附件不再全量落盘。 */ +function slimArchive({ archiveDir, htmlDir }) { + if (process.env.PW_JUNIT_COMPACT === '1' && existsSync(path.join(archiveDir, 'junit.xml'))) { + compactJunit(path.join(archiveDir, 'junit.xml')); + } + const resultsDir = path.join(archiveDir, 'allure-results'); + pruneAllureBinaryAttachments(resultsDir, resolveKeepTraces()); + const htmlBytes = existsSync(htmlDir) ? dirSizeBytes(htmlDir) : 0; + if (htmlBytes > 0) console.log(`[ci] HTML 体积 ${(htmlBytes / 1024 / 1024).toFixed(0)} MB`); +} + + function mergeFlakeDirs(parts, dest) { mkdirSync(dest, { recursive: true }); for (const dir of parts) { @@ -263,7 +430,11 @@ function buildBaseEnv(workers, retries, runFull, dirs) { PW_RETRIES: retries, RUN_FULL: runFull, RUN_QUARANTINE: process.env.RUN_QUARANTINE || '0', - ALLURE: process.env.ALLURE === '0' ? '0' : '1', + // Allure 默认关:Allure 与 HTML 保存同一份 trace/截图,且 Allure 插件只读 latest/allure-results, + // 多出的归档副本没有消费方。需要时 ALLURE=1 打开。 + ALLURE: process.env.ALLURE === '1' ? '1' : '0', + // JUnit 默认用紧凑版:Jenkins recordIssues 只读 message,不需要整段堆栈。 + PW_JUNIT_COMPACT: process.env.PW_JUNIT_COMPACT ?? '1', JUNIT: '1', PLAYWRIGHT_BROWSERS_PATH: process.env.PLAYWRIGHT_BROWSERS_PATH || path.join(ROOT, '.playwright-browsers'), @@ -281,6 +452,7 @@ async function main() { console.log(formatSuiteList()); process.exit(0); } + KEEP_ARCHIVES_ARG = opts.keepArchives; const suite = getSuite(opts.suite); const buildId = resolveBuildId(); @@ -452,7 +624,11 @@ async function main() { finishFlakeReport(flakeDir); - const allureOk = process.env.ALLURE !== '0' && tryGenerateAllure(allureResultsDir, allureReportDir); + // 只保留一份 HTML + JUnit + summary 等文本产物;中间目录先删,再生成 Allure,避免附件重复落盘。 + dropIntermediateDirs(archiveDir, blobDir); + slimArchive({ archiveDir, htmlDir }); + + const allureOk = process.env.ALLURE === '1' && tryGenerateAllure(allureResultsDir, allureReportDir); const summary = { suite: suite.name, @@ -482,7 +658,8 @@ async function main() { mkdirSync(latestDir, { recursive: true }); copyIfExists(junitFile, path.join(latestDir, 'junit.xml')); copyIfExists(htmlDir, path.join(latestDir, 'playwright-report')); - copyIfExists(allureResultsDir, path.join(latestDir, 'allure-results')); + // latest 只需要供 Jenkins 插件消费的报告文件;默认不开 Allure,也就不复制 allure-results。 + if (allureOk) copyIfExists(allureResultsDir, path.join(latestDir, 'allure-results')); if (allureOk) copyIfExists(allureReportDir, path.join(latestDir, 'allure-report')); copyIfExists(path.join(archiveDir, 'summary.json'), path.join(latestDir, 'summary.json')); copyIfExists(path.join(archiveDir, 'meta.txt'), path.join(latestDir, 'meta.txt')); @@ -501,6 +678,10 @@ async function main() { console.log(`[ci] JUnit: ${path.relative(ROOT, junitFile)}`); if (allureOk) console.log(`[ci] Allure: ${path.relative(ROOT, allureReportDir)}`); console.log(`[ci] latest: reports/latest/`); + if (existsSync(archiveDir)) { + console.log(`[ci] 归档体积 ${(dirSizeBytes(archiveDir) / 1024 / 1024).toFixed(0)} MB`); + } + pruneArchives(archiveDir, resolveKeepArchives()); process.exit(exitCode); } diff --git a/scripts/scan-admit-batch.mjs b/scripts/scan-admit-batch.mjs new file mode 100644 index 0000000..ba698ec --- /dev/null +++ b/scripts/scan-admit-batch.mjs @@ -0,0 +1,232 @@ +#!/usr/bin/env node +/** + * Batch scan: 基础设置 > 准入管理 (6 pages). + * Saves data/scan/*-elements.json, then regenerates locators/spd/*.locator.yaml from that JSON. + * + * Usage: + * node scripts/scan-admit-batch.mjs + * node scripts/scan-admit-batch.mjs admit-material admit-supply + */ +import { existsSync, mkdirSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import './playwright-browsers-path.mjs'; +import { chromium } from 'playwright'; + +import { runScan, writeYamlFromScanJson } from './scan-interactive-elements.mjs'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); +const AUTH_STATE = path.join(ROOT, 'auth/user.json'); +const BASE = process.env.BASE_URL || 'http://spdtest.cmic.com.cn:8080'; +const OUT_DIR = path.join(ROOT, 'data/scan'); +const LOC_DIR = path.join(ROOT, 'locators/spd'); + +if (existsSync(path.join(ROOT, '.env'))) { + process.loadEnvFile(path.join(ROOT, '.env')); +} + +const MENUS = [ + { pageKey: 'admit-material', menu: '物料准入管理', pageTitle: '基础设置 > 准入管理 > 物料准入管理' }, + { pageKey: 'admit-product-apply', menu: '产品准入申请', pageTitle: '基础设置 > 准入管理 > 产品准入申请' }, + { pageKey: 'admit-material-list', menu: '物料准入单列表', pageTitle: '基础设置 > 准入管理 > 物料准入单列表' }, + { pageKey: 'admit-product-list', menu: '产品准入单列表', pageTitle: '基础设置 > 准入管理 > 产品准入单列表' }, + { pageKey: 'admit-supply', menu: '供货目录申请', pageTitle: '基础设置 > 准入管理 > 供货目录申请' }, + { pageKey: 'admit-task', menu: '准入任务分配', pageTitle: '基础设置 > 准入管理 > 准入任务分配' }, +]; + +async function ensureAuthStorage() { + mkdirSync(path.dirname(AUTH_STATE), { recursive: true }); + const user = process.env.TEST_USER; + const pass = process.env.TEST_PASS; + if (!user || !pass) { + throw new Error('未配置 TEST_USER/TEST_PASS,请复制 .env.example 为 .env 后填写测试账号'); + } + + const browser = await chromium.launch({ headless: true, channel: 'chrome' }); + const context = await browser.newContext({ + viewport: { width: 1920, height: 1080 }, + locale: 'zh-CN', + storageState: existsSync(AUTH_STATE) ? AUTH_STATE : undefined, + }); + const page = await context.newPage(); + await page.goto(`${BASE}/spd/home/index`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.waitForTimeout(2000); + if (!page.url().includes('/login')) { + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Auth state OK:', AUTH_STATE); + return; + } + + await page.goto(`${BASE}/spd/login`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.locator('#username, input[name="username"], input[placeholder*="用户"]').first().fill(user); + await page.locator('#password, input[type="password"]').first().fill(pass); + await page.getByRole('button', { name: /登\s*录/ }).click(); + await page.waitForURL(/\/spd\/home/, { timeout: 120_000 }); + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Logged in and saved auth:', AUTH_STATE); +} + +async function discoverMenuUrls(menus) { + const browser = await chromium.launch({ headless: true, channel: 'chrome' }); + const context = await browser.newContext({ + storageState: AUTH_STATE, + viewport: { width: 1920, height: 1080 }, + locale: 'zh-CN', + }); + const page = await context.newPage(); + const urls = {}; + + for (const item of menus) { + await page.goto(`${BASE}/spd/home/index`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.waitForTimeout(1500); + if (page.url().includes('/login')) { + throw new Error('登录态失效,无法打开准入管理菜单'); + } + await page.getByRole('menuitem', { name: '基础设置' }).click({ timeout: 20_000 }); + await page.waitForTimeout(500); + const admitTitle = page.locator('.ant-menu-submenu-title').filter({ hasText: /^准入管理$/ }).first(); + if (await admitTitle.isVisible().catch(() => false)) { + const expanded = await admitTitle.locator('xpath=..').evaluate((el) => el.classList.contains('ant-menu-submenu-open')).catch(() => false); + if (!expanded) await admitTitle.click({ timeout: 10_000 }); + } + await page.locator('.ant-menu-item').filter({ hasText: new RegExp(`^\\s*${item.menu}\\s*$`) }).first().click({ timeout: 20_000 }); + await page.waitForTimeout(2000); + const url = page.url().split('?')[0]; + if (url.includes('/login') || url.endsWith('/spd/home/index')) { + throw new Error(`未能进入 ${item.menu},当前 URL: ${page.url()}`); + } + urls[item.pageKey] = url; + console.log(`URL ${item.menu}: ${url}`); + } + + await browser.close(); + return urls; +} + +async function openFirstDropdown(page) { + const select = page.locator('.ant-form .ant-select, .ant-form-item .ant-select').first(); + if (!(await select.isVisible().catch(() => false))) return { dropdownOpened: false, skipRescan: true }; + await select.click({ timeout: 10_000 }); + await page.locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden)').first().waitFor({ + state: 'visible', + timeout: 8_000, + }).catch(() => undefined); + await page.waitForTimeout(400); + return { dropdownOpened: true }; +} + +async function layerOpened(page) { + const modal = await page.locator('.ant-modal, .ant-drawer-open').first().isVisible().catch(() => false); + if (modal) return true; + return page.locator('.put-page').first().isVisible().catch(() => false); +} + +async function openNamedButton(page, pattern, flag) { + const btn = page.getByRole('button', { name: pattern }).first(); + if (!(await btn.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + await btn.click({ timeout: 12_000, force: true }); + await page.locator('.ant-modal, .ant-drawer-open, .put-page').first().waitFor({ state: 'visible', timeout: 8_000 }).catch(() => undefined); + await page.waitForTimeout(500); + const opened = await layerOpened(page); + return { [flag]: opened, skipRescan: !opened }; +} + +async function openRowAction(page, pattern, flag) { + const row = page.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)').first(); + if (!(await row.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + let action = row.getByRole('button', { name: pattern }).first(); + if (!(await action.isVisible().catch(() => false))) { + action = row.locator('a,button,span').filter({ hasText: pattern }).first(); + } + if (!(await action.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + const before = page.url(); + const hadDetail = await page.locator('.put-page').first().isVisible().catch(() => false); + await action.click({ timeout: 8_000, force: true }); + await page.waitForTimeout(800); + const moved = page.url() !== before && !page.url().includes('/login'); + if (moved) return { [flag]: true, navigated: true, returnUrl: before }; + const opened = (await layerOpened(page)) && (!hadDetail || await page.locator('.ant-modal, .ant-drawer-open').first().isVisible().catch(() => false) || await page.locator('.put-page').first().isVisible().catch(() => false)); + return { [flag]: Boolean(opened), skipRescan: !opened }; +} + +function pageCfg(targetUrl, item) { + return { + targetUrl, + useIframe: false, + iframeMatch: '', + iframeLabel: 'main', + module: 'spd-basic', + pageKey: item.pageKey, + pageTitle: item.pageTitle, + jsonOut: path.join(OUT_DIR, `${item.pageKey}-elements.json`), + yamlOut: path.join(LOC_DIR, `${item.pageKey}.locator.yaml`), + jsonSourceName: `${item.pageKey}-elements.json`, + waitSelectors: ['.ant-form-item input', '.ant-form', '.ant-table', '.ant-btn'], + waitSelector: '.ant-form, .ant-table, .ant-btn', + waitUntil: 'domcontentloaded', + expandFirstRow: false, + openAdvancedFilter: false, + interactivePhases: [ + openFirstDropdown, + (page) => openNamedButton(page, /新\s*增/, 'dialogOpened'), + (page) => openNamedButton(page, /关联产品/, 'dialogOpened'), + (page) => openRowAction(page, /详\s*情/, 'detailOpened'), + (page) => openRowAction(page, /编\s*辑/, 'editDialogOpened'), + (page) => openRowAction(page, /日\s*志/, 'logOpened'), + ], + }; +} + +async function main() { + const filter = process.argv.slice(2); + const menus = filter.length ? MENUS.filter((p) => filter.includes(p.pageKey)) : MENUS; + if (!menus.length) { + console.error('No pages matched. Available:', MENUS.map((p) => p.pageKey).join(', ')); + process.exit(1); + } + + await ensureAuthStorage(); + const urls = await discoverMenuUrls(menus); + const summary = []; + + for (const item of menus) { + const targetUrl = urls[item.pageKey]; + console.log(`\n=== Scanning ${item.pageKey} ${targetUrl} ===`); + try { + const result = await runScan(pageCfg(targetUrl, item)); + const yaml = writeYamlFromScanJson(result.jsonOut, { + pageKey: item.pageKey, + pageTitle: item.pageTitle, + module: 'spd-basic', + jsonSourceName: `${item.pageKey}-elements.json`, + iframeMatch: '', + yamlOut: path.join(LOC_DIR, `${item.pageKey}.locator.yaml`), + }); + summary.push({ page: item.pageKey, ok: true, ...result, yamlOut: yaml.yamlOut }); + } catch (err) { + console.error(`FAILED ${item.pageKey}:`, err); + summary.push({ page: item.pageKey, ok: false, error: String(err) }); + } + } + + console.log('\n========== SUMMARY =========='); + for (const row of summary) { + if (row.ok) { + console.log(`✓ ${row.page}: ${row.elementCount} elements`); + console.log(` JSON: ${row.jsonOut}`); + console.log(` YAML: ${row.yamlOut}`); + } else { + console.log(`✗ ${row.page}: ${row.error}`); + } + } + if (summary.some((r) => !r.ok)) process.exit(1); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/scan-basic-batch.mjs b/scripts/scan-basic-batch.mjs new file mode 100644 index 0000000..33aaa91 --- /dev/null +++ b/scripts/scan-basic-batch.mjs @@ -0,0 +1,332 @@ +#!/usr/bin/env node +/** + * Batch scan: 基础设置 > 医院资料 (4 pages) from UI-SPEC BASIC-001~004. + * Multi-phase: list + dropdown/dialog/edit/confirm/upload/richtext. + * + * Usage: + * node scripts/scan-basic-batch.mjs + * node scripts/scan-basic-batch.mjs basic-notice basic-branch + */ +import { existsSync, mkdirSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import './playwright-browsers-path.mjs'; +import { chromium } from 'playwright'; + +import { runScan, writeYamlFromScanJson } from './scan-interactive-elements.mjs'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); +const AUTH_STATE = path.join(ROOT, 'auth/user.json'); +const BASE = process.env.BASE_URL || 'http://spdtest.cmic.com.cn:8080'; +const BASIC_BASE = `${BASE}/spd/basic/hospital`; +const OUT_DIR = path.join(ROOT, 'data/scan'); +const LOC_DIR = path.join(ROOT, 'locators/spd'); + +if (existsSync(path.join(ROOT, '.env'))) { + process.loadEnvFile(path.join(ROOT, '.env')); +} + +const COMMON_WAIT = [ + '.ant-form-item input', + '.ant-form', + '.ant-table', + '.ant-btn', + '#form_item_title', + '#form_item_settleMonthDate', +]; + +async function clickButton(content, pattern) { + const btn = content.getByRole('button', { name: pattern }).first(); + if (await btn.isVisible().catch(() => false)) { + await btn.click({ timeout: 15_000 }); + return true; + } + return false; +} + +async function closeOverlays(page) { + const footerCancel = page.locator('.ant-modal .ant-modal-footer button').filter({ hasText: /取\s*消/ }).last(); + if (await footerCancel.isVisible().catch(() => false)) { + await footerCancel.click({ timeout: 8_000 }).catch(() => undefined); + await page.locator('.ant-modal-wrap').first().waitFor({ state: 'hidden', timeout: 8_000 }).catch(() => undefined); + await page.waitForTimeout(400); + } + for (let i = 0; i < 3; i += 1) { + const popCancel = page + .locator('.ant-popconfirm button, .ant-popover-open button, .ant-modal-confirm button') + .filter({ hasText: /取\s*消/ }) + .last(); + if (await popCancel.isVisible().catch(() => false)) { + await popCancel.click({ timeout: 5_000 }).catch(() => undefined); + await page.waitForTimeout(300); + continue; + } + const modalClose = page.locator('.ant-modal-wrap .ant-modal-close').last(); + if (await modalClose.isVisible().catch(() => false)) { + await modalClose.click({ timeout: 5_000 }).catch(() => undefined); + await page.locator('.ant-modal-wrap').first().waitFor({ state: 'hidden', timeout: 8_000 }).catch(() => undefined); + await page.waitForTimeout(400); + continue; + } + break; + } +} + +function firstDataRow(page) { + return page.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)').first(); +} + +async function waitModal(content, titleRe, timeout = 15_000) { + const loc = titleRe + ? content.locator('.ant-modal').filter({ has: content.locator('.ant-modal-title', { hasText: titleRe }) }) + : content.locator('.ant-modal-wrap .ant-modal, .ant-modal').first(); + await loc.first().waitFor({ state: 'visible', timeout }).catch(() => undefined); + return loc.first(); +} + +async function openAddModal(content, titleRe) { + const clicked = await clickButton(content, /新\s*增/); + if (!clicked) return { dialogOpened: false }; + await waitModal(content, titleRe); + return { dialogOpened: true }; +} + +async function openPublishScopeDropdown(page) { + const select = page.locator('.ant-form-item').filter({ hasText: '发布范围' }).locator('.ant-select').first(); + if (!(await select.isVisible().catch(() => false))) return { dropdownOpened: false }; + await select.click({ timeout: 10_000 }); + await page.locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden)').first().waitFor({ + state: 'visible', + timeout: 10_000, + }).catch(() => undefined); + await page.waitForTimeout(400); + return { dropdownOpened: true }; +} + +async function openNoticeAddWithEditor(page) { + await closeOverlays(page); + const clicked = await clickButton(page, /新\s*增/); + if (!clicked) return { dialogOpened: false, richTextOpened: false }; + await waitModal(page, /新增公告/); + await page.locator('.ant-modal .ql-editor, .ant-modal [contenteditable="true"]').first().waitFor({ + state: 'visible', + timeout: 10_000, + }).catch(() => undefined); + await page.waitForTimeout(500); + const rich = await page.locator('.ant-modal .ql-editor').first().isVisible().catch(() => false); + return { dialogOpened: true, richTextOpened: Boolean(rich) }; +} + +async function openNoticeDeleteConfirm(page) { + await closeOverlays(page); + const del = firstDataRow(page).getByRole('button', { name: /删\s*除/ }); + if (!(await del.isVisible().catch(() => false))) { + const fallback = page.getByRole('button', { name: /删\s*除/ }).first(); + if (!(await fallback.isVisible().catch(() => false))) return { deleteConfirmOpened: false }; + await fallback.click({ timeout: 10_000 }); + } else { + await del.click({ timeout: 10_000 }); + } + await page + .locator('.ant-popconfirm, .ant-popover-open, .ant-modal-confirm, .ant-modal') + .first() + .waitFor({ state: 'visible', timeout: 10_000 }) + .catch(() => undefined); + await page.waitForTimeout(400); + return { deleteConfirmOpened: true }; +} + +async function openNoticeEditModal(page) { + await closeOverlays(page); + const edit = firstDataRow(page).getByRole('button', { name: /编\s*辑/ }); + if (await edit.isVisible().catch(() => false)) { + await edit.click({ timeout: 10_000 }); + } else { + const fallback = page.getByRole('button', { name: /编\s*辑/ }).first(); + if (!(await fallback.isVisible().catch(() => false))) return { editDialogOpened: false }; + await fallback.click({ timeout: 10_000 }); + } + await waitModal(page, /编辑公告|新增公告|公告/); + await page.locator('.ant-modal .ql-editor, .ant-modal [contenteditable="true"]').first().waitFor({ + state: 'visible', + timeout: 8_000, + }).catch(() => undefined); + await page.waitForTimeout(500); + return { editDialogOpened: true }; +} + +async function openHospitalEdit(page) { + const clicked = await clickButton(page, /编\s*辑/); + if (!clicked) return { editModeOpened: false }; + await page.getByRole('button', { name: /保\s*存/ }).first().waitFor({ state: 'visible', timeout: 15_000 }).catch(() => undefined); + await page.waitForTimeout(400); + return { editModeOpened: true }; +} + +async function openFinanceMonthEdit(page) { + const clicked = await clickButton(page, /修\s*改/); + if (!clicked) return { editModeOpened: false }; + await page + .locator('.ant-table-tbody .ant-picker, .ant-table-tbody input, .ant-table-tbody button') + .first() + .waitFor({ state: 'visible', timeout: 10_000 }) + .catch(() => undefined); + await page.waitForTimeout(600); + return { editModeOpened: true }; +} + +async function openBranchEditModal(page) { + await closeOverlays(page); + const edit = firstDataRow(page).getByRole('button', { name: /编\s*辑/ }); + if (await edit.isVisible().catch(() => false)) { + await edit.click({ timeout: 10_000 }); + } else { + const fallback = page.getByRole('button', { name: /编\s*辑/ }).first(); + if (!(await fallback.isVisible().catch(() => false))) return { editDialogOpened: false }; + await fallback.click({ timeout: 10_000 }); + } + await waitModal(page, /编辑院区/); + await page.waitForTimeout(500); + return { editDialogOpened: true }; +} + +function pageCfg(reportPath, rest) { + return { + targetUrl: `${BASIC_BASE}${reportPath}`, + useIframe: false, + iframeMatch: '', + iframeLabel: 'main', + module: 'spd-basic', + jsonOut: path.join(OUT_DIR, `${rest.pageKey}-elements.json`), + yamlOut: path.join(LOC_DIR, `${rest.pageKey}.locator.yaml`), + jsonSourceName: `${rest.pageKey}-elements.json`, + waitSelectors: COMMON_WAIT, + waitSelector: '.ant-form-item input, .ant-table, .ant-btn, .ant-form', + expandFirstRow: false, + openAdvancedFilter: false, + skipQueryClick: rest.skipQueryClick ?? false, + ...rest, + }; +} + +const PAGES = [ + pageCfg('/paltform/notice', { + pageKey: 'basic-notice', + pageTitle: '基础设置 > 医院资料 > 公告管理', + interactivePhases: [ + openPublishScopeDropdown, + openNoticeAddWithEditor, + openNoticeDeleteConfirm, + openNoticeEditModal, + ], + }), + pageCfg('/paltform/info', { + pageKey: 'basic-hospital-info', + pageTitle: '基础设置 > 医院资料 > 医院信息登记', + skipQueryClick: true, + interactivePhases: [openHospitalEdit], + }), + pageCfg('/balance/month', { + pageKey: 'basic-finance-month', + pageTitle: '基础设置 > 医院资料 > 财务月份设置', + interactivePhases: [openFinanceMonthEdit], + }), + pageCfg('/paltform/area', { + pageKey: 'basic-branch', + pageTitle: '基础设置 > 医院资料 > 院区管理', + skipQueryClick: true, + interactivePhases: [ + async (page) => { + await closeOverlays(page); + return openAddModal(page, /新增院区/); + }, + openBranchEditModal, + ], + }), +]; + +async function ensureAuthStorage() { + mkdirSync(path.dirname(AUTH_STATE), { recursive: true }); + const user = process.env.TEST_USER; + const pass = process.env.TEST_PASS; + if (!user || !pass) { + throw new Error('未配置 TEST_USER/TEST_PASS,请复制 .env.example 为 .env 后填写测试账号'); + } + + const browser = await chromium.launch({ headless: true, channel: 'chrome' }); + const context = await browser.newContext({ + viewport: { width: 1920, height: 1080 }, + locale: 'zh-CN', + storageState: existsSync(AUTH_STATE) ? AUTH_STATE : undefined, + }); + const page = await context.newPage(); + await page.goto(`${BASE}/spd/home/index`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.waitForTimeout(2000); + if (!page.url().includes('/login')) { + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Auth state OK:', AUTH_STATE); + return; + } + + await page.goto(`${BASE}/spd/login`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.locator('#username, input[name="username"], input[placeholder*="用户"]').first().fill(user); + await page.locator('#password, input[type="password"]').first().fill(pass); + await page.getByRole('button', { name: /登\s*录/ }).click(); + await page.waitForURL(/\/spd\/home/, { timeout: 120_000 }); + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Logged in and saved auth:', AUTH_STATE); +} + +async function main() { + const filter = process.argv.slice(2); + const pages = filter.length ? PAGES.filter((p) => filter.includes(p.pageKey)) : PAGES; + + if (!pages.length) { + console.error('No pages matched. Available:', PAGES.map((p) => p.pageKey).join(', ')); + process.exit(1); + } + + await ensureAuthStorage(); + + const summary = []; + for (const page of pages) { + console.log(`\n=== Scanning ${page.pageKey} ===`); + try { + const result = await runScan(page); + const yaml = writeYamlFromScanJson(result.jsonOut, { + pageKey: page.pageKey, + pageTitle: page.pageTitle, + module: 'spd-basic', + jsonSourceName: `${page.pageKey}-elements.json`, + iframeMatch: '', + yamlOut: path.join(LOC_DIR, `${page.pageKey}.locator.yaml`), + }); + summary.push({ page: page.pageKey, ok: true, ...result, yamlOut: yaml.yamlOut }); + } catch (err) { + console.error(`FAILED ${page.pageKey}:`, err); + summary.push({ page: page.pageKey, ok: false, error: String(err) }); + } + } + + console.log('\n========== SUMMARY =========='); + for (const row of summary) { + if (row.ok) { + console.log(`✓ ${row.page}: ${row.elementCount} elements`); + console.log(` JSON: ${row.jsonOut}`); + console.log(` YAML: ${row.yamlOut}`); + } else { + console.log(`✗ ${row.page}: ${row.error}`); + } + } + + if (summary.some((r) => !r.ok)) process.exit(1); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/scan-interactive-elements.mjs b/scripts/scan-interactive-elements.mjs index 5d45a1c..df3ef85 100644 --- a/scripts/scan-interactive-elements.mjs +++ b/scripts/scan-interactive-elements.mjs @@ -52,7 +52,9 @@ const SCAN_SCRIPT = `(() => { '.ant-pagination-options-size-changer,.ant-pagination-options-quick-jumper input,' + '.ant-table-column-sorter,.ant-dropdown-trigger,.ant-tabs-tab,.ant-drawer-close,' + '.ant-modal-close,.advance-filter,.ant-cascader,.ant-drawer-footer .ant-btn,' + - '.ant-table-row-expand-icon,.ant-table-row-expand-icon-cell'; + '.ant-table-row-expand-icon,.ant-table-row-expand-icon-cell,' + + // antd Tree:节点无 tabindex/role,checkbox 不是原生 input,需显式纳入 + '.ant-tree-checkbox,.ant-tree-switcher,.ant-tree-title'; function isVisible(el) { const style = window.getComputedStyle(el); @@ -68,6 +70,9 @@ const SCAN_SCRIPT = `(() => { } function labelFor(el) { + // 树节点(权限树 / 分类树)的语义来自自身文本,不能回溯到外层表单标签, + // 否则所有节点都会被打上同一个 label(如「角色名称」)而无法区分。 + if (el.closest('.ant-tree')) return ''; if (el.id) { const lbl = document.querySelector('label[for="' + CSS.escape(el.id) + '"]'); if (lbl) return norm(lbl.textContent); @@ -91,6 +96,9 @@ const SCAN_SCRIPT = `(() => { if (el.classList.contains('ant-picker')) return 'datepicker'; if (el.classList.contains('ant-btn')) return 'button'; if (el.classList.contains('ant-checkbox-wrapper')) return 'checkbox'; + if (el.classList.contains('ant-tree-checkbox')) return 'checkbox'; + if (el.classList.contains('ant-tree-switcher')) return 'tree_switcher'; + if (el.classList.contains('ant-tree-title')) return 'tree_title'; if (el.classList.contains('ant-radio-wrapper')) return 'radio'; if (el.classList.contains('ant-tabs-tab')) return 'tab'; if (el.classList.contains('advance-filter')) return 'filter_trigger'; @@ -122,6 +130,35 @@ const SCAN_SCRIPT = `(() => { return s.replace(/([!"#$%&'()*+,./:;<=>?@[\\\\]^ \`{|}~])/g, '\\\\$1'); } + // 树节点祖先路径:用于生成可读且唯一的树定位器键(如 基础设置 > 医院资料 > 院区管理)。 + // 注意:antd Tree 的 DOM 是扁平的(靠 level 缩进表示层级),不能靠 parentElement 回溯。 + function treePathOf(el) { + const tree = el.closest('.ant-tree'); + if (!tree) return null; + const titleNode = el.closest('.ant-tree-treenode') || (el.classList.contains('ant-tree-title') ? el.parentElement?.closest('.ant-tree-treenode') : null); + if (!titleNode) return null; + const all = [...tree.querySelectorAll('.ant-tree-treenode')]; + const idx = all.indexOf(titleNode); + if (idx < 0) return null; + const levelOf = (n) => Number((n.className.match(/ant-tree-treenode-level-(\\d+)/) || [])[1] ?? 0); + const targetLevel = levelOf(titleNode); + const path = []; + const self = titleNode.querySelector('.ant-tree-title'); + const selfText = self ? norm(self.textContent) : ''; + if (!selfText) return null; + path.unshift(selfText); + let need = targetLevel - 1; + for (let i = idx - 1; i >= 0 && need >= 0; i -= 1) { + if (levelOf(all[i]) === need) { + const t = all[i].querySelector('.ant-tree-title'); + const txt = t ? norm(t.textContent) : ''; + if (txt) path.unshift(txt); + need -= 1; + } + } + return path; + } + function buildLocators(el, label, text, type) { const locators = []; const testId = el.getAttribute('data-testid') || el.getAttribute('data-test'); @@ -164,18 +201,64 @@ const SCAN_SCRIPT = `(() => { if (name && name.length <= 40) { locators.push({ strategy: 'text', value: name, priority: 8 }); } + // 树节点:优先用树容器 + 节点文本精确定位,避免与页面同名菜单/行混淆 + if (el.closest('.ant-tree') && text) { + const treeCls = el.closest('.ant-tree')?.className || ''; + const treeSel = String(treeCls).includes('menu-role-tree') ? '.menu-role-tree' : '.ant-tree'; + if (el.classList.contains('ant-tree-title')) { + locators.unshift({ + strategy: 'tree_node', + value: text, + tree: treeSel, + priority: 1, + }); + } else if (el.classList.contains('ant-tree-checkbox')) { + const nodeText = norm(el.closest('.ant-tree-treenode')?.querySelector('.ant-tree-title')?.textContent || ''); + if (nodeText) { + locators.unshift({ + strategy: 'tree_checkbox', + value: nodeText, + tree: treeSel, + priority: 1, + }); + } + } + } locators.sort((a, b) => a.priority - b.priority); return locators; } function sectionOf(el) { - if (el.closest('.ant-pagination')) return 'pagination'; - const drawer = el.closest('.ant-drawer-open, .ant-drawer'); + const detail = el.closest('.put-page'); + if (detail) { + const title = detail.querySelector('.ant-descriptions-title, .ant-page-header-heading-title'); + const t = title ? norm(title.textContent) : ''; + return t ? 'detail:' + t : 'detail'; + } + const modal = el.closest('.ant-modal'); + if (modal) { + const wrap = modal.closest('.ant-modal-wrap'); + const hidden = wrap && (wrap.style.display === 'none' || wrap.classList.contains('ant-modal-wrap-hidden')); + if (!hidden) { + const title = modal.querySelector('.ant-modal-title'); + const t = title ? norm(title.textContent) : ''; + return t ? 'modal:' + t : 'modal'; + } + } + const pop = el.closest('.ant-popconfirm, .ant-modal-confirm'); + if (pop) return 'confirm'; + const dd = el.closest('.ant-select-dropdown, .ant-picker-dropdown, .ant-dropdown'); + if (dd && !dd.classList.contains('ant-select-dropdown-hidden') && !dd.classList.contains('ant-picker-dropdown-hidden') && !dd.classList.contains('ant-dropdown-hidden')) { + return 'dropdown'; + } + const drawer = el.closest('.ant-drawer'); if (drawer && drawer.classList.contains('ant-drawer-open')) { - if (el.closest('.ant-drawer-footer')) return 'panel:高级筛选:footer'; - const title = drawer.querySelector('.ant-drawer-title, .ant-modal-title'); - if (title) return 'panel:' + norm(title.textContent); + if (el.closest('.ant-drawer-footer')) return 'panel:drawer:footer'; + const title = drawer.querySelector('.ant-drawer-title'); + if (title && norm(title.textContent)) return 'panel:' + norm(title.textContent); + return 'panel:drawer'; } + if (el.closest('.ant-pagination')) return 'pagination'; const tab = el.closest('.ant-tabs-tabpane-active'); if (tab) { const tabBtn = document.querySelector('.ant-tabs-tab-active'); @@ -231,6 +314,7 @@ const SCAN_SCRIPT = `(() => { }, locators, recommended: best, + treePath: treePathOf(el), }); }); @@ -291,6 +375,16 @@ function toPlaywrightExpr(loc, frameLabel) { return `${prefix}.getByText('${loc.value.replace(/'/g, "\\'")}', { exact: true })`; case 'text': return `${prefix}.getByText('${loc.value.replace(/'/g, "\\'")}')`; + case 'tree_node': { + const tree = loc.tree || '.ant-tree'; + const t = loc.value.replace(/'/g, "\\'"); + return `${prefix}.locator('${tree} .ant-tree-title').filter({ hasText: /^\\s*${t}\\s*$/ })`; + } + case 'tree_checkbox': { + const tree = loc.tree || '.ant-tree'; + const t = loc.value.replace(/'/g, "\\'"); + return `${prefix}.locator('${tree} .ant-tree-treenode').filter({ hasText: /^\\s*${t}\\s*$/ }).locator('.ant-tree-checkbox')`; + } case 'css': return `${prefix}.locator('${loc.value.replace(/'/g, "\\'")}')`; default: @@ -328,6 +422,49 @@ const FORM_ID_KEY_MAP = { form_item_billDate: 'documentDateStart', form_item_statPeriod: 'statPeriodStart', form_item_goodsKind: 'consumableType', + // 手术套包设置(基础包维护 / 术种维护 / 标准套包维护) + table_search_form_keyword: 'productName', + table_search_form_goodsSpec: 'specModel', + table_search_form_goodsMfrsName: 'manufacturer', + table_search_form_operationName: 'operationName', + table_search_form_disease: 'diseaseName', + form_item_pkgName: 'basePkgName', + form_item_pkgCode: 'basePkgCode', + form_item_kindId: 'basePkgCategory', + form_item_kindName: 'basePkgCategoryName', + form_item_remark: 'remark', + basic_kindName: 'operTypeCategoryName', + // 医院资料 / 厂商 / 准入 / 用户 + form_item_title: 'noticeTitle', + form_item_expireDate: 'expireDate', + form_item_provId: 'supplierId', + form_item_subProvId: 'subSupplierId', + table_search_form_provName: 'supplierName', + table_search_form_provId: 'supplierId', + table_search_form_dateValue: 'dateRange', + table_search_form_orderNo: 'documentNo', + table_search_form_purModes: 'purchaseMode', + form_item_goodsMfrsId: 'manufacturer', + form_item_goodsSpec: 'specModel', + form_item_twoHourFlag: 'twoHourArrival', + form_item_daysOfStockout: 'stockoutDays', +}; + +// 仅有 placeholder、无 label/id 的输入框(树搜索、弹窗内搜索等) +const PLACEHOLDER_KEY_MAP = { + 请输入分类名称: 'categorySearchInput', + 请输入术种分类名称: 'operTypeCategorySearchInput', + '请输入术种分类名称、术种名称、标准套包名称': 'stdPkgTreeSearchInput', + '请输入分类/基础包名称': 'addBasePkgSearchInput', + 厂商名称: 'mfrsNameInput', + 公告标题: 'noticeTitleInput', + 院区名称: 'campusNameInput', + 请输入院区名称: 'campusNameInput', + 请输入方案编码: 'schemeCodeInput', + 请输入指标编码: 'indicatorCodeInput', + 请输入指标名称: 'indicatorNameInput', + '请输入名称/年度': 'nameOrYearInput', + '请输入产品编码、名称': 'productCodeNameInput', }; function slugify(s) { @@ -402,12 +539,152 @@ function slugify(s) { 科室名称: 'dimensionDeptName', 产品编号名称: 'productName', '产品编号/名称': 'productName', + // 手术套包设置 + 基础包维护: 'basePkgMaintenance', + 术种维护: 'operTypeMaintenance', + 标准套包维护: 'stdPkgMaintenance', + 基础包名称: 'basePkgName', + 包编码: 'basePkgCode', + 所属分类: 'basePkgCategory', + 分类名称: 'categoryName', + 术种分类名称: 'operTypeCategoryName', + 术种分类: 'operTypeCategory', + 术种名称: 'operationName', + 病种名称: 'diseaseName', + 诊断编码: 'diagnosisCode', + 诊断名称: 'diagnosisName', + 操作编码: 'operationCode', + 操作名称: 'operationName', + 新增分类: 'addCategory', + 新增基础包: 'addBasePkg', + 新增术种: 'addOperType', + 添加基础包: 'addBasePkg', + 添加散件: 'addLoosePart', + 添加产品: 'addProduct', + 新增: 'add', + '新 增': 'add', + 编辑: 'edit', + 移除: 'remove', + 停用: 'disable', + 启用: 'enable', + 保存: 'save', + '保 存': 'save', + 取消: 'cancel', + '取 消': 'cancel', + 确认: 'confirm', + '确 认': 'confirm', + 确定: 'confirm', + '确定(0)': 'confirm', + 关闭: 'close', + '关 闭': 'close', + 返回: 'back', + '返 回': 'back', + 展开: 'expandFilter', + 收起: 'collapseFilter', + 产品概览: 'productOverview', + 停用Tab: 'tabDisabled', + 未建分类: 'tabUncategorized', + 详情: 'detail', + 日志: 'log', + 提交: 'submit', + '提 交': 'submit', + 操作: 'action', + 状态: 'status', + 备注: 'remark', + 内容: 'content', + 年度: 'year', + 变更类型: 'changeType', + 截止日期: 'expireDate', + 新增日期: 'createDate', + 公告标题: 'noticeTitle', + 发布日期: 'publishDate', + 发布范围: 'publishScope', + 厂商名称: 'mfrsName', + 器械注册人: 'registrant', + 经销商: 'distributor', + 方案编码: 'schemeCode', + 指标编号: 'indicatorCode', + 二级供应商: 'subSupplier', + 是否2小时到货: 'twoHourArrival', + 授权链路效期预警: 'authExpiryWarn', + 一致: 'matched', + 不一致: 'mismatched', + // 基础设置子菜单 + 医院资料: 'hospitalInfo', + 院区管理: 'branchManage', + 公告管理: 'noticeManage', + 财务月份设置: 'financeMonthSetting', + 医院信息登记: 'hospitalInfoRegister', + 厂商管理: 'vendorManage', + 厂商查询: 'vendorQuery', + 供应商资质管理: 'vendorQual', + 授权变更初审: 'authFirst', + 授权变更终审: 'authFinal', + 注册证变更初审: 'certFirst', + 注册证变更终审: 'certFinal', + 注册证差异对比: 'certDiff', + 评价方案维护: 'evalScheme', + 评价指标维护: 'evalIndicator', + 供应商节假日管理: 'vendorHoliday', + 准入管理: 'admitManage', + 物料准入管理: 'admitMaterial', + 产品准入申请: 'admitProductApply', + 物料准入单列表: 'admitMaterialList', + 产品准入单列表: 'admitProductList', + 供货目录申请: 'admitSupply', + 准入任务分配: 'admitTask', + 手术套包设置: 'surgerySetting', + 用户管理: 'userManage', + 医共体用户管理: 'unionUserManage', + 角色管理: 'roleManage', + // 顶部主导航 + 首页: 'home', + 基础设置: 'basicSetting', + 采购管理: 'purchase', + 期初管理: 'opening', + 中心库管理: 'centerStock', + 科室业务管理: 'deptBusiness', + 财务管理: 'finance', + 骨科带量: 'orthoVolume', + 综合管理: 'general', + 阳采管理: 'yangcai', + 流程中心: 'processCenter', + 后勤物资管理: 'logistics', + 统计报表: 'statReport', + 数据可视化: 'dataViz', }; if (map[s]) return map[s]; if (map[normalized]) return map[normalized]; - const ascii = s.match(/[a-zA-Z0-9_]+/g); - if (ascii?.length) return ascii.join('_').toLowerCase(); - return `el_${Buffer.from(s).toString('hex').slice(0, 8)}`; + // 含汉字时不要抽中间的数字当键(否则「是否2小时到货」会塌成 Select/Input) + if (!/[\u4e00-\u9fff]/.test(normalized)) { + const ascii = s.match(/[a-zA-Z0-9_]+/g); + if (ascii?.length) return ascii.join('_').toLowerCase(); + } + // 中文兜底:用完整 hex 而非前 8 位,避免「基础设置/医院资料…」等前缀相同导致键塌缩 + return `el_${Buffer.from(normalized).toString('hex')}`; +} + +function isNoiseElement(el) { + const rec = el.recommended || {}; + const recVal = String(rec.value || el.label || '').trim(); + const recText = String(el.text || '').trim(); + // 图标类 aria-label(more / remove / Close 等):不稳定且无业务语义 + if (rec.strategy === 'label' && /^(more|remove|close|delete|edit|info-circle|question-circle|down|up|left|right|increase value|decrease value)$/i.test(String(rec.value || ''))) { + return true; + } + // 纯图标按钮:无文案、无 placeholder、无 id + const meaninglessText = !el.text || /^(more|remove|close|delete)$/i.test(recText); + if (rec.strategy === 'label' && meaninglessText) return true; + if (/^(Increase Value|Decrease Value)$/i.test(recText)) return true; + // 表头排序器只保留一个(已在循环内处理);此处过滤无文案的排序 span + if (el.className?.includes('ant-table-column-sorter') && !el.text) return true; + // 顶部用户栏信息块(院区切换 / 消息数 / 用户名)- 非页面业务元素 + if (/^中心库[((]/.test(recVal) && (rec.strategy === 'label_text' || el.className?.includes('ant-dropdown-trigger'))) { + return true; + } + if (rec.strategy === 'label_text' && /^(中心库|99\+|\d+)$/.test(recVal)) return true; + if (/^(99\+)$/.test(recText) && el.className?.includes('ant-dropdown-trigger')) return true; + return false; } function buildYaml(scan) { @@ -418,9 +695,14 @@ function buildYaml(scan) { lines.push(`# Page: ${CONFIG.pageTitle}`); if (scan.advancedFilterOpened) lines.push('# Note: advanced filter panel was opened during scan'); if (scan.rowExpanded) lines.push('# Note: first table row was expanded during scan'); + if (scan.dropdownOpened) lines.push('# Note: filter dropdown was opened during scan'); + if (scan.dialogOpened) lines.push('# Note: dialog/modal was opened during scan'); + if (scan.editDialogOpened) lines.push('# Note: edit dialog was opened during scan'); + if (scan.detailOpened) lines.push('# Note: detail view was opened during scan'); + if (scan.logOpened) lines.push('# Note: log view was opened during scan'); lines.push(''); lines.push(`page: ${CONFIG.pageKey}`); - lines.push('module: spd-stat'); + lines.push(`module: ${CONFIG.module || 'spd-stat'}`); lines.push(`url: ${scan.url}`); lines.push(''); lines.push('locators:'); @@ -444,9 +726,17 @@ function buildYaml(scan) { el.recommended = { strategy: 'css', value: '.ant-table-column-sorter', priority: 9 }; } if (!el.recommended) continue; + // 过滤无业务价值的噪声元素(图标 aria-label、行内重复操作按钮等) + if (isNoiseElement(el)) continue; const frameLabel = frameLabelFromUrl(el.frame); - let key = el.id && FORM_ID_KEY_MAP[el.id] ? FORM_ID_KEY_MAP[el.id] : null; + // 树节点:用祖先路径生成可读且唯一的键,避免所有节点塌缩成同一个 hex 前缀 + const isTree = el.type === 'tree_title' || el.type === 'tree_switcher' || (el.type === 'checkbox' && el.treePath); + let key = isTree && el.treePath ? 'tree_' + el.treePath.map(slugify).join('_') : null; + if (!key && el.id && FORM_ID_KEY_MAP[el.id]) key = FORM_ID_KEY_MAP[el.id]; + if (!key && el.placeholder && !el.label && !el.id) { + key = PLACEHOLDER_KEY_MAP[el.placeholder] || null; + } const baseName = el.label || el.text || @@ -491,7 +781,24 @@ function buildYaml(scan) { if (section.includes('panel:高级筛选')) key = `drawer_${key}`; if (section.includes('pagination')) key = `pagination_${key}`; - let finalKey = key.replace(/^[^a-zA-Z_]/, '').replace(/[^a-zA-Z0-9_]/g, '_') || `el_${el.index}`; + let finalKey; + if (isTree && el.treePath?.length) { + // 树节点:键必须包含完整祖先路径,否则不同模块下的同名菜单(如「库存查询」)会撞键。 + // 叶子若是英文菜单则用可读键,中文叶子用短 hex 后缀保证唯一且稳定。 + const leaf = slugify(el.treePath[el.treePath.length - 1]); + const leafReadable = /^[a-z][a-z0-9_]*$/i.test(leaf) && !leaf.startsWith('el_'); + const kind = el.type === 'checkbox' ? 'cb' : el.type === 'tree_switcher' ? 'sw' : 'node'; + const pathHash = Buffer.from(el.treePath.join('>')).toString('hex').slice(0, 12); + finalKey = leafReadable ? `tree_${kind}_${leaf}_${pathHash}` : `tree_${kind}_${pathHash}`; + } else { + finalKey = key.replace(/^[^a-zA-Z_]/, '').replace(/[^a-zA-Z0-9_]/g, '_') || `el_${el.index}`; + } + // 表格行内重复的行操作按钮(编辑/移除/详情等)只保留一个,避免 editBtn_2/3... + const isRowAction = + (el.type === 'button' || el.tag === 'button' || el.className?.includes('ant-btn')) && + /Btn$/.test(finalKey) && + (el.section || '').includes('main'); + if (isRowAction && usedKeys.has(finalKey)) continue; let n = 2; while (usedKeys.has(finalKey)) { finalKey = `${key}_${n++}`; @@ -513,6 +820,10 @@ function buildYaml(scan) { if (rec.role) lines.push(` role: ${rec.role}`); if (rec.name) lines.push(` name: ${JSON.stringify(rec.name)}`); if (rec.value) lines.push(` value: ${JSON.stringify(rec.value)}`); + if (rec.tree) lines.push(` tree: ${JSON.stringify(rec.tree)}`); + } + if (el.treePath?.length > 1) { + lines.push(` tree_path: ${JSON.stringify(el.treePath.join(' > '))}`); } if (pw) lines.push(` playwright: ${JSON.stringify(pw)}`); lines.push(''); @@ -659,10 +970,65 @@ async function clickQueryIfPresent(reportFrame) { return false; } +function fingerprint(el) { + return [el.section, el.type, el.id || '', el.role || '', el.label || '', el.text || '', el.placeholder || ''].join('|'); +} + +async function closeScanOverlays(page) { + for (let i = 0; i < 6; i += 1) { + const back = page + .locator('.put-page, .ant-drawer-open, .ant-modal-wrap, .ant-modal') + .getByRole('button', { name: /返\s*回|取\s*消|关\s*闭/ }) + .first(); + if (await back.isVisible().catch(() => false)) { + await back.click({ timeout: 4_000, force: true }).catch(() => undefined); + await page.waitForTimeout(400); + continue; + } + const backText = page.locator('a,button,span').filter({ hasText: /^返\s*回$/ }).last(); + if (await backText.isVisible().catch(() => false)) { + await backText.click({ timeout: 4_000, force: true }).catch(() => undefined); + await page.waitForTimeout(400); + continue; + } + const cancel = page + .locator('.ant-modal-footer button, .ant-modal-confirm button, .ant-popconfirm button, .ant-drawer-footer button') + .filter({ hasText: /取\s*消|关\s*闭/ }) + .last(); + if (await cancel.isVisible().catch(() => false)) { + await cancel.click({ timeout: 4_000, force: true }).catch(() => undefined); + await page.waitForTimeout(300); + continue; + } + const closer = page.locator('.ant-modal-wrap .ant-modal-close, .ant-drawer-close').last(); + if (await closer.isVisible().catch(() => false)) { + await closer.click({ timeout: 4_000, force: true }).catch(() => undefined); + await page.waitForTimeout(300); + continue; + } + const closedByScript = await page.evaluate(() => { + const layer = document.querySelector('.put-page'); + if (!layer) return false; + const btn = [...layer.querySelectorAll('button,a,span')].find((el) => /返\s*回|关\s*闭/.test((el.textContent || '').trim())); + if (!btn) return false; + btn.click(); + return true; + }).catch(() => false); + if (closedByScript) { + await page.waitForTimeout(400); + continue; + } + await page.keyboard.press('Escape').catch(() => undefined); + break; + } +} + async function prepareReportPage(page) { - const reportFrame = page.frameLocator(`iframe[src*="${CONFIG.iframeMatch}"]`); + const reportFrame = CONFIG.useIframe === false + ? page + : page.frameLocator(`iframe[src*="${CONFIG.iframeMatch}"]`); const readySelector = await waitReportReady(reportFrame); - console.log(` iframe ready (${readySelector})`); + console.log(` content ready (${readySelector})`); const meta = { queryExecuted: false, advancedFilterOpened: false, rowExpanded: false }; if (CONFIG.skipQueryClick !== true) { @@ -723,7 +1089,10 @@ export async function runScan(overrides = {}) { const page = await context.newPage(); console.log('Navigating to', CONFIG.targetUrl); - await page.goto(CONFIG.targetUrl, { waitUntil: 'networkidle', timeout: 120_000 }); + await page.goto(CONFIG.targetUrl, { + waitUntil: CONFIG.waitUntil || 'networkidle', + timeout: 120_000, + }); await page.waitForTimeout(5000); const prepMeta = await prepareReportPage(page); @@ -731,6 +1100,43 @@ export async function runScan(overrides = {}) { let scan = await scanPageOrFrames(page); Object.assign(scan, prepMeta); + const seen = new Set(scan.elements.map(fingerprint)); + const phases = Array.isArray(CONFIG.interactivePhases) ? CONFIG.interactivePhases : []; + for (const phase of phases) { + let meta = {}; + try { + meta = (await phase(page)) || {}; + } catch (err) { + console.warn(' phase failed:', err); + meta = {}; + } + Object.assign(scan, meta); + if (!meta.skipRescan) { + const extra = await scanPageOrFrames(page); + for (const el of extra.elements) { + const key = fingerprint(el); + if (seen.has(key)) continue; + seen.add(key); + scan.elements.push(el); + } + scan.frames.push(...extra.frames); + } + if (meta.navigated && meta.returnUrl) { + await page.goto(meta.returnUrl, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.waitForTimeout(1500); + } else { + await closeScanOverlays(page); + } + } + scan.elements.forEach((el, i) => { + el.index = i + 1; + }); + scan.elementCount = scan.elements.length; + + delete scan.skipRescan; + delete scan.navigated; + delete scan.returnUrl; + scan.url = sanitizeUrl(scan.url); for (const el of scan.elements) { if (el.frame) el.frame = frameLabelFromUrl(el.frame); diff --git a/scripts/scan-surgery-batch.mjs b/scripts/scan-surgery-batch.mjs new file mode 100644 index 0000000..78a3a14 --- /dev/null +++ b/scripts/scan-surgery-batch.mjs @@ -0,0 +1,296 @@ +#!/usr/bin/env node +/** + * Batch scan: 基础设置 > 手术套包设置 (3 pages) from UI-SPEC SURG-001~003. + * Saves data/scan/*-elements.json, then regenerates locators/spd/*.locator.yaml. + * + * Usage: + * node scripts/scan-surgery-batch.mjs + * node scripts/scan-surgery-batch.mjs surgery-base-pkg surgery-oper-type + */ +import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import './playwright-browsers-path.mjs'; +import { chromium } from 'playwright'; + +import { runScan, writeYamlFromScanJson } from './scan-interactive-elements.mjs'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); +const AUTH_STATE = path.join(ROOT, 'auth/user.json'); +const BASE = process.env.BASE_URL || 'http://spdtest.cmic.com.cn:8080'; +const OUT_DIR = path.join(ROOT, 'data/scan'); +const LOC_DIR = path.join(ROOT, 'locators/spd'); +const URL_MAP_OUT = path.join(OUT_DIR, 'surgery-menu-urls.json'); + +if (existsSync(path.join(ROOT, '.env'))) { + process.loadEnvFile(path.join(ROOT, '.env')); +} + +const MENUS = [ + { pageKey: 'surgery-base-pkg', menu: '基础包维护', pageTitle: '基础设置 > 手术套包设置 > 基础包维护', specId: 'SURG-001' }, + { pageKey: 'surgery-oper-type', menu: '术种维护', pageTitle: '基础设置 > 手术套包设置 > 术种维护', specId: 'SURG-002' }, + { pageKey: 'surgery-std-pkg', menu: '标准套包维护', pageTitle: '基础设置 > 手术套包设置 > 标准套包维护', specId: 'SURG-003' }, +]; + +async function ensureAuthStorage() { + mkdirSync(path.dirname(AUTH_STATE), { recursive: true }); + const user = process.env.TEST_USER; + const pass = process.env.TEST_PASS; + if (!user || !pass) { + throw new Error('未配置 TEST_USER/TEST_PASS,请复制 .env.example 为 .env 后填写测试账号'); + } + + const browser = await chromium.launch({ headless: true, channel: 'chrome' }); + const context = await browser.newContext({ + viewport: { width: 1920, height: 1080 }, + locale: 'zh-CN', + storageState: existsSync(AUTH_STATE) ? AUTH_STATE : undefined, + }); + const page = await context.newPage(); + await page.goto(`${BASE}/spd/home/index`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.waitForTimeout(2000); + if (!page.url().includes('/login')) { + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Auth state OK:', AUTH_STATE); + return; + } + + await page.goto(`${BASE}/spd/login`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.locator('#username, input[name="username"], input[placeholder*="用户"]').first().fill(user); + await page.locator('#password, input[type="password"]').first().fill(pass); + await Promise.all([ + page.waitForURL((url) => url.pathname.includes('/spd/home'), { timeout: 120_000 }), + page.getByRole('button', { name: /登\s*录/ }).click(), + ]).catch(async () => { + for (let i = 0; i < 60; i += 1) { + if (page.url().includes('/spd/home')) return; + await page.waitForTimeout(1000); + } + throw new Error(`登录后未进入首页,当前 URL: ${page.url()}`); + }); + await page.waitForTimeout(1500); + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Logged in and saved auth:', AUTH_STATE); +} + +/** 展开 基础设置 > 手术套包设置 子菜单 */ +async function expandSurgeryMenu(page) { + await page.getByRole('menuitem', { name: '基础设置' }).click({ timeout: 20_000 }); + await page.waitForTimeout(500); + const surgTitle = page.locator('.ant-menu-submenu-title').filter({ hasText: /^手术套包设置$/ }).first(); + if (await surgTitle.isVisible().catch(() => false)) { + const expanded = await surgTitle + .locator('xpath=..') + .evaluate((el) => el.classList.contains('ant-menu-submenu-open')) + .catch(() => false); + if (!expanded) await surgTitle.click({ timeout: 10_000 }); + await page.waitForTimeout(400); + } +} + +async function discoverMenuUrls(menus) { + const browser = await chromium.launch({ headless: true, channel: 'chrome' }); + const context = await browser.newContext({ + storageState: AUTH_STATE, + viewport: { width: 1920, height: 1080 }, + locale: 'zh-CN', + }); + const page = await context.newPage(); + const urls = {}; + + for (const item of menus) { + await page.goto(`${BASE}/spd/home/index`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.waitForTimeout(1500); + if (page.url().includes('/login')) { + throw new Error('登录态失效,无法打开手术套包设置菜单'); + } + await expandSurgeryMenu(page); + await page + .locator('.ant-menu-item') + .filter({ hasText: new RegExp(`^\\s*${item.menu}\\s*$`) }) + .first() + .click({ timeout: 20_000 }); + await page.waitForTimeout(2000); + const url = page.url().split('?')[0]; + if (url.includes('/login') || url.endsWith('/spd/home/index')) { + throw new Error(`未能进入 ${item.menu},当前 URL: ${page.url()}`); + } + urls[item.pageKey] = url; + console.log(`URL ${item.menu}: ${url}`); + } + + await browser.close(); + mkdirSync(OUT_DIR, { recursive: true }); + writeFileSync(URL_MAP_OUT, JSON.stringify({ base: BASE, scannedAt: new Date().toISOString(), urls }, null, 2), 'utf8'); + console.log('Wrote URL map:', URL_MAP_OUT); + return urls; +} + +async function openFirstDropdown(page) { + const select = page.locator('.ant-form .ant-select, .ant-form-item .ant-select').first(); + if (!(await select.isVisible().catch(() => false))) return { dropdownOpened: false, skipRescan: true }; + await select.click({ timeout: 10_000 }); + await page + .locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden)') + .first() + .waitFor({ state: 'visible', timeout: 8_000 }) + .catch(() => undefined); + await page.waitForTimeout(400); + return { dropdownOpened: true }; +} + +async function layerOpened(page) { + const modal = await page.locator('.ant-modal, .ant-drawer-open').first().isVisible().catch(() => false); + if (modal) return true; + return page.locator('.put-page').first().isVisible().catch(() => false); +} + +async function openNamedButton(page, pattern, flag) { + const btn = page.getByRole('button', { name: pattern }).first(); + if (!(await btn.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + await btn.click({ timeout: 12_000, force: true }); + await page + .locator('.ant-modal, .ant-drawer-open, .put-page') + .first() + .waitFor({ state: 'visible', timeout: 8_000 }) + .catch(() => undefined); + await page.waitForTimeout(500); + const opened = await layerOpened(page); + return { [flag]: opened, skipRescan: !opened }; +} + +async function openRowAction(page, pattern, flag) { + const row = page.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)').first(); + if (!(await row.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + let action = row.getByRole('button', { name: pattern }).first(); + if (!(await action.isVisible().catch(() => false))) { + action = row.locator('a,button,span').filter({ hasText: pattern }).first(); + } + if (!(await action.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + const before = page.url(); + await action.click({ timeout: 8_000, force: true }); + await page.waitForTimeout(800); + const moved = page.url() !== before && !page.url().includes('/login'); + if (moved) return { [flag]: true, navigated: true, returnUrl: before }; + const opened = await layerOpened(page); + return { [flag]: Boolean(opened), skipRescan: !opened }; +} + +async function clickTab(page, name) { + const tab = page.locator('.ant-tabs-tab').filter({ hasText: new RegExp(name) }).first(); + if (!(await tab.isVisible().catch(() => false))) return { tabOpened: false, skipRescan: true }; + await tab.click({ timeout: 8_000 }); + await page.waitForTimeout(600); + return { tabOpened: true }; +} + +/** 展开左侧分类树首个可展开节点,采集树中元素 */ +async function expandFirstTreeNode(page) { + const switcher = page + .locator('.ant-tree-switcher:not(.ant-tree-switcher-noop), .ant-tree-treenode .ant-tree-switcher_open') + .first(); + if (!(await switcher.isVisible().catch(() => false))) return { treeExpanded: false, skipRescan: true }; + await switcher.click({ timeout: 8_000 }).catch(() => undefined); + await page.waitForTimeout(600); + return { treeExpanded: true }; +} + +function pageCfg(targetUrl, item) { + return { + targetUrl, + useIframe: false, + iframeMatch: '', + iframeLabel: 'main', + module: 'spd-basic', + pageKey: item.pageKey, + pageTitle: item.pageTitle, + jsonOut: path.join(OUT_DIR, `${item.pageKey}-elements.json`), + yamlOut: path.join(LOC_DIR, `${item.pageKey}.locator.yaml`), + jsonSourceName: `${item.pageKey}-elements.json`, + waitSelectors: ['.ant-tree', '.ant-form-item input', '.ant-form', '.ant-table', '.ant-btn', '.ant-tabs'], + waitSelector: '.ant-tree, .ant-form, .ant-table, .ant-btn, .ant-tabs', + waitUntil: 'domcontentloaded', + expandFirstRow: false, + openAdvancedFilter: false, + interactivePhases: [ + openFirstDropdown, + expandFirstTreeNode, + (page) => clickTab(page, '停用'), + (page) => clickTab(page, '未建分类'), + (page) => openNamedButton(page, /新增基础包/, 'addBasePkgOpened'), + (page) => openNamedButton(page, /添加基础包/, 'addBasePkgOpened'), + (page) => openNamedButton(page, /添加散件/, 'addLoosePartOpened'), + (page) => openNamedButton(page, /新\s*增/, 'dialogOpened'), + (page) => openRowAction(page, /编\s*辑/, 'editDialogOpened'), + (page) => openRowAction(page, /详\s*情/, 'detailOpened'), + (page) => openRowAction(page, /停\s*用/, 'disableDialogOpened'), + ], + }; +} + +async function main() { + const filter = process.argv.slice(2); + const menus = filter.length ? MENUS.filter((p) => filter.includes(p.pageKey)) : MENUS; + if (!menus.length) { + console.error('No pages matched. Available:', MENUS.map((p) => p.pageKey).join(', ')); + process.exit(1); + } + + await ensureAuthStorage(); + const urls = await discoverMenuUrls(menus); + const summary = []; + + for (const item of menus) { + const targetUrl = urls[item.pageKey]; + console.log(`\n=== Scanning ${item.pageKey} ${targetUrl} ===`); + try { + const result = await runScan(pageCfg(targetUrl, item)); + const yaml = writeYamlFromScanJson(result.jsonOut, { + pageKey: item.pageKey, + pageTitle: item.pageTitle, + module: 'spd-basic', + jsonSourceName: `${item.pageKey}-elements.json`, + iframeMatch: '', + yamlOut: path.join(LOC_DIR, `${item.pageKey}.locator.yaml`), + }); + summary.push({ + page: item.pageKey, + menu: item.menu, + specId: item.specId, + ok: true, + url: targetUrl, + ...result, + yamlOut: yaml.yamlOut, + }); + } catch (err) { + console.error(`FAILED ${item.pageKey}:`, err); + summary.push({ page: item.pageKey, menu: item.menu, specId: item.specId, ok: false, error: String(err) }); + } + } + + console.log('\n========== SUMMARY =========='); + for (const row of summary) { + if (row.ok) { + console.log(`✓ ${row.page}: ${row.elementCount} elements ${row.url}`); + console.log(` JSON: ${row.jsonOut}`); + console.log(` YAML: ${row.yamlOut}`); + } else { + console.log(`✗ ${row.page}: ${row.error}`); + } + } + writeFileSync( + path.join(OUT_DIR, 'surgery-scan-summary.json'), + JSON.stringify(summary, null, 2), + 'utf8', + ); + if (summary.some((r) => !r.ok)) process.exit(1); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/scan-user-batch.mjs b/scripts/scan-user-batch.mjs new file mode 100644 index 0000000..e9adb41 --- /dev/null +++ b/scripts/scan-user-batch.mjs @@ -0,0 +1,317 @@ +#!/usr/bin/env node +/** + * Batch scan: 基础设置 > 用户管理 (3 pages) from UI-SPEC USER-001~003. + * 直接按 UI-SPEC 中已确认的页面路径打开(无需从菜单点进去,避免同名菜单歧义)。 + * + * 输出: + * data/scan/basic-user-elements.json 用户管理 + * data/scan/basic-user-union-elements.json 医共体用户管理 + * data/scan/basic-role-elements.json 角色管理 + * locators/spd/{pageKey}.locator.yaml 由 JSON 提炼的 Playwright 定位器 + * + * Usage: + * node scripts/scan-user-batch.mjs + * node scripts/scan-user-batch.mjs basic-user basic-role + * + * 只重算 YAML(不重新扫描): + * node scripts/json-to-locator-yaml.mjs data/scan/basic-user-elements.json + */ +import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import './playwright-browsers-path.mjs'; +import { runScan, writeYamlFromScanJson } from './scan-interactive-elements.mjs'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); +const AUTH_STATE = path.join(ROOT, 'auth/user.json'); +const BASE = process.env.BASE_URL || 'http://spdtest.cmic.com.cn:8080'; +const OUT_DIR = path.join(ROOT, 'data/scan'); +const LOC_DIR = path.join(ROOT, 'locators/spd'); +const SUMMARY_OUT = path.join(OUT_DIR, 'user-scan-summary.json'); + +if (existsSync(path.join(ROOT, '.env'))) { + process.loadEnvFile(path.join(ROOT, '.env')); +} + +const COMMON_WAIT = [ + '.ant-form-item input', + '.ant-form-item .ant-select', + '.ant-form', + '.ant-table', + '.ant-btn', + '.ant-pagination', +]; + +async function closeOverlays(page) { + const footerCancel = page + .locator('.ant-modal .ant-modal-footer button, .ant-drawer .ant-drawer-footer button') + .filter({ hasText: /取\s*消/ }) + .last(); + if (await footerCancel.isVisible().catch(() => false)) { + await footerCancel.click({ timeout: 8_000 }).catch(() => undefined); + await page.waitForTimeout(400); + } + for (let i = 0; i < 3; i += 1) { + const popCancel = page + .locator('.ant-popconfirm button, .ant-popover-open button, .ant-modal-confirm button') + .filter({ hasText: /取\s*消/ }) + .last(); + if (await popCancel.isVisible().catch(() => false)) { + await popCancel.click({ timeout: 5_000 }).catch(() => undefined); + await page.waitForTimeout(300); + continue; + } + const modalClose = page + .locator('.ant-modal-wrap .ant-modal-close, .ant-drawer-close') + .last(); + if (await modalClose.isVisible().catch(() => false)) { + await modalClose.click({ timeout: 5_000 }).catch(() => undefined); + await page.waitForTimeout(400); + continue; + } + break; + } +} + +/** 打开页面上第一个筛选下拉,采集下拉面板内的选项元素 */ +async function openFirstFilterDropdown(page) { + const select = page.locator('.ant-form .ant-select, .ant-form-item .ant-select').first(); + if (!(await select.isVisible().catch(() => false))) return { dropdownOpened: false, skipRescan: true }; + await select.click({ timeout: 10_000 }); + await page + .locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden)') + .first() + .waitFor({ state: 'visible', timeout: 8_000 }) + .catch(() => undefined); + await page.waitForTimeout(500); + return { dropdownOpened: true }; +} + +/** 展开「高级筛选」面板(用户管理 / 医共体用户管理) */ +async function openAdvancedFilter(page) { + await closeOverlays(page); + const btn = page.getByText(/高级筛选/).first(); + if (!(await btn.isVisible().catch(() => false))) return { advancedFilterOpened: false, skipRescan: true }; + await btn.click({ timeout: 10_000 }).catch(() => undefined); + await page.waitForTimeout(600); + return { advancedFilterOpened: true }; +} + +/** 打开「新增」弹窗(按标题断言,避免采到别的弹窗) */ +async function openAddModal(content, titleRe) { + await closeOverlays(content); + const btn = content.getByRole('button', { name: /新\s*增/ }).first(); + if (!(await btn.isVisible().catch(() => false))) return { dialogOpened: false, skipRescan: true }; + await btn.click({ timeout: 15_000, force: true }); + await content + .locator('.ant-modal, .ant-drawer-open') + .filter({ hasText: titleRe }) + .first() + .waitFor({ state: 'visible', timeout: 15_000 }) + .catch(() => undefined); + await content.waitForTimeout(800); + const opened = await content.locator('.ant-modal, .ant-drawer-open').first().isVisible().catch(() => false); + return { dialogOpened: opened, skipRescan: !opened }; +} + +function firstDataRow(page) { + return page.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)').first(); +} + +/** 点击首行的某个行操作,记录是弹窗还是跳转 */ +async function openRowAction(page, pattern, flag) { + await closeOverlays(page); + const row = firstDataRow(page); + if (!(await row.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + let action = row.getByRole('button', { name: pattern }).first(); + if (!(await action.isVisible().catch(() => false))) { + action = row.locator('a,button,span').filter({ hasText: pattern }).first(); + } + if (!(await action.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + + const before = page.url(); + await action.click({ timeout: 8_000, force: true }); + await page.waitForTimeout(900); + + const moved = page.url() !== before && !page.url().includes('/login'); + if (moved) return { [flag]: true, navigated: true, returnUrl: before }; + + const opened = await page.locator('.ant-modal, .ant-drawer-open').first().isVisible().catch(() => false); + return { [flag]: Boolean(opened), skipRescan: !opened }; +} + +/** 角色管理:点击左侧角色列表的首个角色,采集右侧权限树 */ +async function selectFirstRole(page) { + await closeOverlays(page); + const row = page.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)').first(); + const target = (await row.isVisible().catch(() => false)) + ? row.locator('td').first() + : page.locator('.ant-list-item, .role-list li').first(); + if (!(await target.isVisible().catch(() => false))) return { roleSelected: false, skipRescan: true }; + await target.click({ timeout: 10_000 }).catch(() => undefined); + await page.waitForTimeout(900); + return { roleSelected: true }; +} + +/** 角色管理:展开权限树的一个二级模块 */ +async function expandFirstPermissionNode(page) { + const switcher = page + .locator('.ant-tree-switcher:not(.ant-tree-switcher-noop), .ant-tree-switcher_open') + .first(); + if (!(await switcher.isVisible().catch(() => false))) return { treeExpanded: false, skipRescan: true }; + await switcher.click({ timeout: 8_000 }).catch(() => undefined); + await page.waitForTimeout(700); + return { treeExpanded: true }; +} + +function pageCfg(uiPath, rest) { + return { + targetUrl: `${BASE}${uiPath}`, + useIframe: false, + iframeMatch: '', + iframeLabel: 'main', + module: 'spd-basic', + jsonOut: path.join(OUT_DIR, `${rest.pageKey}-elements.json`), + yamlOut: path.join(LOC_DIR, `${rest.pageKey}.locator.yaml`), + jsonSourceName: `${rest.pageKey}-elements.json`, + waitSelectors: COMMON_WAIT, + waitSelector: '.ant-form-item input, .ant-table, .ant-btn, .ant-form', + waitUntil: 'domcontentloaded', + expandFirstRow: false, + openAdvancedFilter: false, + skipQueryClick: true, + ...rest, + }; +} + +const PAGES = [ + pageCfg('/spd/basic/user/platform/person', { + pageKey: 'basic-user', + pageTitle: '基础设置 > 用户管理 > 用户管理', + specId: 'USER-001', + interactivePhases: [ + openFirstFilterDropdown, + openAdvancedFilter, + (page) => openAddModal(page, /新增用户|新增/), + (page) => openRowAction(page, /编\s*辑/, 'editDialogOpened'), + (page) => openRowAction(page, /授\s*权/, 'authDialogOpened'), + ], + }), + pageCfg('/spd/basic/user/platform/personCom', { + pageKey: 'basic-user-union', + pageTitle: '基础设置 > 用户管理 > 医共体用户管理', + specId: 'USER-002', + interactivePhases: [ + openFirstFilterDropdown, + openAdvancedFilter, + (page) => openAddModal(page, /新增/), + (page) => openRowAction(page, /编\s*辑/, 'editDialogOpened'), + (page) => openRowAction(page, /授\s*权/, 'authDialogOpened'), + ], + }), + pageCfg('/spd/basic/user/platform/role', { + pageKey: 'basic-role', + pageTitle: '基础设置 > 用户管理 > 角色管理', + specId: 'USER-003', + interactivePhases: [ + selectFirstRole, + expandFirstPermissionNode, + (page) => openAddModal(page, /新增/), + (page) => openRowAction(page, /编\s*辑/, 'editDialogOpened'), + ], + }), +]; + +async function ensureAuthStorage() { + mkdirSync(path.dirname(AUTH_STATE), { recursive: true }); + const user = process.env.TEST_USER; + const pass = process.env.TEST_PASS; + if (!user || !pass) { + throw new Error('未配置 TEST_USER/TEST_PASS,请复制 .env.example 为 .env 后填写测试账号'); + } + const { chromium } = await import('playwright'); + const browser = await chromium.launch({ headless: true, channel: 'chrome' }); + const context = await browser.newContext({ + viewport: { width: 1920, height: 1080 }, + locale: 'zh-CN', + storageState: existsSync(AUTH_STATE) ? AUTH_STATE : undefined, + }); + const page = await context.newPage(); + await page.goto(`${BASE}/spd/home/index`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.waitForTimeout(2000); + if (!page.url().includes('/login')) { + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Auth state OK:', AUTH_STATE); + return; + } + await page.goto(`${BASE}/spd/login`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.locator('#username, input[name="username"], input[placeholder*="用户"]').first().fill(user); + await page.locator('#password, input[type="password"]').first().fill(pass); + await page.getByRole('button', { name: /登\s*录/ }).click(); + await page.waitForURL(/\/spd\/home/, { timeout: 120_000 }); + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Logged in and saved auth:', AUTH_STATE); +} + +async function main() { + const filter = process.argv.slice(2); + const pages = filter.length ? PAGES.filter((p) => filter.includes(p.pageKey)) : PAGES; + if (!pages.length) { + console.error('No pages matched. Available:', PAGES.map((p) => p.pageKey).join(', ')); + process.exit(1); + } + + await ensureAuthStorage(); + const summary = []; + + for (const item of pages) { + console.log(`\n=== Scanning ${item.pageKey} ${item.targetUrl} ===`); + try { + const result = await runScan(item); + // 扫描阶段会顺带写一份 YAML;这里再按落盘 JSON 提炼一次,确保 YAML 只由 JSON 决定 + const yaml = writeYamlFromScanJson(result.jsonOut, { + pageKey: item.pageKey, + pageTitle: item.pageTitle, + module: 'spd-basic', + jsonSourceName: `${item.pageKey}-elements.json`, + iframeMatch: '', + yamlOut: path.join(LOC_DIR, `${item.pageKey}.locator.yaml`), + }); + summary.push({ + page: item.pageKey, + specId: item.specId, + ok: true, + url: item.targetUrl, + elementCount: result.elementCount, + jsonOut: result.jsonOut, + yamlOut: yaml.yamlOut, + }); + } catch (err) { + console.error(`FAILED ${item.pageKey}:`, err); + summary.push({ page: item.pageKey, specId: item.specId, ok: false, error: String(err) }); + } + } + + console.log('\n========== SUMMARY =========='); + for (const row of summary) { + if (row.ok) { + console.log(`✓ ${row.page}: ${row.elementCount} elements`); + console.log(` JSON: ${row.jsonOut}`); + console.log(` YAML: ${row.yamlOut}`); + } else { + console.log(`✗ ${row.page}: ${row.error}`); + } + } + mkdirSync(OUT_DIR, { recursive: true }); + writeFileSync(SUMMARY_OUT, JSON.stringify(summary, null, 2), 'utf8'); + if (summary.some((r) => !r.ok)) process.exit(1); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/scan-vendor-batch.mjs b/scripts/scan-vendor-batch.mjs new file mode 100644 index 0000000..84b4985 --- /dev/null +++ b/scripts/scan-vendor-batch.mjs @@ -0,0 +1,290 @@ +#!/usr/bin/env node +/** + * Batch scan: 基础设置 > 厂商管理 (10 pages) from UI-SPEC VENDOR-001~010. + * Saves data/scan/*-elements.json, then regenerates locators/spd/*.locator.yaml. + * + * Usage: + * node scripts/scan-vendor-batch.mjs + * node scripts/scan-vendor-batch.mjs vendor-query vendor-qual + */ +import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import './playwright-browsers-path.mjs'; +import { chromium } from 'playwright'; + +import { runScan, writeYamlFromScanJson } from './scan-interactive-elements.mjs'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); +const AUTH_STATE = path.join(ROOT, 'auth/user.json'); +const BASE = process.env.BASE_URL || 'http://spdtest.cmic.com.cn:8080'; +const OUT_DIR = path.join(ROOT, 'data/scan'); +const LOC_DIR = path.join(ROOT, 'locators/spd'); +const URL_MAP_OUT = path.join(OUT_DIR, 'vendor-menu-urls.json'); + +if (existsSync(path.join(ROOT, '.env'))) { + process.loadEnvFile(path.join(ROOT, '.env')); +} + +const MENUS = [ + { pageKey: 'vendor-query', menu: '厂商查询', pageTitle: '基础设置 > 厂商管理 > 厂商查询', specId: 'VENDOR-001' }, + { pageKey: 'vendor-qual', menu: '供应商资质管理', pageTitle: '基础设置 > 厂商管理 > 供应商资质管理', specId: 'VENDOR-002' }, + { pageKey: 'vendor-auth-first', menu: '授权变更初审', pageTitle: '基础设置 > 厂商管理 > 授权变更初审', specId: 'VENDOR-003' }, + { pageKey: 'vendor-auth-final', menu: '授权变更终审', pageTitle: '基础设置 > 厂商管理 > 授权变更终审', specId: 'VENDOR-004' }, + { pageKey: 'vendor-cert-first', menu: '注册证变更初审', pageTitle: '基础设置 > 厂商管理 > 注册证变更初审', specId: 'VENDOR-005' }, + { pageKey: 'vendor-cert-final', menu: '注册证变更终审', pageTitle: '基础设置 > 厂商管理 > 注册证变更终审', specId: 'VENDOR-006' }, + { pageKey: 'vendor-cert-diff', menu: '注册证差异对比', pageTitle: '基础设置 > 厂商管理 > 注册证差异对比', specId: 'VENDOR-007' }, + { pageKey: 'vendor-eval-scheme', menu: '评价方案维护', pageTitle: '基础设置 > 厂商管理 > 评价方案维护', specId: 'VENDOR-008' }, + { pageKey: 'vendor-eval-indicator', menu: '评价指标维护', pageTitle: '基础设置 > 厂商管理 > 评价指标维护', specId: 'VENDOR-009' }, + { pageKey: 'vendor-holiday', menu: '供应商节假日管理', pageTitle: '基础设置 > 厂商管理 > 供应商节假日管理', specId: 'VENDOR-010' }, +]; + +async function ensureAuthStorage() { + mkdirSync(path.dirname(AUTH_STATE), { recursive: true }); + const user = process.env.TEST_USER; + const pass = process.env.TEST_PASS; + if (!user || !pass) { + throw new Error('未配置 TEST_USER/TEST_PASS,请复制 .env.example 为 .env 后填写测试账号'); + } + + const browser = await chromium.launch({ headless: true, channel: 'chrome' }); + const context = await browser.newContext({ + viewport: { width: 1920, height: 1080 }, + locale: 'zh-CN', + storageState: existsSync(AUTH_STATE) ? AUTH_STATE : undefined, + }); + const page = await context.newPage(); + await page.goto(`${BASE}/spd/home/index`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.waitForTimeout(2000); + if (!page.url().includes('/login')) { + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Auth state OK:', AUTH_STATE); + return; + } + + await page.goto(`${BASE}/spd/login`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.locator('#username, input[name="username"], input[placeholder*="用户"]').first().fill(user); + await page.locator('#password, input[type="password"]').first().fill(pass); + await Promise.all([ + page.waitForURL((url) => url.pathname.includes('/spd/home'), { timeout: 120_000 }), + page.getByRole('button', { name: /登\s*录/ }).click(), + ]).catch(async () => { + // 部分环境导航事件不触发 waitForURL,轮询兜底 + for (let i = 0; i < 60; i += 1) { + if (page.url().includes('/spd/home')) return; + await page.waitForTimeout(1000); + } + throw new Error(`登录后未进入首页,当前 URL: ${page.url()}`); + }); + await page.waitForTimeout(1500); + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Logged in and saved auth:', AUTH_STATE); +} + +async function expandVendorMenu(page) { + await page.getByRole('menuitem', { name: '基础设置' }).click({ timeout: 20_000 }); + await page.waitForTimeout(500); + const vendorTitle = page.locator('.ant-menu-submenu-title').filter({ hasText: /^厂商管理$/ }).first(); + if (await vendorTitle.isVisible().catch(() => false)) { + const expanded = await vendorTitle + .locator('xpath=..') + .evaluate((el) => el.classList.contains('ant-menu-submenu-open')) + .catch(() => false); + if (!expanded) await vendorTitle.click({ timeout: 10_000 }); + } +} + +async function discoverMenuUrls(menus) { + const browser = await chromium.launch({ headless: true, channel: 'chrome' }); + const context = await browser.newContext({ + storageState: AUTH_STATE, + viewport: { width: 1920, height: 1080 }, + locale: 'zh-CN', + }); + const page = await context.newPage(); + const urls = {}; + + for (const item of menus) { + await page.goto(`${BASE}/spd/home/index`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.waitForTimeout(1500); + if (page.url().includes('/login')) { + throw new Error('登录态失效,无法打开厂商管理菜单'); + } + await expandVendorMenu(page); + await page + .locator('.ant-menu-item') + .filter({ hasText: new RegExp(`^\\s*${item.menu}\\s*$`) }) + .first() + .click({ timeout: 20_000 }); + await page.waitForTimeout(2000); + const url = page.url().split('?')[0]; + if (url.includes('/login') || url.endsWith('/spd/home/index')) { + throw new Error(`未能进入 ${item.menu},当前 URL: ${page.url()}`); + } + urls[item.pageKey] = url; + console.log(`URL ${item.menu}: ${url}`); + } + + await browser.close(); + mkdirSync(OUT_DIR, { recursive: true }); + writeFileSync(URL_MAP_OUT, JSON.stringify({ base: BASE, scannedAt: new Date().toISOString(), urls }, null, 2), 'utf8'); + console.log('Wrote URL map:', URL_MAP_OUT); + return urls; +} + +async function openFirstDropdown(page) { + const select = page.locator('.ant-form .ant-select, .ant-form-item .ant-select').first(); + if (!(await select.isVisible().catch(() => false))) return { dropdownOpened: false, skipRescan: true }; + await select.click({ timeout: 10_000 }); + await page + .locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden)') + .first() + .waitFor({ state: 'visible', timeout: 8_000 }) + .catch(() => undefined); + await page.waitForTimeout(400); + return { dropdownOpened: true }; +} + +async function layerOpened(page) { + const modal = await page.locator('.ant-modal, .ant-drawer-open').first().isVisible().catch(() => false); + if (modal) return true; + return page.locator('.put-page').first().isVisible().catch(() => false); +} + +async function openNamedButton(page, pattern, flag) { + const btn = page.getByRole('button', { name: pattern }).first(); + if (!(await btn.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + await btn.click({ timeout: 12_000, force: true }); + await page + .locator('.ant-modal, .ant-drawer-open, .put-page') + .first() + .waitFor({ state: 'visible', timeout: 8_000 }) + .catch(() => undefined); + await page.waitForTimeout(500); + const opened = await layerOpened(page); + return { [flag]: opened, skipRescan: !opened }; +} + +async function openRowAction(page, pattern, flag) { + const row = page.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)').first(); + if (!(await row.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + let action = row.getByRole('button', { name: pattern }).first(); + if (!(await action.isVisible().catch(() => false))) { + action = row.locator('a,button,span').filter({ hasText: pattern }).first(); + } + if (!(await action.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + const before = page.url(); + await action.click({ timeout: 8_000, force: true }); + await page.waitForTimeout(800); + const moved = page.url() !== before && !page.url().includes('/login'); + if (moved) return { [flag]: true, navigated: true, returnUrl: before }; + const opened = await layerOpened(page); + return { [flag]: Boolean(opened), skipRescan: !opened }; +} + +async function clickTab(page, name) { + const tab = page.locator('.ant-tabs-tab').filter({ hasText: new RegExp(name) }).first(); + if (!(await tab.isVisible().catch(() => false))) return { tabOpened: false, skipRescan: true }; + await tab.click({ timeout: 8_000 }); + await page.waitForTimeout(600); + return { tabOpened: true }; +} + +function pageCfg(targetUrl, item) { + return { + targetUrl, + useIframe: false, + iframeMatch: '', + iframeLabel: 'main', + module: 'spd-basic', + pageKey: item.pageKey, + pageTitle: item.pageTitle, + jsonOut: path.join(OUT_DIR, `${item.pageKey}-elements.json`), + yamlOut: path.join(LOC_DIR, `${item.pageKey}.locator.yaml`), + jsonSourceName: `${item.pageKey}-elements.json`, + waitSelectors: ['.ant-form-item input', '.ant-form', '.ant-table', '.ant-btn', '.ant-tabs'], + waitSelector: '.ant-form, .ant-table, .ant-btn, .ant-tabs', + waitUntil: 'domcontentloaded', + expandFirstRow: false, + openAdvancedFilter: false, + interactivePhases: [ + openFirstDropdown, + (page) => clickTab(page, '经销商'), + (page) => clickTab(page, '授权链路效期预警'), + (page) => clickTab(page, '不一致'), + (page) => openNamedButton(page, /新\s*增/, 'dialogOpened'), + (page) => openNamedButton(page, /导\s*出/, 'exportOpened'), + (page) => openRowAction(page, /详\s*情/, 'detailOpened'), + (page) => openRowAction(page, /查\s*看/, 'viewOpened'), + (page) => openRowAction(page, /编\s*辑/, 'editDialogOpened'), + (page) => openRowAction(page, /日\s*志/, 'logOpened'), + ], + }; +} + +async function main() { + const filter = process.argv.slice(2); + const menus = filter.length ? MENUS.filter((p) => filter.includes(p.pageKey)) : MENUS; + if (!menus.length) { + console.error('No pages matched. Available:', MENUS.map((p) => p.pageKey).join(', ')); + process.exit(1); + } + + await ensureAuthStorage(); + const urls = await discoverMenuUrls(menus); + const summary = []; + + for (const item of menus) { + const targetUrl = urls[item.pageKey]; + console.log(`\n=== Scanning ${item.pageKey} ${targetUrl} ===`); + try { + const result = await runScan(pageCfg(targetUrl, item)); + const yaml = writeYamlFromScanJson(result.jsonOut, { + pageKey: item.pageKey, + pageTitle: item.pageTitle, + module: 'spd-basic', + jsonSourceName: `${item.pageKey}-elements.json`, + iframeMatch: '', + yamlOut: path.join(LOC_DIR, `${item.pageKey}.locator.yaml`), + }); + summary.push({ + page: item.pageKey, + menu: item.menu, + specId: item.specId, + ok: true, + url: targetUrl, + ...result, + yamlOut: yaml.yamlOut, + }); + } catch (err) { + console.error(`FAILED ${item.pageKey}:`, err); + summary.push({ page: item.pageKey, menu: item.menu, specId: item.specId, ok: false, error: String(err) }); + } + } + + console.log('\n========== SUMMARY =========='); + for (const row of summary) { + if (row.ok) { + console.log(`✓ ${row.page}: ${row.elementCount} elements ${row.url}`); + console.log(` JSON: ${row.jsonOut}`); + console.log(` YAML: ${row.yamlOut}`); + } else { + console.log(`✗ ${row.page}: ${row.error}`); + } + } + writeFileSync( + path.join(OUT_DIR, 'vendor-scan-summary.json'), + JSON.stringify(summary, null, 2), + 'utf8', + ); + if (summary.some((r) => !r.ok)) process.exit(1); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/scan-wh-flow-batch.mjs b/scripts/scan-wh-flow-batch.mjs new file mode 100644 index 0000000..8269f57 --- /dev/null +++ b/scripts/scan-wh-flow-batch.mjs @@ -0,0 +1,475 @@ +#!/usr/bin/env node +/** + * Batch scan: 基础设置 > 库房管理 / 流程设置 (UI-SPEC WH-001~002, FLOW-001~002). + * 先按菜单点击解析真实 URL,再扫描交互元素。 + * + * 输出: + * data/scan/warehouse-zone-elements.json 库区设置 + * data/scan/warehouse-probe-elements.json 探针绑定 + * data/scan/flow-def-elements.json 审批流程设置 + * data/scan/flow-inst-elements.json 审批流实例 + * locators/spd/{pageKey}.locator.yaml + * + * Usage: + * node scripts/scan-wh-flow-batch.mjs + * node scripts/scan-wh-flow-batch.mjs warehouse-zone warehouse-probe + * node scripts/scan-wh-flow-batch.mjs flow-def flow-inst + * + * 只重算 YAML(不重新扫描): + * node scripts/json-to-locator-yaml.mjs --all-warehouse --all-flow + */ +import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import './playwright-browsers-path.mjs'; +import { chromium } from 'playwright'; + +import { runScan, writeYamlFromScanJson } from './scan-interactive-elements.mjs'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); +const AUTH_STATE = path.join(ROOT, 'auth/user.json'); +const BASE = process.env.BASE_URL || 'http://spdtest.cmic.com.cn:8080'; +const OUT_DIR = path.join(ROOT, 'data/scan'); +const LOC_DIR = path.join(ROOT, 'locators/spd'); +const URL_MAP_OUT = path.join(OUT_DIR, 'wh-flow-menu-urls.json'); +const SUMMARY_OUT = path.join(OUT_DIR, 'wh-flow-scan-summary.json'); + +if (existsSync(path.join(ROOT, '.env'))) { + process.loadEnvFile(path.join(ROOT, '.env')); +} + +const COMMON_WAIT = [ + '.ant-form-item input', + '.ant-form-item .ant-select', + '.ant-form', + '.ant-table', + '.ant-btn', + '.ant-pagination', + '.ant-list-item', + '.ant-menu-item', +]; + +const MENUS = [ + { + pageKey: 'warehouse-zone', + group: '库房管理', + menu: '库区设置', + pageTitle: '基础设置 > 库房管理 > 库区设置', + specId: 'WH-001', + skipQueryClick: true, + interactivePhases: [ + selectLeftFirstItem, + openFirstFilterDropdown, + (page) => openNamedButton(page, /新\s*增/, 'dialogOpened'), + (page) => openRowAction(page, /编\s*辑/, 'editDialogOpened'), + (page) => openRowAction(page, /人员管理/, 'staffDialogOpened'), + (page) => openRowAction(page, /货位管理/, 'locationDialogOpened'), + ], + }, + { + pageKey: 'warehouse-probe', + group: '库房管理', + menu: '探针绑定', + pageTitle: '基础设置 > 库房管理 > 探针绑定', + specId: 'WH-002', + skipQueryClick: false, + interactivePhases: [ + openFirstFilterDropdown, + (page) => openRowAction(page, /绑定货位/, 'bindDialogOpened'), + openModalFirstDropdown, + ], + }, + { + pageKey: 'flow-def', + group: '流程设置', + menu: '审批流程设置', + pageTitle: '基础设置 > 流程设置 > 审批流程设置', + specId: 'FLOW-001', + skipQueryClick: true, + interactivePhases: [ + selectLeftFirstItem, + openFirstFilterDropdown, + (page) => openNamedButton(page, /新增流程/, 'addFlowClicked'), + (page) => openInlineEdit(page, /编\s*辑/, 'conditionEditorOpened'), + ], + }, + { + pageKey: 'flow-inst', + group: '流程设置', + menu: '审批流实例', + pageTitle: '基础设置 > 流程设置 > 审批流实例', + specId: 'FLOW-002', + skipQueryClick: false, + interactivePhases: [ + openFirstFilterDropdown, + openSecondFilterDropdown, + openFlowDetail, + ], + }, +]; + +async function closeOverlays(page) { + await page.keyboard.press('Escape').catch(() => undefined); + await page.waitForTimeout(200); + const footerCancel = page + .locator('.ant-modal .ant-modal-footer button, .ant-drawer .ant-drawer-footer button') + .filter({ hasText: /取\s*消|关\s*闭/ }) + .last(); + if (await footerCancel.isVisible().catch(() => false)) { + await footerCancel.click({ timeout: 8_000 }).catch(() => undefined); + await page.waitForTimeout(400); + } + for (let i = 0; i < 3; i += 1) { + const popCancel = page + .locator('.ant-popconfirm button, .ant-popover-open button, .ant-modal-confirm button') + .filter({ hasText: /取\s*消/ }) + .last(); + if (await popCancel.isVisible().catch(() => false)) { + await popCancel.click({ timeout: 5_000 }).catch(() => undefined); + await page.waitForTimeout(300); + continue; + } + const modalClose = page.locator('.ant-modal-wrap .ant-modal-close, .ant-drawer-close').last(); + if (await modalClose.isVisible().catch(() => false)) { + await modalClose.click({ timeout: 5_000 }).catch(() => undefined); + await page.waitForTimeout(400); + continue; + } + break; + } +} + +function firstDataRow(page) { + return page.locator('.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)').first(); +} + +async function layerOpened(page) { + const modal = await page.locator('.ant-modal, .ant-drawer-open').first().isVisible().catch(() => false); + if (modal) return true; + return page.locator('.put-page').first().isVisible().catch(() => false); +} + +async function openFirstFilterDropdown(page) { + await closeOverlays(page); + const select = page.locator('.ant-form .ant-select, .ant-form-item .ant-select').first(); + if (!(await select.isVisible().catch(() => false))) return { dropdownOpened: false, skipRescan: true }; + await select.click({ timeout: 10_000 }); + await page + .locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden)') + .first() + .waitFor({ state: 'visible', timeout: 8_000 }) + .catch(() => undefined); + await page.waitForTimeout(500); + return { dropdownOpened: true }; +} + +async function openSecondFilterDropdown(page) { + await closeOverlays(page); + const select = page.locator('.ant-form .ant-select, .ant-form-item .ant-select').nth(1); + if (!(await select.isVisible().catch(() => false))) return { secondDropdownOpened: false, skipRescan: true }; + await select.click({ timeout: 10_000 }); + await page + .locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden)') + .first() + .waitFor({ state: 'visible', timeout: 8_000 }) + .catch(() => undefined); + await page.waitForTimeout(400); + return { secondDropdownOpened: true }; +} + +async function openModalFirstDropdown(page) { + const modal = page.locator('.ant-modal:visible, .ant-drawer-open').first(); + if (!(await modal.isVisible().catch(() => false))) return { modalDropdownOpened: false, skipRescan: true }; + const select = modal.locator('.ant-select').first(); + if (!(await select.isVisible().catch(() => false))) return { modalDropdownOpened: false, skipRescan: true }; + await select.click({ timeout: 8_000 }); + await page + .locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden)') + .first() + .waitFor({ state: 'visible', timeout: 8_000 }) + .catch(() => undefined); + await page.waitForTimeout(400); + return { modalDropdownOpened: true }; +} + +async function selectLeftFirstItem(page) { + await closeOverlays(page); + const candidates = [ + page.locator('.ant-tree .ant-tree-title').first(), + page.locator('.ant-menu-inline .ant-menu-item:not(.ant-menu-item-selected)').first(), + page.locator('.ant-list-item').first(), + page.locator('.ant-table-tbody tr.ant-table-row td').first(), + ]; + for (const loc of candidates) { + if (await loc.isVisible().catch(() => false)) { + await loc.click({ timeout: 10_000 }).catch(() => undefined); + await page.waitForTimeout(800); + return { leftItemSelected: true }; + } + } + return { leftItemSelected: false, skipRescan: true }; +} + +async function openNamedButton(page, pattern, flag) { + await closeOverlays(page); + const btn = page.getByRole('button', { name: pattern }).first(); + if (!(await btn.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + await btn.click({ timeout: 12_000, force: true }); + await page + .locator('.ant-modal, .ant-drawer-open, .put-page') + .first() + .waitFor({ state: 'visible', timeout: 8_000 }) + .catch(() => undefined); + await page.waitForTimeout(600); + const opened = await layerOpened(page); + return { [flag]: opened, skipRescan: !opened }; +} + +async function openRowAction(page, pattern, flag) { + await closeOverlays(page); + const row = firstDataRow(page); + if (!(await row.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + let action = row.getByRole('button', { name: pattern }).first(); + if (!(await action.isVisible().catch(() => false))) { + action = row.locator('a,button,span').filter({ hasText: pattern }).first(); + } + if (!(await action.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + + const before = page.url(); + await action.click({ timeout: 8_000, force: true }); + await page.waitForTimeout(1600); + + const moved = page.url() !== before && !page.url().includes('/login'); + if (moved) return { [flag]: true, navigated: true, returnUrl: before }; + + const opened = await layerOpened(page); + return { [flag]: Boolean(opened), skipRescan: !opened }; +} + +/** 审批流实例「详情」多为链接/文字按钮,且可能被下拉遮挡 */ +async function openFlowDetail(page) { + await closeOverlays(page); + const row = firstDataRow(page); + const candidates = [ + row.getByRole('button', { name: /详\s*情/ }).first(), + row.getByRole('link', { name: /详\s*情/ }).first(), + row.locator('a,button,span').filter({ hasText: /详\s*情/ }).first(), + page.locator('.ant-table-tbody a, .ant-table-tbody button, .ant-table-tbody span').filter({ hasText: /详\s*情/ }).first(), + ]; + let action = null; + for (const loc of candidates) { + if (await loc.isVisible().catch(() => false)) { + action = loc; + break; + } + } + if (!action) return { detailOpened: false, skipRescan: true }; + const before = page.url(); + await action.click({ timeout: 8_000, force: true }); + await page.waitForTimeout(1600); + const moved = page.url() !== before && !page.url().includes('/login'); + if (moved) return { detailOpened: true, navigated: true, returnUrl: before }; + const opened = await layerOpened(page); + return { detailOpened: Boolean(opened), skipRescan: !opened }; +} + +async function openInlineEdit(page, pattern, flag) { + await closeOverlays(page); + const action = page.locator('a,button,span').filter({ hasText: pattern }).first(); + if (!(await action.isVisible().catch(() => false))) return { [flag]: false, skipRescan: true }; + await action.click({ timeout: 8_000, force: true }); + await page.waitForTimeout(700); + const opened = await layerOpened(page); + const picker = await page + .locator('.ant-select-dropdown:not(.ant-select-dropdown-hidden), .ant-picker-dropdown, .ant-popover-open') + .first() + .isVisible() + .catch(() => false); + return { [flag]: opened || picker, skipRescan: !(opened || picker) }; +} + +async function ensureAuthStorage() { + mkdirSync(path.dirname(AUTH_STATE), { recursive: true }); + const user = process.env.TEST_USER; + const pass = process.env.TEST_PASS; + if (!user || !pass) { + throw new Error('未配置 TEST_USER/TEST_PASS,请复制 .env.example 为 .env 后填写测试账号'); + } + + const browser = await chromium.launch({ headless: true, channel: 'chrome' }); + const context = await browser.newContext({ + viewport: { width: 1920, height: 1080 }, + locale: 'zh-CN', + storageState: existsSync(AUTH_STATE) ? AUTH_STATE : undefined, + }); + const page = await context.newPage(); + await page.goto(`${BASE}/spd/home/index`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.waitForTimeout(2000); + if (!page.url().includes('/login')) { + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Auth state OK:', AUTH_STATE); + return; + } + + await page.goto(`${BASE}/spd/login`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.locator('#username, input[name="username"], input[placeholder*="用户"]').first().fill(user); + await page.locator('#password, input[type="password"]').first().fill(pass); + await Promise.all([ + page.waitForURL((url) => url.pathname.includes('/spd/home'), { timeout: 120_000 }), + page.getByRole('button', { name: /登\s*录/ }).click(), + ]).catch(async () => { + for (let i = 0; i < 60; i += 1) { + if (page.url().includes('/spd/home')) return; + await page.waitForTimeout(1000); + } + throw new Error(`登录后未进入首页,当前 URL: ${page.url()}`); + }); + await page.waitForTimeout(1500); + await context.storageState({ path: AUTH_STATE }); + await browser.close(); + console.log('Logged in and saved auth:', AUTH_STATE); +} + +async function expandGroupMenu(page, groupName) { + await page.getByRole('menuitem', { name: '基础设置' }).click({ timeout: 20_000 }); + await page.waitForTimeout(500); + const title = page.locator('.ant-menu-submenu-title').filter({ hasText: new RegExp(`^${groupName}$`) }).first(); + if (await title.isVisible().catch(() => false)) { + const expanded = await title + .locator('xpath=..') + .evaluate((el) => el.classList.contains('ant-menu-submenu-open')) + .catch(() => false); + if (!expanded) await title.click({ timeout: 10_000 }); + await page.waitForTimeout(400); + } +} + +async function discoverMenuUrls(menus) { + const browser = await chromium.launch({ headless: true, channel: 'chrome' }); + const context = await browser.newContext({ + storageState: AUTH_STATE, + viewport: { width: 1920, height: 1080 }, + locale: 'zh-CN', + }); + const page = await context.newPage(); + const urls = {}; + + for (const item of menus) { + await page.goto(`${BASE}/spd/home/index`, { waitUntil: 'domcontentloaded', timeout: 120_000 }); + await page.waitForTimeout(1500); + if (page.url().includes('/login')) { + throw new Error(`登录态失效,无法打开${item.group}菜单`); + } + await expandGroupMenu(page, item.group); + await page + .locator('.ant-menu-item') + .filter({ hasText: new RegExp(`^\\s*${item.menu}\\s*$`) }) + .first() + .click({ timeout: 20_000 }); + await page.waitForTimeout(2000); + const url = page.url().split('?')[0]; + if (url.includes('/login') || url.endsWith('/spd/home/index')) { + throw new Error(`未能进入 ${item.menu},当前 URL: ${page.url()}`); + } + urls[item.pageKey] = url; + console.log(`URL ${item.menu}: ${url}`); + } + + await browser.close(); + mkdirSync(OUT_DIR, { recursive: true }); + writeFileSync(URL_MAP_OUT, JSON.stringify({ base: BASE, scannedAt: new Date().toISOString(), urls }, null, 2), 'utf8'); + console.log('Wrote URL map:', URL_MAP_OUT); + return urls; +} + +function pageCfg(targetUrl, item) { + return { + targetUrl, + useIframe: false, + iframeMatch: '', + iframeLabel: 'main', + module: 'spd-basic', + pageKey: item.pageKey, + pageTitle: item.pageTitle, + jsonOut: path.join(OUT_DIR, `${item.pageKey}-elements.json`), + yamlOut: path.join(LOC_DIR, `${item.pageKey}.locator.yaml`), + jsonSourceName: `${item.pageKey}-elements.json`, + waitSelectors: COMMON_WAIT, + waitSelector: '.ant-form-item input, .ant-table, .ant-btn, .ant-form, .ant-list-item', + waitUntil: 'domcontentloaded', + expandFirstRow: false, + openAdvancedFilter: false, + skipQueryClick: item.skipQueryClick === true, + interactivePhases: item.interactivePhases, + }; +} + +async function main() { + const filter = process.argv.slice(2); + const menus = filter.length ? MENUS.filter((p) => filter.includes(p.pageKey)) : MENUS; + if (!menus.length) { + console.error('No pages matched. Available:', MENUS.map((p) => p.pageKey).join(', ')); + process.exit(1); + } + + await ensureAuthStorage(); + const urls = await discoverMenuUrls(menus); + const summary = []; + + for (const item of menus) { + const targetUrl = urls[item.pageKey]; + console.log(`\n=== Scanning ${item.pageKey} ${targetUrl} ===`); + try { + const result = await runScan(pageCfg(targetUrl, item)); + const yaml = writeYamlFromScanJson(result.jsonOut, { + pageKey: item.pageKey, + pageTitle: item.pageTitle, + module: 'spd-basic', + jsonSourceName: `${item.pageKey}-elements.json`, + iframeMatch: '', + yamlOut: path.join(LOC_DIR, `${item.pageKey}.locator.yaml`), + }); + summary.push({ + page: item.pageKey, + menu: item.menu, + specId: item.specId, + ok: true, + url: targetUrl, + elementCount: result.elementCount, + jsonOut: result.jsonOut, + yamlOut: yaml.yamlOut, + }); + } catch (err) { + console.error(`FAILED ${item.pageKey}:`, err); + summary.push({ + page: item.pageKey, + menu: item.menu, + specId: item.specId, + ok: false, + error: String(err), + }); + } + } + + console.log('\n========== SUMMARY =========='); + for (const row of summary) { + if (row.ok) { + console.log(`✓ ${row.page}: ${row.elementCount} elements ${row.url}`); + console.log(` JSON: ${row.jsonOut}`); + console.log(` YAML: ${row.yamlOut}`); + } else { + console.log(`✗ ${row.page}: ${row.error}`); + } + } + mkdirSync(OUT_DIR, { recursive: true }); + writeFileSync(SUMMARY_OUT, JSON.stringify(summary, null, 2), 'utf8'); + if (summary.some((r) => !r.ok)) process.exit(1); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/sync-docs-ui-specs.py b/scripts/sync-docs-ui-specs.py index 6b3f118..1422882 100644 --- a/scripts/sync-docs-ui-specs.py +++ b/scripts/sync-docs-ui-specs.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""将 docs/统计报表-* 截图同步至 knowledge-base/08-UI专项规范库。""" +"""将 docs/ 下页面截图同步至 knowledge-base/08-UI专项规范库(统计报表、准入、厂商、用户、库房、流程等)。""" from __future__ import annotations @@ -13,7 +13,7 @@ DOCS = ROOT / "docs" KB = ROOT / "knowledge-base" / "08-UI专项规范库" TODAY = date.today().isoformat() -PAGES: list[dict] = [ +STAT_PAGES: list[dict] = [ # 对账报表 {"id": "RECON-001", "group": "对账报表", "name": "科室对账查询", "folder": "统计报表-对账报表-科室对账查询"}, {"id": "RECON-002", "group": "对账报表", "name": "科室净入汇总表", "folder": "统计报表-对账报表-科室净入汇总表"}, @@ -79,12 +79,716 @@ PAGES: list[dict] = [ {"id": "REAG-001", "group": "试剂批号统计", "name": "试剂批号统计明细", "folder": "统计报表-试剂批号统计-试剂批号统计明细"}, ] +# 基础设置 → 准入管理(docs/基础管理-准入管理-*) +ADMIT_PAGES: list[dict] = [ + { + "id": "ADMIT-001", + "name": "物料准入管理", + "folder": "基础管理-准入管理-物料准入管理", + "filters": "新增日期(区间)、进院供应商、耗材类型、产品名称、规格型号、注册证号、器械注册人", + "status_tabs": "全部、待申请、审批中、已通过、已驳回、已作废、生效失败、已生效", + "toolbar": "**关联产品**、**生成申请单**、**一键生成**、**作废**;列设置", + "list_note": "含准入类型(新品入院/产品增规/C码变更等)、产品编号/名称、规格型号、器械注册人、耗材类型、默认进院供应商;行操作 **详情**、**日志**、**编辑**(视状态)", + }, + { + "id": "ADMIT-002", + "name": "产品准入申请", + "folder": "基础管理-准入管理-产品准入申请", + "filters": "以截图为准(常见:日期、供应商、产品/规格、单号等)", + "status_tabs": "以截图状态 Tab 为准", + "toolbar": "以截图工具栏为准", + "list_note": "产品准入申请列表与行内操作以截图为准", + }, + { + "id": "ADMIT-003", + "name": "物料准入单列表", + "folder": "基础管理-准入管理-物料准入单列表", + "filters": "以截图为准", + "status_tabs": "以截图状态 Tab 为准", + "toolbar": "以截图工具栏为准", + "list_note": "物料准入单列表、详情/审批相关操作以截图为准", + }, + { + "id": "ADMIT-004", + "name": "产品准入单列表", + "folder": "基础管理-准入管理-产品准入单列表", + "filters": "以截图为准", + "status_tabs": "以截图状态 Tab 为准", + "toolbar": "以截图工具栏为准", + "list_note": "产品准入单列表与行内操作以截图为准", + }, + { + "id": "ADMIT-005", + "name": "供货目录申请", + "folder": "基础管理-准入管理-供货目录申请", + "filters": "制单日期(区间)、进院供应商、二级供应商、变更单号、产品名称、规格型号", + "status_tabs": "全部、待提交、审批中、已通过、已驳回", + "toolbar": "**批量提交**、**新增**;列设置", + "list_note": "申请单号、申请人、申请时间、进院/二级供应商、业务模式、是否默认、备注;行操作 **编辑**、**详情**", + }, + { + "id": "ADMIT-006", + "name": "准入任务分配", + "folder": "基础管理-准入管理-准入任务分配", + "filters": "以截图为准", + "status_tabs": "以截图为准(若有)", + "toolbar": "以截图工具栏为准", + "list_note": "准入任务分配列表与分配操作以截图为准", + }, +] + +# 基础设置 → 厂商管理(docs/基础管理-厂商管理-*) +VENDOR_PAGES: list[dict] = [ + { + "id": "VENDOR-001", + "name": "厂商查询", + "folder": "基础管理-厂商管理-厂商查询", + "filters": "厂商名称(搜索框)", + "status_tabs": "器械注册人、经销商", + "toolbar": "搜索、列设置、刷新/导出(以截图图标为准)", + "list_note": "列:序号、厂商编号、厂商名称、产地、详细地址、简称、开户银行、银行账号;底部分页", + "extra_imgs": 1, + }, + { + "id": "VENDOR-002", + "name": "供应商资质管理", + "folder": "基础管理-厂商管理-供应商资质管理", + "filters": "随证件 Tab 变化(如进院供应商名称、证件类型;授权链路:授权书名称/授权单位/被授权单位)", + "status_tabs": ( + "证件 Tab:证件效期提醒、授权链路效期预警、供应商三证、器械注册人、器械注册证号、" + "授权书、承诺书、法人委托书、二级供应商三证;效期快捷:近三个月到期、近一个月到期、已过期(带计数)" + ), + "toolbar": "**导出** / **导出明细**(视 Tab);列设置", + "list_note": "证件效期提醒列含数据来源、进院供应商、有效期至、证件名称/类型、效期提醒、查看;授权链路预警列含授权单位、授权书名称、被授权单位、效期起止、效期提醒、查看", + "extra_imgs": 8, + }, + { + "id": "VENDOR-003", + "name": "授权变更初审", + "folder": "基础管理-厂商管理-授权变更初审", + "filters": "制单日期(区间)、单号、进院供应商、预转供应商、状态、变更类型、原供应商、医院ID、医院名称", + "status_tabs": "无独立状态 Tab(状态下拉筛选)", + "toolbar": "查询、重置、收起/展开", + "list_note": "列:进院供应商名称、单号、状态、制单时间、制单人、变更类型、原供应商、预转供应商;行操作 **详情**", + }, + { + "id": "VENDOR-004", + "name": "授权变更终审", + "folder": "基础管理-厂商管理-授权变更终审", + "filters": "制单日期(区间)、单号、进院供应商、预转供应商、状态、变更类型、原供应商", + "status_tabs": "无独立状态 Tab(状态下拉筛选)", + "toolbar": "查询、重置、收起/展开", + "list_note": "列与初审类似:进院供应商名称、单号、状态、制单时间、制单人、变更类型、原/预转供应商;行操作 **详情**", + }, + { + "id": "VENDOR-005", + "name": "注册证变更初审", + "folder": "基础管理-厂商管理-注册证变更初审", + "filters": "制单日期(区间)、单号、变更类型、注册证号(新)、状态、变更情况具体说明", + "status_tabs": "无独立状态 Tab(状态下拉筛选)", + "toolbar": "查询、重置、收起/展开", + "list_note": "列:进院供应商名称、单号、状态、制单时间、制单人、变更类型、注册证号(新);行操作 **详情**", + }, + { + "id": "VENDOR-006", + "name": "注册证变更终审", + "folder": "基础管理-厂商管理-注册证变更终审", + "filters": "制单日期(区间)、单号、变更类型、注册证号(新)、状态", + "status_tabs": "无独立状态 Tab(状态下拉筛选)", + "toolbar": "查询、重置、收起/展开", + "list_note": "列与初审类似:进院供应商名称、单号、状态、制单时间、制单人、变更类型、注册证号(新);行操作 **详情**", + }, + { + "id": "VENDOR-007", + "name": "注册证差异对比", + "folder": "基础管理-厂商管理-注册证差异对比", + "filters": ( + "物料名称、规格型号、状态、望海注册证号、是否医疗器械、生产厂家、FLI+注册证号、" + "27位医保编码;可选「包含FLI+长期有效数据」" + ), + "status_tabs": "全部、一致、不一致", + "toolbar": "**导出**;数据更新时间 + **立即更新**;列设置", + "list_note": ( + "对比望海 vs FLI+:物料编号/名称、规格型号、生产厂家、27位医保编码、" + "两侧注册证号/生效日期/有效期至;宽表横向滚动" + ), + }, + { + "id": "VENDOR-008", + "name": "评价方案维护", + "folder": "基础管理-厂商管理-评价方案维护", + "filters": "创建日期(区间)、启用日期(区间)、方案编码、方案名称", + "status_tabs": "无", + "toolbar": "**新增**;列设置", + "list_note": "列:方案编码、方案名称、创建人/时间、启用人/时间、停用人/时间、状态、备注、操作", + }, + { + "id": "VENDOR-009", + "name": "评价指标维护", + "folder": "基础管理-厂商管理-评价指标维护", + "filters": "左侧指标分类树搜索;右侧:指标编码、指标名称", + "status_tabs": "无(左树 + 右列表)", + "toolbar": "左树 **新增**;右表 **新增**;列设置", + "list_note": "左:指标分类树(可编辑);右列:评价类型、指标编码、指标名称、状态;行操作 **编辑**、**详情**、**日志**", + }, + { + "id": "VENDOR-010", + "name": "供应商节假日管理", + "folder": "基础管理-厂商管理-供应商节假日管理", + "filters": "左侧:名称/年度搜索", + "status_tabs": "无(左列表 + 右详情)", + "toolbar": "左:**新增**;右:供应商自定义列表 **新增**、**批量删除**", + "list_note": ( + "左:供应商节假日设置(年度、名称、编辑);右上:默认假日详情(名称/年度/起止/创建人时间);" + "右下:供应商自定义节假日(进院供应商编码/名称、起止、备注、创建人时间、操作)" + ), + }, +] + +# 基础设置 → 用户管理(docs/基础管理-用户管理-*) +USER_PAGES: list[dict] = [ + { + "id": "USER-001", + "name": "用户管理", + "folder": "基础管理-用户管理-用户管理", + "path": "/spd/basic/user/platform/person", + "tab_title": "用户管理", + "layout": "整页单表:顶部筛选区 + 工具栏 + 用户列表 + 底部分页(无左侧组织树)", + "filters": [ + ("姓名", "文本", "占位「姓名、登录账号」"), + ("科室", "下拉(可搜索)", "请选择"), + ("角色", "下拉(可搜索)", "请选择"), + ], + "actions": "**重置**、**高级筛选**(`下高级筛选` 收起/展开);本页截图未见独立「查询」按钮", + "toolbar": "页签右侧 **新增用户** 图标按钮(按实际文案定位)", + "columns": [ + ("序号", "行号(可勾选)"), + ("姓名", "用户真实姓名"), + ("登录账号", "登录用户名"), + ("联系方式", "手机号/电话,可能为非法占位值(如 `1`、`0922`)"), + ("所属科室", "科室名称,未分配显示 `--`"), + ("医院员工编号", "工号,未填显示 `--`"), + ("角色", "已分配角色,多角色截断显示(如 `内网医院初始化模板角色 (…`)"), + ("科室任职数", "该用户在各科室的任职条数,无则 `--`"), + ("操作", "**编辑**、**授权**、**重置密码**"), + ], + "row_ops": "**编辑**、**授权**、**重置密码**", + "paging": "「共 **142** 条」、页码 `1 2 3 >`、**50 条/页**、**跳至 N 页**", + "scan_key": "basic-user", + "po_hint": "pages/spd/basic-user.page.ts", + "notes": [ + "菜单导航:`page.getByRole('menuitem', { name: '用户管理', exact: true }).click()` 存在歧义(一级菜单名同为「用户管理」),建议直接 `openPath('/spd/basic/user/platform/person')` 并断言 URL。", + "本页**无「所属医院」筛选/列**;与医共体用户管理页列顺序不同,勿复用同一套列断言。", + "行操作 **授权**、**重置密码** 均为写操作,须先确认当前行。", + ], + "points": [ + "姓名/科室/角色组合筛选后列表条数与「共 N 条」一致;重置后恢复默认。", + "「角色」多角色截断显示(如 `内网医院初始化模板角色 (…`),编辑页应能查看全部角色。", + "「科室任职数」与用户在各科室任职记录条数一致,无任职显示 `--`。", + "新增/编辑:必填为空拦截;取消不保存;编辑回填与原值一致。", + "授权弹窗保存后重新打开应保留勾选;取消不生效。", + ], + }, + { + "id": "USER-002", + "name": "医共体用户管理", + "folder": "基础管理-用户管理-医共体用户管理", + "path": "/spd/basic/user/platform/personCom", + "tab_title": "医共体用户管理", + "layout": "整页单表,结构与「用户管理」一致,差异在引入**所属医院**维度(医共体多院区统一查看)", + "filters": [ + ("姓名", "文本", "占位「姓名、登录账号」"), + ("所属医院", "下拉(可搜索)", "默认已带医院,如 `邹城市人民医院(test)`"), + ("联系方式", "文本", "占位「联系方式」"), + ], + "actions": "**重置**、**高级筛选**;本页截图未见独立「查询」按钮", + "toolbar": "页签右侧 **新增** 图标按钮(按实际文案定位)", + "columns": [ + ("序号", "行号(可勾选)"), + ("登录账号", "列宽较窄,长账号截断显示(如 `zhon.…`、`jiang.…`)"), + ("姓名", "用户真实姓名"), + ("联系方式", "手机号/电话,可能为非法占位值"), + ("所属医院", "医共体下所属医院,如 `邹城市人民医院(test)`"), + ("所属科室", "科室名称,未分配显示 `--`"), + ("医院员工编号", "工号,未填 `--`"), + ("角色", "已分配角色,多角色截断显示"), + ("科室任职数", "截断表头(截图显示 `科'`),与用户管理页同义"), + ("操作", "**编辑**、**授权**、**重置密码**"), + ], + "row_ops": "**编辑**、**授权**、**重置密码**", + "paging": "「共 **142** 条」、页码 `1 2 3 >`、**50 条/页**、**跳至 N 页**(与用户管理页数据量一致,两页为同一用户池)", + "scan_key": "basic-user-union", + "po_hint": "pages/spd/basic-user-union.page.ts", + "notes": [ + "菜单导航:`page.getByRole('menuitem', { name: '医共体用户管理', exact: true }).click()`;或 `openPath('/spd/basic/user/platform/personCom')` 断言 URL。", + "**勿与用户管理页混用列断言**:本页为 `登录账号 → 姓名 → 联系方式 → 所属医院 → 所属科室 → 医院员工编号 → 角色 → 科室任职数 → 操作`,用户管理页为 `姓名 → 登录账号 → …`(无「所属医院」)。", + "「登录账号」列窄,`cellText` 取到的是截断文本;按账号搜索/断言应使用筛选框而非列文本。", + "写操作须先定位当前行,医共体下避免误改其他院区用户。", + ], + "points": [ + "切换「所属医院」后列表只显示该院区用户,「共 N 条」同步变化。", + "两页同一用户的关键字段(姓名、登录账号、角色、所属科室)一致,仅多出「所属医院」维度。", + "姓名 + 联系方式组合筛选精确命中;重置恢复默认。", + "写操作(编辑/授权/重置密码)须先定位当前行,医共体下避免误改其他院区用户。", + ], + }, + { + "id": "USER-003", + "name": "角色管理", + "folder": "基础管理-用户管理-角色管理", + "path": "/spd/basic/user/platform/role", + "tab_title": "", + "layout": ( + "左右分栏:左侧角色列表,右侧「菜单权限」勾选树(应用 `SPD内网系统PC端` → 模块 → 菜单/功能,三级结构)" + ), + "filters": [ + ("角色名称", "文本(左侧列表顶部)", "占位含「角色」,输入即过滤"), + ], + "actions": "左侧搜索框实时过滤;右侧权限树按模块展开勾选;底部 **收起导航** 为全局导航按钮,非本页功能", + "toolbar": "左侧角色行内 **编辑** / 删除类图标按钮(右上角图标按钮按实际文案定位)", + "columns": [ + ("角色名称", "左列主键;示例:医院管理员、中心库库管、护士、护士长、库管、测试01、test、验收-herp、验收-ict、角色测试123"), + ("操作", "行内 **编辑** / 删除类图标按钮"), + ], + "row_ops": "**编辑** / 删除类图标按钮;右侧为菜单权限树(非行操作)", + "paging": "左列底部 **收起导航**(全局按钮);未见独立分页控件", + "scan_key": "basic-role", + "po_hint": "pages/spd/basic-role.page.ts", + "notes": [ + "菜单导航:`page.getByRole('menuitem', { name: '角色管理', exact: true }).click()`;或 `openPath('/spd/basic/user/platform/role')` 断言 URL。", + "左右分栏:先断言角色列表与权限树均可见;选中角色后再断言右侧勾选状态已刷新(避免读到上一个角色的树)。", + "权限树按模块展开,断言时优先定位「模块名 + 菜单名」层级,避免全局文本匹配到多份同名菜单。", + "授权保存属写操作:测试账号权限变更会影响其他用例,执行前记录原勾选,结束后还原。", + ], + "points": [ + "角色搜索过滤结果与关键字匹配;清空后恢复全量。", + "切换角色后右侧菜单权限勾选与该角色已配置权限一致(无串选/残留)。", + "父节点勾选/取消后子节点全选/全不选;部分勾选时父节点为半选态。", + "保存授权后重新进入页面,勾选状态持久化;取消不保存。", + "角色增删改后,用户管理页「角色」下拉与用户行「角色」列同步更新。", + ], + "modules": [ + ("基础设置", "医院资料、医院信息登记、院区管理、公告管理、财务月份设置"), + ( + "运维管理", + "菜单控制规则设置、医院配置、菜单受控列表、院区配置、科室配置、术语配置、资源模板设置、" + "字典管理、打印机配置、编码规则管理、PDA授权、PDA版本管理、缓存监控、操作日志、" + "消息通知模板、消息通知设置、功能注册、任务执行记录查询", + ), + ( + "厂商管理", + "厂商查询、供应商资质管理、授权变更初审、注册证变更初审、注册证变更终审、授权变更终审、" + "注册证差异对比、评价方案维护、评价指标维护、供应商节假日管理", + ), + ( + "产品管理", + "产品核对重新发起、产品维护、物料信息管理、产品初审核对、产品供应关系维护、产品物价核对、" + "院区产品设置、院区单元包设置、中心库上下限建议(AI)、请领出库渠道设置、结算点设置、" + "调价管理、产品信息变更、变更单明细查询、外围系统产品新增、外围系统产品变更管理、产品查询 等", + ), + ], + }, +] + +# 基础设置 → 库房管理(docs/基础管理-库房管理-*) +# 菜单另有「华清通道」,本次无截图,不建规范。 +WH_PAGES: list[dict] = [ + { + "id": "WH-001", + "group": "库房管理", + "name": "库区设置", + "folder": "基础管理-库房管理-库区设置", + "path": "/spd/basic/storehouse/platform/storehouse/area", + "siblings": "**库区设置** / **探针绑定**", + "tab_title": "库区设置", + "layout": "左右分栏:左侧「库房信息」树,右侧为当前库房下的库区列表", + "filters": [ + ("库房名称", "文本(左侧树顶部)", "占位「库房名称」,过滤库房树"), + ("库区编号/名称", "文本(右侧列表右上)", "占位「库区编号/名称」,搜索图标触发"), + ], + "actions": "左侧 **同步**;右侧搜索图标检索,另有刷新与列设置。截图未见独立「查询」按钮", + "toolbar": "右侧 **新增**", + "columns": [ + ("库区编号", "如 `A001`、`B001`、`C001`、`E001`、`0807`"), + ("库区名称", "如「中心库A区」;默认库区在名称旁带绿色「默认库区」标签(截图为中心库B区)"), + ("库区地址", "自由文本,可与编号或名称相同"), + ("是否专项库区", "是 / 否"), + ("操作", "**编辑**、**人员管理**、**货位管理**、**停用**或**启用**"), + ], + "row_ops": "**编辑**、**人员管理**、**货位管理**、**停用** / **启用**", + "paging": "右侧「共 **5** 条」、页码 `1`、**50 条/页**(条数随所选库房变化)", + "scan_key": "warehouse-zone", + "po_hint": "pages/spd/warehouse-zone.page.ts", + "notes": [ + "菜单导航:`page.getByRole('menuitem', { name: '库区设置', exact: true }).click()`;或 `openPath('/spd/basic/storehouse/platform/storehouse/area')` 并断言 URL。", + "先点选左侧库房,再断言右侧列表;切换库房后列表须刷新,避免读到上一库房的库区。", + "**停用** / **启用**、**同步**、**新增** 均为写操作。默认库区是名称旁标签,不是独立列。", + "「人员管理」「货位管理」会离开当前列表(弹层或子页),本次无截图,断言前先确认面板标题。", + ], + "points": [ + "左侧库房关键字过滤只命中名称匹配的节点;清空后恢复全量树。", + "切换库房后,右侧库区编号/名称/地址属于该库房,「共 N 条」同步变化。", + "「库区编号/名称」搜索同时覆盖编号与名称;无命中时列表为空且分页为 0。", + "同一库房仅一个「默认库区」标签;专项库区「是/否」与编辑回填一致。", + "停用后该行操作变为「启用」,再次进入仍保持;启用行显示「停用」。", + ], + "extra_md": """ +### 左侧库房树 + +标题 **库房信息**,操作 **同步**。截图节点示例:光谷院区中心库房(当前选中)、洪山院区中心库房、1205院区中心库房、结算院区中心库房、回归测试院区中心库房、千泉山院区中心库房、马王堆院区中心库房、主院区中心库房、岳麓山院区中心库房、数据检查院区中心库房、东土城院区中心库房、智能院区中心库房、自动采购/请领建议专用院区中心库房。 + +选中库房后,右侧列表只展示该库房的库区。 +""", + }, + { + "id": "WH-002", + "group": "库房管理", + "name": "探针绑定", + "folder": "基础管理-库房管理-探针绑定", + "path": "/spd/basic/storehouse/storehouse/probe", + "siblings": "**库区设置** / **探针绑定**", + "tab_title": "探针绑定", + "shot_note": "`UI-SPEC-SPD-WH-002/page-main.png`、`page-main-2.png`(货位绑定弹窗)", + "layout": "整页单表:顶部筛选 + 探针列表;行内「绑定货位」打开「货位绑定探针」弹窗", + "filters": [ + ("探针ID", "文本", "占位「请输入」"), + ("主机编码", "文本", "占位「请输入」"), + ("是否绑定货位", "下拉", "占位「请选择」(已绑定 / 未绑定)"), + ], + "actions": "**查询**、**重置**", + "toolbar": "列设置(截图未见新增按钮)", + "columns": [ + ("序号", "行号"), + ("探针ID", "如 `360001`–`360011`"), + ("主机编码", "如 `X233300001`,多条探针可共用同一主机"), + ("是否绑定货位", "已绑定 / 未绑定"), + ("绑定的货位", "已绑定货位数量(如 `28`、`3`、`2`);未绑定为 `0`"), + ("操作", "**绑定货位**"), + ], + "row_ops": "**绑定货位**(打开货位选择弹窗)", + "paging": "主列表「共 **11** 条」、**50 条/页**(以当前环境为准)", + "scan_key": "warehouse-probe", + "po_hint": "pages/spd/warehouse-probe.page.ts", + "notes": [ + "菜单导航:`page.getByRole('menuitem', { name: '探针绑定', exact: true }).click()`;或 `openPath('/spd/basic/storehouse/storehouse/probe')` 并断言 URL。", + "「绑定的货位」是数量,不是货位号;货位号在弹窗「货位号」列。", + "弹窗有独立筛选与分页(截图「共 2626 条」),不要用弹窗总条数断言主列表。", + "弹窗 **确定** 会改绑定关系,按钮文案带已选数量(如 `确定(0)`);冒烟只打开后 **取消**。", + ], + "points": [ + "探针ID、主机编码、是否绑定货位组合查询后,列表与「共 N 条」一致;重置恢复默认。", + "「已绑定」行的绑定数量大于 0,「未绑定」行数量为 0。", + "打开「绑定货位」后弹窗标题为「货位绑定探针」,关闭后主列表勾选与筛选条件不丢失。", + "弹窗内按当前库房/库区/货位号筛选,结果列与条件一致;未选货位时确定按钮计数为 0。", + "同一货位行若已有探针ID,再次勾选前需确认是换绑还是追加(以保存后的主列表数量为准)。", + ], + "extra_md": """ +### 货位绑定探针弹窗 + +![探针绑定-货位绑定探针](UI-SPEC-SPD-WH-002/page-main-2.png) + +行内 **绑定货位** 打开。筛选与主列表相互独立。 + +| 字段 | 控件 | 占位/默认值 | +|------|------|-------------| +| 当前库房 | 下拉 | 可带默认库房,如「千泉山院区中心…」 | +| 当前库区 | 下拉 | 空 | +| 货位查询 | 文本 | 占位「请输入」 | +| 货位状态 | 下拉 | 空 | +| 默认货位 | 下拉 | 空 | + +- 操作:**查询**、**重置** + +| 列 | 说明 | +|----|------| +| 勾选 | 多选;底部 **确定(N)** 的 N 为已选货位数 | +| 货位号 | 如 `001-01-01-01`、`001-ZNG001-01-01` | +| 库区默认货位 | 是 / 否 | +| 当前库房 | 如「千泉山院区中心库房」 | +| 当前库区 | 如「千泉山院区中心库库区1」 | +| 状态 | 启用等 | +| 探针ID | 已绑定显示探针号(如 `360003`),未绑定 `--` | + +弹窗分页截图为「共 **2626** 条」、**50 条/页**、**跳至 N 页**。底部 **取消**、**确定(0)**。 +""", + }, +] + +# 基础设置 → 流程设置(docs/基础管理-流程设置-*) +FLOW_PAGES: list[dict] = [ + { + "id": "FLOW-001", + "group": "流程设置", + "name": "审批流程设置", + "folder": "基础管理-流程设置-审批流程设置", + "path": "/spd/basic/flowSet/workflow/flowSet", + "siblings": "**审批流程设置** / **审批流实例**", + "tab_title": "审批流程设置", + "layout": "左右分栏:左侧业务类型列表,右侧为该类型的默认审批流与自定义审批流", + "filters": [ + ("流程名称", "文本(左侧列表顶部)", "占位「流程名称」,过滤业务类型"), + ], + "actions": "左侧搜索过滤类型;右侧 **新增流程**、底部 **保存** / **重置**", + "toolbar": "自定义审批流区域右上 **新增流程**", + "columns": [ + ("流程名称", "必填;自定义流名称,截图示例 `1`、`2`"), + ("条件", "必填;行内 **编辑**。示例:「是否超限:是」「科室:中心库(洪山)」"), + ("审批流程", "必填;节点卡片(节点类型、账号、人员)+ **+** 追加节点"), + ("操作", "**删除**"), + ], + "row_ops": "条件列 **编辑**;审批节点 **+**;行 **删除**", + "paging": "左侧为业务类型全量列表,未见分页;右侧自定义流按行展示,未见分页", + "scan_key": "flow-def", + "po_hint": "pages/spd/flow-def.page.ts", + "notes": [ + "菜单导航:`page.getByRole('menuitem', { name: '审批流程设置', exact: true }).click()`;或 `openPath('/spd/basic/flowSet/workflow/flowSet')` 断言 URL。与「审批流实例」区分,使用 exact。", + "先选中左侧业务类型,再读右侧默认流与自定义流;切换类型后两侧都要刷新,避免串用上一类型的节点。", + "**保存** 会改变线上审批路由,冒烟只断言设计器结构,不提交;改完须 **重置** 或恢复原配置。", + "节点卡片含「节点 / 账号 / 人员」三行,断言时按卡片容器取文本,避免全局匹配到同名人员。", + ], + "points": [ + "流程名称关键字只保留匹配的业务类型;清空后恢复全量。", + "切换业务类型后,默认审批流节点与自定义审批流行与该类型已保存配置一致。", + "自定义流「流程名称 / 条件 / 审批流程」为空时保存被拦截;删除行后未点保存不落库。", + "重置放弃未保存改动,重新进入与上次保存结果一致。", + "默认流与自定义流互不覆盖:只改自定义行时,默认审批人节点保持不变。", + ], + "extra_md": """ +### 左侧业务类型 + +截图可见类型(采购计划为当前选中,列表可继续向下滚动):采购计划、结算、调价、产品信息变更、请领单、领用申请单、跟台手术申请单、试剂性能验证、退库出库单、临采任务、二级库调拨、备货计划、产品准入、供货目录申请、移库申请单、物料准入、退货出库拣货、产品DI变更、科室消耗退回、限量任务、科室领用退库出库单。 + +### 默认审批流 + +标题 **默认审批流**。审批人节点卡片示例:节点「运营主管」、账号 `test_1`、人员「蒋华」,卡片右侧 **+** 追加节点。未配置自定义流时,单据走该默认链。 + +### 自定义审批流 + +标题 **自定义审批流**。列头「流程名称」「条件」「审批流程」带必填标记。节点示例: + +| 行 | 条件 | 节点 | +|----|------|------| +| 1 | 是否超限:是 | 指定用户 / `cgzxtest01` / 采购中心管理员01 | +| 2 | 科室:中心库(洪山) | 指定用户 / `qizhenjun` / 齐-超管 | + +底部 **保存**、**重置**。 +""", + }, + { + "id": "FLOW-002", + "group": "流程设置", + "name": "审批流实例", + "folder": "基础管理-流程设置-审批流实例", + "path": "/spd/basic/flowSet/workflow/flowCase", + "siblings": "**审批流程设置** / **审批流实例**", + "tab_title": "审批流实例", + "layout": "整页单表:顶部筛选 + 审批单据列表 + 底部分页", + "filters": [ + ("单据编号", "文本", "占位「单据编号」"), + ("审批状态", "下拉", "空;列表可见「审批通过」「审批中」"), + ("发起时间", "日期区间", "截图示例 `2026-08-21` → `2026-10-20`(约两个月,结束日为当天)"), + ("业务类型", "下拉", "空;与审批流程设置左侧类型同源"), + ("摘要", "文本", "占位「摘要」"), + ], + "actions": "**查询**、**重置**", + "toolbar": "列设置(截图未见新增、导出)", + "columns": [ + ("序号", "行号"), + ("单据编号", "前缀随业务类型变化:`PLAN` 采购计划、`DB` 二级库调拨、`GC` 产品信息变更、`BUY` 请领单"), + ("业务类型", "如采购计划、二级库调拨、产品信息变更、请领单"), + ("发起人", "姓名,窄列截断"), + ("发起部门", "部门名称,窄列截断"), + ("发起时间", "日期时间,窄列截断为 `YYYY-MM-DD …`"), + ("摘要", "业务摘要;无则 `--`,长文本截断"), + ("审批状态", "审批通过 / 审批中(以页面枚举为准)"), + ("操作", "**详情**"), + ], + "row_ops": "**详情**(只读,打开该单据审批过程)", + "paging": "「共 **2348** 条」、页码、**50 条/页**、**跳至 N 页**(默认日期区间较大,条数随区间变化)", + "scan_key": "flow-inst", + "po_hint": "pages/spd/flow-inst.page.ts", + "notes": [ + "菜单导航:`page.getByRole('menuitem', { name: '审批流实例', exact: true }).click()`;或 `openPath('/spd/basic/flowSet/workflow/flowCase')` 断言 URL。不要点到「审批流程设置」。", + "默认发起时间跨度大,全量查询条数多;收窄日期后再断言「共 N 条」,且列表「发起时间」须落在区间内。", + "单据编号前缀与业务类型对应,按编号搜索时用完整单号,不用截断后的发起人/部门列文本。", + "本页行操作只有 **详情**;改审批人、条件应回到审批流程设置,且该页保存会影响后续新实例。", + ], + "points": [ + "单据编号精确查询至多一条,且编号、业务类型、状态与条件一致。", + "审批状态、业务类型、发起时间、摘要组合筛选后,抽样行同时满足全部条件。", + "重置后日期回到默认区间,其余条件清空,「共 N 条」恢复。", + "摘要为 `--` 的行,按非空摘要关键字查询不应命中。", + "详情打开的单据编号与当前行一致;关闭详情后筛选条件与页码保持。", + ], + "extra_md": "", + }, +] + +PAGES: list[dict] = STAT_PAGES + ADMIT_PAGES + VENDOR_PAGES + USER_PAGES + WH_PAGES + FLOW_PAGES + def png_files(folder: Path) -> list[Path]: return sorted(p for p in folder.iterdir() if p.suffix.lower() == ".png") -def render_spec(page: dict, spec_id: str) -> str: +def render_admit_spec(page: dict, spec_id: str) -> str: + title = f"准入管理-{page['name']}页面说明" + menu = f"**基础设置** → **准入管理** → **{page['name']}**" + docs_path = f"docs/{page['folder']}/" + kb_dir = f"UI-SPEC-SPD-{spec_id}" + extra_img = "" + if spec_id == "ADMIT-005": + extra_img = "\n\n![供货目录申请-补充截图](UI-SPEC-SPD-ADMIT-005/page-main-2.png)\n" + + return f"""--- +title: {title} +module: 基础设置 +status: 生效 +related_br: "" +related_api: "" +related_jrn: "" +related_tc: "" +related_def: "" +create_time: "{TODAY}" +update_time: "{TODAY}" +author: "" +--- + +# 准入管理 - {page['name']} + +> 来源:`{docs_path}` 页面截图。用于 UI 自动化定位、功能测试与准入流程验收参考。 +> 同模块索引见 [`INDEX.md`](./INDEX.md)。 + +## 文档与截图 + +| 项 | 路径 | +|----|------| +| 截图原件 | `{docs_path}` | +| 知识库副本 | `{kb_dir}/page-main.png` | + +## 页面路径 + +{menu} + +| 项 | 值 | +|----|-----| +| 主框架 URL | 待元素扫描补充(菜单名:`{page['name']}`) | +| iframe 选择器 | 无(整页渲染,非 iframe 报表) | +| 测试环境示例 | `http://spdtest.cmic.com.cn:8080/spd/form/` | + +## 主界面 + +![{page['name']}主界面]({kb_dir}/page-main.png){extra_img} + +### 筛选区 + +- 字段:{page.get('filters', '以截图为准')} +- 操作:**查询**、**重置**;部分页面含 **收起/展开** + +### 状态 Tab(若有) + +- {page.get('status_tabs', '以截图为准')} + +### 列表与工具栏 + +- {page.get('toolbar', '以截图为准')} +- {page.get('list_note', '主表列与行内操作以截图为准')} +- 底部分页:「共 N 条」、页码与每页条数切换 + +## 自动化建议 + +1. 完成专项元素扫描后,补充 `data/scan/`、`locators/spd/` 与 Page Object。 +2. 菜单导航:`page.getByRole('menuitem', {{ name: '{page['name']}', exact: true }}).click()`(注意与同组「产品/物料准入单列表」等近似菜单区分)。 +3. 状态 Tab 切换后列表与 Tab 计数一致;批量操作需先勾选行。 + +## 测试关注点 + +1. 筛选 + 状态 Tab 组合后,列表条数与底部分页「共 N 条」一致。 +2. 生成申请单/一键生成/批量提交等写操作与当前勾选行、筛选条件一致。 +3. 审批态行操作(编辑/详情/作废)与状态机规则一致。 +""" + + +def render_vendor_spec(page: dict, spec_id: str) -> str: + title = f"厂商管理-{page['name']}页面说明" + menu = f"**基础设置** → **厂商管理** → **{page['name']}**" + docs_path = f"docs/{page['folder']}/" + kb_dir = f"UI-SPEC-SPD-{spec_id}" + extra_n = int(page.get("extra_imgs") or 0) + extra_img = "" + for i in range(2, extra_n + 2): + extra_img += f"\n\n![{page['name']}-补充截图{i - 1}]({kb_dir}/page-main-{i}.png)\n" + + return f"""--- +title: {title} +module: 基础设置 +status: 生效 +related_br: "" +related_api: "" +related_jrn: "" +related_tc: "" +related_def: "" +create_time: "{TODAY}" +update_time: "{TODAY}" +author: "" +--- + +# 厂商管理 - {page['name']} + +> 来源:`{docs_path}` 页面截图。用于 UI 自动化定位、功能测试与厂商/资质流程验收参考。 +> 同模块索引见 [`INDEX.md`](./INDEX.md)。 + +## 文档与截图 + +| 项 | 路径 | +|----|------| +| 截图原件 | `{docs_path}` | +| 知识库副本 | `{kb_dir}/page-main.png` | + +## 页面路径 + +{menu} + +| 项 | 值 | +|----|-----| +| 主框架 URL | 待元素扫描补充(菜单名:`{page['name']}`) | +| iframe 选择器 | 无(整页渲染,非 iframe 报表) | +| 测试环境示例 | `http://spdtest.cmic.com.cn:8080/spd/form/` | + +## 主界面 + +![{page['name']}主界面]({kb_dir}/page-main.png){extra_img} + +### 筛选区 + +- 字段:{page.get('filters', '以截图为准')} +- 操作:**查询**、**重置**;部分页面含 **收起/展开** + +### 状态 / 分类 Tab(若有) + +- {page.get('status_tabs', '以截图为准')} + +### 列表与工具栏 + +- {page.get('toolbar', '以截图为准')} +- {page.get('list_note', '主表列与行内操作以截图为准')} +- 底部分页:「共 N 条」、页码与每页条数切换(左右分栏页面对应侧分页) + +## 自动化建议 + +1. 完成专项元素扫描后,补充 `data/scan/`、`locators/spd/` 与 Page Object。 +2. 菜单导航:`page.getByRole('menuitem', {{ name: '{page['name']}', exact: true }}).click()`(注意初审/终审、方案/指标等近似菜单区分)。 +3. 资质多 Tab、变更审批详情、左右分栏(指标树/节假日)需先断言目标面板可见再操作。 + +## 测试关注点 + +1. 筛选 + Tab/状态下拉组合后,列表条数与底部分页「共 N 条」一致。 +2. 授权/注册证初审与终审详情、状态流转与业务规则一致;写操作前确认勾选/当前行。 +3. 注册证差异对比「一致/不一致」与两侧字段抽样一致;导出与当前查询条件一致。 +""" + + +def render_stat_spec(page: dict, spec_id: str) -> str: title = f"{page['group']}-{page['name']}页面说明" menu = f"**统计报表** → **{page['group']}** → **{page['name']}**" docs_path = f"docs/{page['folder']}/" @@ -161,6 +865,251 @@ author: "" """ +def render_user_spec(page: dict, spec_id: str) -> str: + title = f"用户管理-{page['name']}页面说明" + menu = f"**基础设置** → **用户管理** → **{page['name']}**" + docs_path = f"docs/{page['folder']}/" + kb_dir = f"UI-SPEC-SPD-{spec_id}" + scan_key = page["scan_key"] + + tab_row = f"| 浏览器页签标题 | `{page['tab_title']}` |\n" if page.get("tab_title") else "" + # 第 1 条固定为「先扫描再补 PO」,其余建议顺延编号 + notes = "\n".join(f"{i}. {n}" for i, n in enumerate(page["notes"], start=2)) + points = "\n".join(f"{i}. {p}" for i, p in enumerate(page["points"], start=1)) + + filter_rows = "\n".join(f"| {f} | {c} | {d} |" for f, c, d in page["filters"]) + col_rows = "\n".join(f"| {name} | {desc} |" for name, desc in page["columns"]) + + modules = "" + if page.get("modules"): + rows = "\n".join(f"| {m} | {items} |" for m, items in page["modules"]) + modules = f""" +### 右侧菜单权限树 + +二级模块(示例): + +| 模块 | 菜单/功能(示例) | +|------|-------------------| +{rows} + +- 勾选父节点联动子节点,保存后即为该角色授权范围。 +""" + + return f"""--- +title: {title} +module: 基础设置 +status: 生效 +related_br: "" +related_api: "" +related_jrn: "" +related_tc: "" +related_def: "" +create_time: "{TODAY}" +update_time: "{TODAY}" +author: "" +--- + +# 用户管理 - {page['name']} + +> 来源:`{docs_path}` 页面截图。用于 UI 自动化定位、功能测试与用户/权限验收参考。 +> 同模块索引见 [`INDEX.md`](./INDEX.md)。 + +## 文档与截图 + +| 项 | 路径 | +|----|------| +| 截图原件 | `{docs_path}` | +| 知识库副本 | `{kb_dir}/page-main.png` | + +## 页面路径 + +{menu}(同组页面:**用户管理** / **医共体用户管理** / **角色管理**) + +| 项 | 值 | +|----|-----| +| 主框架 URL | `http://spdtest.cmic.com.cn:8080{page['path']}` | +{tab_row}| iframe 选择器 | 无(整页渲染,非 iframe 报表) | +| 元素扫描 JSON | 待扫描补充(建议 `data/scan/{scan_key}-elements.json`) | +| 定位器 YAML | 待扫描补充(建议 `locators/spd/{scan_key}.locator.yaml`) | +| 测试环境示例 | `http://spdtest.cmic.com.cn:8080/spd/form/` | + +## 主界面 + +![{page['name']}主界面]({kb_dir}/page-main.png) + +{page['layout']} + +### 筛选/搜索区 + +| 字段 | 控件 | 占位/默认值 | +|------|------|-------------| +{filter_rows} + +- 操作:{page['actions']} + +### 列表与工具栏 + +工具栏:{page.get('toolbar', '**新增**(按实际文案定位)')}。 + +| 列 | 说明 | +|----|------| +{col_rows} + +行操作:{page['row_ops']}。 + +### 分页 + +{page['paging']} +{modules} +## 自动化建议 + +1. 完成元素扫描后,补充 `data/scan/`、`locators/spd/` 与 Page Object(建议 `{page['po_hint']}`)。 +{notes} + +## 测试关注点 + +{points} + +## 关联 + +- 同模块:用户管理([`UI-SPEC-SPD-USER-001`](./UI-SPEC-SPD-USER-001.md))、医共体用户管理([`UI-SPEC-SPD-USER-002`](./UI-SPEC-SPD-USER-002.md))、角色管理([`UI-SPEC-SPD-USER-003`](./UI-SPEC-SPD-USER-003.md))。 +- 同层菜单:医院资料(`UI-SPEC-SPD-BASIC-*`)、厂商管理(`UI-SPEC-SPD-VENDOR-*`)、手术套包设置(`UI-SPEC-SPD-SURG-*`)。 +{page.get('related_extra', '')}""" + + +def render_group_spec(page: dict, spec_id: str) -> str: + """库房管理 / 流程设置等左右分栏或单表页面(结构对齐用户管理规范)。""" + group = page["group"] + title = f"{group}-{page['name']}页面说明" + menu = f"**基础设置** → **{group}** → **{page['name']}**" + docs_path = f"docs/{page['folder']}/" + kb_dir = f"UI-SPEC-SPD-{spec_id}" + scan_key = page["scan_key"] + siblings = page.get("siblings", f"**{page['name']}**") + shot = page.get("shot_note") or f"`{kb_dir}/page-main.png`" + + tab_row = f"| 浏览器页签标题 | `{page['tab_title']}` |\n" if page.get("tab_title") else "" + notes = "\n".join(f"{i}. {n}" for i, n in enumerate(page["notes"], start=2)) + points = "\n".join(f"{i}. {p}" for i, p in enumerate(page["points"], start=1)) + filter_rows = "\n".join(f"| {f} | {c} | {d} |" for f, c, d in page["filters"]) + col_rows = "\n".join(f"| {name} | {desc} |" for name, desc in page["columns"]) + extra = (page.get("extra_md") or "").strip() + extra = f"\n{extra}\n" if extra else "\n" + page_url = ( + f"`http://spdtest.cmic.com.cn:8080{page['path']}`" + if page.get("path") + else f"待元素扫描补充(菜单名:`{page['name']}`)" + ) + + if group == "库房管理": + peer = ( + "- 同模块:库区设置([`UI-SPEC-SPD-WH-001`](./UI-SPEC-SPD-WH-001.md))、" + "探针绑定([`UI-SPEC-SPD-WH-002`](./UI-SPEC-SPD-WH-002.md))。" + "菜单另有「华清通道」,本次无截图,未建规范。\n" + "- 同层菜单:医院资料(`UI-SPEC-SPD-BASIC-*`)、用户管理(`UI-SPEC-SPD-USER-*`)、" + "流程设置(`UI-SPEC-SPD-FLOW-*`)。" + ) + else: + peer = ( + "- 同模块:审批流程设置([`UI-SPEC-SPD-FLOW-001`](./UI-SPEC-SPD-FLOW-001.md))、" + "审批流实例([`UI-SPEC-SPD-FLOW-002`](./UI-SPEC-SPD-FLOW-002.md))。\n" + "- 同层菜单:库房管理(`UI-SPEC-SPD-WH-*`)、准入管理(`UI-SPEC-SPD-ADMIT-*`)、" + "用户管理(`UI-SPEC-SPD-USER-*`)。自定义审批流保存后,新单据出现在审批流实例。" + ) + + return f"""--- +title: {title} +module: 基础设置 +status: 生效 +related_br: "" +related_api: "" +related_jrn: "" +related_tc: "" +related_def: "" +create_time: "{TODAY}" +update_time: "{TODAY}" +author: "" +--- + +# {group} - {page['name']} + +> 来源:`{docs_path}` 页面截图。用于 UI 自动化定位、功能测试与{group}验收参考。 +> 同模块索引见 [`INDEX.md`](./INDEX.md)。 + +## 文档与截图 + +| 项 | 路径 | +|----|------| +| 截图原件 | `{docs_path}` | +| 知识库副本 | {shot} | + +## 页面路径 + +{menu}(同组页面:{siblings}) + +| 项 | 值 | +|----|-----| +| 主框架 URL | {page_url} | +{tab_row}| iframe 选择器 | 无(整页渲染,非 iframe 报表) | +| 元素扫描 JSON | `data/scan/{scan_key}-elements.json` | +| 定位器 YAML | `locators/spd/{scan_key}.locator.yaml` | +| 测试环境示例 | `http://spdtest.cmic.com.cn:8080/spd/form/` | + +## 主界面 + +![{page['name']}主界面]({kb_dir}/page-main.png) + +{page['layout']} + +### 筛选/搜索区 + +| 字段 | 控件 | 占位/默认值 | +|------|------|-------------| +{filter_rows} + +- 操作:{page['actions']} + +### 列表与工具栏 + +工具栏:{page.get('toolbar', '列设置')}。 + +| 列 | 说明 | +|----|------| +{col_rows} + +行操作:{page['row_ops']}。 + +### 分页 + +{page['paging']} +{extra} +## 自动化建议 + +1. 元素扫描 JSON 与定位器 YAML 已生成;下一步补 Page Object(建议 `{page['po_hint']}`)。 +{notes} + +## 测试关注点 + +{points} + +## 关联 + +{peer} +""" + + +def render_spec(page: dict, spec_id: str) -> str: + if spec_id.startswith("ADMIT-"): + return render_admit_spec(page, spec_id) + if spec_id.startswith("VENDOR-"): + return render_vendor_spec(page, spec_id) + if spec_id.startswith("USER-"): + return render_user_spec(page, spec_id) + if spec_id.startswith("WH-") or spec_id.startswith("FLOW-"): + return render_group_spec(page, spec_id) + return render_stat_spec(page, spec_id) + + def main() -> None: parser = argparse.ArgumentParser(description="同步 docs 截图至 UI 专项规范库") parser.add_argument( @@ -184,8 +1133,12 @@ def main() -> None: if not images: raise SystemExit(f"no png in {src_dir}") shutil.copy2(images[0], kb_dir / "page-main.png") - if len(images) > 1: - shutil.copy2(images[1], kb_dir / "page-main-2.png") + # 复制全部补充截图:page-main-2.png … page-main-N.png + for idx, img in enumerate(images[1:], start=2): + shutil.copy2(img, kb_dir / f"page-main-{idx}.png") + # 若配置了 extra_imgs,确保规范中引用的张数不超过实际文件 + if "extra_imgs" in page: + page = {**page, "extra_imgs": max(0, min(int(page["extra_imgs"]), len(images) - 1))} spec_file.write_text(render_spec(page, spec_id), encoding="utf-8") print(f"OK UI-SPEC-SPD-{spec_id}\t{page['name']}\t{len(images)} img") print("done", len(pages)) diff --git a/scripts/sync-quarantine.mjs b/scripts/sync-quarantine.mjs new file mode 100644 index 0000000..87fb197 --- /dev/null +++ b/scripts/sync-quarantine.mjs @@ -0,0 +1,158 @@ +#!/usr/bin/env node +/** + * Flake 隔离闭环:把 reports/flake 的隔离候选合并写入 config/quarantine.json。 + * + * 背景:flake-report.mjs 只产出「隔离候选」清单,若不落库,主门禁会一直跑这些高 flake 用例, + * 导致夜间全量被环境/瞬时问题反复刷红。本脚本负责把它变成真正的隔离项(闭环)。 + * + * 用法: + * node scripts/sync-quarantine.mjs # 预览:只打印将要写入的项 + * node scripts/sync-quarantine.mjs --write # 实际写入 config/quarantine.json + * node scripts/sync-quarantine.mjs --write --owner=liguangyu --mode=observe + * + * 安全约束: + * - 只隔离 environment / transient / script 类候选;**product(断言/业务缺陷)绝不自动隔离**。 + * - 已存在的人工条目(含 ticket/owner 等)一律保留,只补充/更新自动条目。 + * - 自动条目带 `auto: true` 标记,便于下次同步识别与回收。 + */ +import { + existsSync, + readFileSync, + writeFileSync, +} from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); + +/** 允许自动隔离的失败类别;product 属业务缺陷,禁止自动隔离。 */ +const AUTO_ISOLATABLE = new Set(['environment', 'transient', 'script', 'data']); + +const CLASS_LABEL = { + environment: '环境/基础设施抖动', + transient: '瞬时 Flake', + script: '脚本/定位不稳定', + data: '测试数据不足', + product: '产品缺陷', +}; + +function parseArgs(argv) { + const out = { + trend: path.join(ROOT, 'reports/flake/latest.json'), + quarantine: path.join(ROOT, 'config/quarantine.json'), + write: false, + mode: 'quarantine', + owner: process.env.QUARANTINE_OWNER || 'ui-auto', + ticket: process.env.QUARANTINE_TICKET || '', + }; + for (const arg of argv) { + if (arg === '--write') out.write = true; + else if (arg.startsWith('--trend=')) out.trend = path.resolve(arg.slice(8)); + else if (arg.startsWith('--quarantine=')) out.quarantine = path.resolve(arg.slice(13)); + else if (arg.startsWith('--mode=')) out.mode = arg.slice(7); + else if (arg.startsWith('--owner=')) out.owner = arg.slice(8); + else if (arg.startsWith('--ticket=')) out.ticket = arg.slice(9); + } + if (!['quarantine', 'observe'].includes(out.mode)) out.mode = 'quarantine'; + return out; +} + +function readJson(file, fallback) { + try { + return JSON.parse(readFileSync(file, 'utf8')); + } catch { + return fallback; + } +} + +function extractCaseId(title) { + return String(title || '').match(/\b(STAT-[A-Z]+-\d{3,})\b/)?.[1]; +} + +/** 取该用例出现最多的失败类别(决定是否可自动隔离)。 */ +function dominantClass(classes) { + const entries = Object.entries(classes || {}); + if (!entries.length) return { cls: '', n: 0 }; + entries.sort((a, b) => b[1] - a[1]); + return { cls: entries[0][0], n: entries[0][1] }; +} + +function buildAutoItems(trend, opts) { + const items = []; + const skipped = []; + for (const cand of trend.quarantineCandidates || []) { + const { cls } = dominantClass(cand.classes); + const id = extractCaseId(cand.title); + if (!id) { + skipped.push({ title: cand.title, reason: '标题无 STAT- 编号,无法稳定匹配' }); + continue; + } + if (!AUTO_ISOLATABLE.has(cls)) { + skipped.push({ title: cand.title, reason: `失败类别 ${cls || '未知'}(疑似产品缺陷)不自动隔离` }); + continue; + } + items.push({ + id, + mode: opts.mode, + reason: `flake failRate=${Number(cand.failRate).toFixed(2)} (${cand.failCount}/${cand.decided}) 主导类别=${CLASS_LABEL[cls] || cls}`, + owner: opts.owner, + since: new Date().toISOString().slice(0, 10), + category: cls, + auto: true, + ...(opts.ticket ? { ticket: opts.ticket } : {}), + }); + } + return { items, skipped }; +} + +function mergeItems(existingItems, autoItems) { + const manual = (existingItems || []).filter((item) => !item.auto); + const manualIds = new Set(manual.map((item) => item.id).filter(Boolean)); + const autoById = new Map(); + for (const item of autoItems) { + // 人工已声明的用例不再自动覆盖,尊重人工结论。 + if (manualIds.has(item.id)) continue; + autoById.set(item.id, item); + } + return [...manual, ...[...autoById.values()]]; +} + +function main() { + const opts = parseArgs(process.argv.slice(2)); + if (!existsSync(opts.trend)) { + console.error(`[quarantine] 未找到 flake 趋势文件:${path.relative(ROOT, opts.trend)}`); + console.error('[quarantine] 先执行 `npm run flake:report` 生成隔离候选。'); + process.exit(2); + } + + const trend = readJson(opts.trend, { quarantineCandidates: [] }); + const { items: autoItems, skipped } = buildAutoItems(trend, opts); + const current = readJson(opts.quarantine, { version: 1, items: [] }); + const merged = mergeItems(current.items, autoItems); + + const added = merged.filter((item) => item.auto).length; + console.log( + `[quarantine] 候选=${(trend.quarantineCandidates || []).length} 自动可隔离=${autoItems.length} 跳过=${skipped.length} 合并后总计=${merged.length}`, + ); + for (const item of autoItems) { + console.log(` + ${item.id} [${item.mode}] ${item.reason}`); + } + for (const s of skipped) { + console.log(` ~ 跳过 ${s.title}:${s.reason}`); + } + + if (!opts.write) { + console.log('[quarantine] 预览模式,未写入。加 --write 落库。'); + return; + } + + const next = { version: current.version ?? 1, items: merged }; + writeFileSync(opts.quarantine, `${JSON.stringify(next, null, 2)}\n`, 'utf8'); + console.log( + `[quarantine] 已写入 ${path.relative(ROOT, opts.quarantine)}(自动项 ${added},人工项保留 ${ + merged.length - added + })。`, + ); +} + +main(); diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..ceaf7bf --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,46 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"ADMIT-PLS-001","基础设置 · 准入管理","产品准入单列表 · 页面进入","菜单进入产品准入单列表","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录(已登录);具备「基础设置 → 准入管理」权限;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe)","1. 按菜单「基础设置 → 准入管理 → 产品准入单列表」进入 +2. 断言 URL 含 `/spd/basic/dataSync/dataSync/productOrder/list`,且路径不是 `zrlist` +3. 断言制单日期、查询、状态 Tab、列表表头可见","进入产品准入单列表;不误入物料准入单列表。","" +"ADMIT-PLS-002","基础设置 · 准入管理","产品准入单列表 · 筛选与状态","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 断言出现数据行,或「暂无数据」且分页总数为 0","查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。","" +"ADMIT-PLS-003","基础设置 · 准入管理","产品准入单列表 · 筛选与状态","按产品名称筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe);默认查询至少 1 条且产品名称非空","1. 从首行读取 `${sampleProductName}` +2. 若筛选区无产品名称,则用注册证号或器械注册人中首行非空字段 +3. 制单日期保持默认,点击「查 询」 +4. 断言结果包含该关键字","结果与已填关键字匹配。","" +"ADMIT-PLS-004","基础设置 · 准入管理","产品准入单列表 · 筛选与状态","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe);已填入筛选关键字","1. 点击「重 置」 +2. 断言筛选恢复默认","筛选恢复默认;列表按默认规则刷新。","" +"ADMIT-PLS-005","基础设置 · 准入管理","产品准入单列表 · 筛选与状态","无匹配结果空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe)","1. 注册证号填入 `__NOT_EXIST_CERT__99999` +2. 点击「查 询」 +3. 断言空态且分页总数为 0","无匹配时不报错;总数为 0。","" +"ADMIT-PLS-006","基础设置 · 准入管理","产品准入单列表 · 筛选与状态","列表核心列与附件计数","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe);默认查询至少 1 条数据","1. 断言表头含准入单号、进院供应商、二级供应商、产品名称、注册证号、耗材类型、产品条数、状态、备注、附件、操作 +2. 断言首行附件列展示个数(可为 0 个)","核心列可见;附件列有个数;操作为「详情」。本页列表不要求「准入类型」列。","" +"ADMIT-PLS-007","基础设置 · 准入管理","产品准入单列表 · 筛选与状态","切换已通过 Tab","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe)","1. 点击「已通过」 +2. 断言有数据时状态为已通过 +3. 断言 Tab 括号数量等于分页「共 N 条」","已通过 Tab 的状态与计数一致。","" +"ADMIT-PLS-008","基础设置 · 准入管理","产品准入单列表 · 筛选与状态","与物料准入单列表不串 URL","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe);当前在产品准入单列表","1. 记录当前 URL +2. 断言 URL 含 `productOrder/list` 且不含 `zrlist` +3. 不点击物料准入单列表菜单","停留在产品准入单列表路径,不串到物料单路径。","" +"ADMIT-PLS-009","基础设置 · 准入管理","产品准入单列表 · 单据详情","打开详情后返回","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe);默认查询至少 1 条数据","1. 记录首行 `${sampleOrderNo}` +2. 点击「详情」 +3. 断言可见「审批进程」「单据日志」「返 回」 +4. 返回后 URL 仍含 `productOrder/list`","详情可打开;返回后仍是产品准入单列表。","" +"ADMIT-PLS-010","基础设置 · 准入管理","产品准入单列表 · 单据详情","详情产品条数与列表一致","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe);已打开首行详情","1. 读取详情明细分页总数或当前行数 +2. 断言等于列表「产品条数」","明细条数与产品条数一致。","" +"ADMIT-PLS-011","基础设置 · 准入管理","产品准入单列表 · 单据详情","单据日志可打开","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe);已打开单据详情","1. 点击「单据日志」 +2. 断言日志层可见 +3. 关闭后仍在该单据","单据日志可查看;详情不被销毁。","" +"ADMIT-PLS-012","基础设置 · 准入管理","产品准入单列表 · 单据详情","详情搜索无匹配(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe);已打开单据详情","1. 搜索框填入 `__NOT_EXIST_GOODS__99999` +2. 断言明细空态,返回按钮仍在","无匹配只影响明细;单据头仍在。","" +"ADMIT-PLS-013","基础设置 · 准入管理","产品准入单列表 · 单据详情","已通过单据不确认通过","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入单列表页(/spd/basic/dataSync/dataSync/productOrder/list,无 iframe);已通过 Tab 至少 1 条并打开详情","1. 「通过」禁用或不可见时,断言状态保持已通过 +2. 若可点且出现确认框,点「取 消」 +3. 返回列表,断言状态仍为已通过","不把已通过单据再次审批成功。无已通过数据则 skip。","" +"ADMIT-PLS-014","基础设置 · 准入管理","产品准入单列表 · 筛选与状态","多条件组合查询(制单日期等6项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 准入管理」权限;已进入准入管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「制单日期」日期区间选择当天 +3. 「销售商」填写关键字 +4. 「耗材类型」下拉选择任一可选项 +5. 「器械注册人」填写关键字 +6. 「注册证号」填写关键字 +7. 「供应商」填写关键字 +8. 点击「查询」 +9. 检查列表返回结果","多条件同时生效:制单日期、销售商、耗材类型、器械注册人、注册证号、供应商 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..15e2ad5 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,56 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"ADMIT-PAP-001","基础设置 · 准入管理","产品准入申请 · 页面进入","菜单进入产品准入申请页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录(已登录);具备「基础设置 → 准入管理」权限;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe)","1. 按菜单「基础设置 → 准入管理 → 产品准入申请」进入 +2. 断言 URL 含 `/spd/basic/dataSync/dataSync/productApply/list`,且不是 `ZRlist` +3. 断言可见「查 询」、「Excel导入」「生成申请单」「一键生成」「新增产品」「作 废」及列表表头","进入产品准入申请页;不误入物料准入管理(ZRlist)或产品准入单列表。","" +"ADMIT-PAP-002","基础设置 · 准入管理","产品准入申请 · 筛选查询","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 断言出现数据行,或「暂无数据」且分页总数为 0","查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。","" +"ADMIT-PAP-003","基础设置 · 准入管理","产品准入申请 · 筛选查询","产品名称筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe);默认查询至少 1 条数据","1. 从首行读取 `${sampleProductName}` +2. 填入产品名称,新增日期保持默认,点击「查 询」 +3. 断言结果行「产品名称」均包含该关键字","产品名称与已填关键字匹配。","" +"ADMIT-PAP-004","基础设置 · 准入管理","产品准入申请 · 筛选查询","名称与耗材类型组合查询","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe);默认查询至少 1 条数据","1. 读取首行 `${sampleProductName}`、`${sampleConsumableType}` +2. 同时填入后点击「查 询」(日期保持默认) +3. 断言结果同时满足两个条件","组合查询结果同时满足产品名称与耗材类型。类型为空则只断言名称。","" +"ADMIT-PAP-005","基础设置 · 准入管理","产品准入申请 · 筛选查询","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe)","1. 填入 `${sampleProductName}` +2. 点击「重 置」 +3. 断言筛选恢复默认","筛选字段恢复默认;列表按默认规则刷新。","" +"ADMIT-PAP-006","基础设置 · 准入管理","产品准入申请 · 筛选查询","无匹配结果空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe)","1. 产品名称填入 `__NOT_EXIST_GOODS__99999` +2. 点击「查 询」 +3. 断言空态且分页总数为 0","无匹配时不报错;总数为 0。","" +"ADMIT-PAP-007","基础设置 · 准入管理","产品准入申请 · 状态与列表","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe);默认查询至少 1 条数据","1. 断言表头含准入单号、产品编号、产品名称、规格型号、注册规格、注册型号、器械注册人、单位、操作","核心列可见;操作含「详情」「日志」。","" +"ADMIT-PAP-008","基础设置 · 准入管理","产品准入申请 · 状态与列表","切换待申请 Tab","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe)","1. 点击「待申请」 +2. 断言有数据时行不属于已生效/已作废;计数为 0 时为空态","待申请 Tab 与列表状态一致。","" +"ADMIT-PAP-009","基础设置 · 准入管理","产品准入申请 · 状态与列表","翻至第 2 页","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe);分页总数大于每页条数","1. 记录第 1 页首行准入单号 +2. 点击页码「2」或「下一页」 +3. 断言页码高亮为 2,首行单号与第 1 页不同","翻页成功且数据更换。不足两页则 skip。","" +"ADMIT-PAP-010","基础设置 · 准入管理","产品准入申请 · 状态与列表","跳至指定页","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe);总页数不少于 2,分页存在「跳至」","1. 在跳至输入框填入 `2` 并回车 +2. 断言当前页为 2","跳转到第 2 页。无跳至或仅 1 页则 skip。","" +"ADMIT-PAP-011","基础设置 · 准入管理","产品准入申请 · 新增与导入","打开新增产品后返回","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe)","1. 点击「新增产品」 +2. 断言可见产品名称、器械注册人、规格型号、注册证号及返回 +3. 点击返回,不保存","新增页字段可见;返回后列表不新增记录。","" +"ADMIT-PAP-012","基础设置 · 准入管理","产品准入申请 · 新增与导入","新增取消不落库","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe)","1. 记录分页总数 `${sampleTotal}` +2. 打开新增,产品名称填 `${uniqProductName}`(建议 `AUTO-PAP-${timestamp}`)后返回不保存 +3. 按该名称查询,断言无此行,总数不变","取消后不产生新产品。","" +"ADMIT-PAP-013","基础设置 · 准入管理","产品准入申请 · 新增与导入","Excel 导入打开后取消","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe)","1. 记录总数 `${sampleTotal}` +2. 点击「Excel导入」 +3. 断言导入层打开 +4. 关闭或取消,不选择文件 +5. 断言总数不变","取消导入不增加产品。","" +"ADMIT-PAP-014","基础设置 · 准入管理","产品准入申请 · 新增与导入","产品名称为空保存拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe);已打开新增产品页","1. 产品名称保持 mock 空值,其它能填的非名称字段可留空 +2. 点击保存 +3. 断言必填提示,页面不关闭 +4. 返回列表,断言总数不变","保存被拦截;不落库。","" +"ADMIT-PAP-015","基础设置 · 准入管理","产品准入申请 · 详情、编辑与作废","打开详情后返回","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe);默认查询至少 1 条数据","1. 记录首行 `${sampleProductName}` +2. 点击「详情」,断言名称一致 +3. 返回列表","详情与该行一致;返回后仍在产品准入申请列表。","" +"ADMIT-PAP-016","基础设置 · 准入管理","产品准入申请 · 详情、编辑与作废","待申请编辑回填","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe);待申请至少 1 行含「编辑」","1. 读取该行 `${sampleProductName}`、`${sampleSpec}` +2. 点击「编辑」 +3. 断言名称与规格型号回填一致 +4. 返回不保存","编辑回填与列表行一致。无编辑按钮则 skip。","" +"ADMIT-PAP-017","基础设置 · 准入管理","产品准入申请 · 详情、编辑与作废","编辑取消不保存","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe);存在可编辑行","1. 打开编辑,把产品名称改为 `${uniqProductName}` 后返回不保存 +2. 再次打开,断言名称仍为原值","取消后数据不变。","" +"ADMIT-PAP-018","基础设置 · 准入管理","产品准入申请 · 详情、编辑与作废","作废确认后取消","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe);待申请至少 1 条可勾选","1. 勾选 1 行并点击「作 废」 +2. 断言确认框出现 +3. 点击「取 消」 +4. 断言该行仍在,状态未变为已作废","取消作废不改变状态。本条不点击确定。","" +"ADMIT-PAP-019","基础设置 · 准入管理","产品准入申请 · 详情、编辑与作废","已通过行不提供编辑","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入产品准入申请页(/spd/basic/dataSync/dataSync/productApply/list,无 iframe);已通过 Tab 计数大于 0","1. 切换「已通过」 +2. 断言首行无「编辑」,或打开后保存被拒绝","已通过行不能改写产品名称。计数为 0 则 skip。","" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..f3a1032 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,57 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"ADMIT-SUP-001","基础设置 · 准入管理","供货目录申请 · 页面进入","菜单进入供货目录申请页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录(已登录);具备「基础设置 → 准入管理」权限;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe)","1. 按菜单「基础设置 → 准入管理 → 供货目录申请」进入 +2. 断言 URL 含 `/spd/basic/dataSync/dataSync/supplyApply/list` +3. 断言可见「查 询」、「批量提交」、「新 增」及列表表头","进入供货目录申请页;不误入准入任务分配。","" +"ADMIT-SUP-002","基础设置 · 准入管理","供货目录申请 · 筛选与状态","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 断言出现数据行,或「暂无数据」且分页总数为 0","查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。","" +"ADMIT-SUP-003","基础设置 · 准入管理","供货目录申请 · 筛选与状态","按进院供应商筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe);默认查询至少 1 条且进院供应商非空","1. 从首行读取 `${sampleHospitalSupplier}` +2. 在进院供应商选择该值,制单日期保持默认,点击「查 询」 +3. 断言结果行「进院供应商」与所选值一致","进院供应商列与条件一致。","" +"ADMIT-SUP-004","基础设置 · 准入管理","供货目录申请 · 筛选与状态","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe);已选择进院供应商","1. 点击「重 置」 +2. 断言进院供应商等筛选恢复默认","筛选恢复默认;列表按默认规则刷新。","" +"ADMIT-SUP-005","基础设置 · 准入管理","供货目录申请 · 筛选与状态","变更单号无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe)","1. 变更单号填入 `__NOT_EXIST_ORDER__99999`;若筛选区无此框,则在产品名称填同一 mock +2. 点击「查 询」 +3. 断言空态且分页总数为 0","无匹配时不报错;总数为 0。","" +"ADMIT-SUP-006","基础设置 · 准入管理","供货目录申请 · 筛选与状态","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe);默认查询至少 1 条数据","1. 断言表头含申请单号、申请人、申请时间、进院供应商、二级供应商、业务模式、是否默认、备注、操作","核心列可见;操作含「编辑」或「详情」(随状态变化)。","" +"ADMIT-SUP-007","基础设置 · 准入管理","供货目录申请 · 筛选与状态","切换待提交 Tab 且计数一致","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe)","1. 点击「待提交」 +2. 读取括号数量 +3. 断言分页「共 N 条」等于该数量;有数据时状态为待提交","待提交 Tab 与分页总数一致。计数为 0 时为空态。","" +"ADMIT-SUP-008","基础设置 · 准入管理","供货目录申请 · 新增与编辑","打开新增供货目录","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe)","1. 点击「新 增」 +2. 断言可见进院供应商、二级供应商、业务模式、是否默认、计划生效时间、添加产品、提交 +3. 返回不提交","新增页字段齐全;返回后列表不新增申请单。","" +"ADMIT-SUP-009","基础设置 · 准入管理","供货目录申请 · 新增与编辑","新增放弃不落库","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe)","1. 记录总数 `${sampleTotal}` +2. 打开新增,备注填 `${uniqRemark}`(建议 `AUTO-SUP-${timestamp}`)后返回不提交 +3. 断言总数不变,列表无该备注","未提交不产生申请单。","" +"ADMIT-SUP-010","基础设置 · 准入管理","供货目录申请 · 新增与编辑","业务模式与是否默认只切换不提交","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe);已打开新增页","1. 展开业务模式、是否默认,断言存在可选项 +2. 各选一项后返回,不提交 +3. 断言列表总数不变","选项可见;不提交则无新单。","" +"ADMIT-SUP-011","基础设置 · 准入管理","供货目录申请 · 新增与编辑","待提交编辑回填","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe);待提交至少 1 行含「编辑」","1. 读取该行 `${sampleHospitalSupplier}`、`${sampleBizMode}`、`${sampleDefaultFlag}` +2. 点击「编辑」 +3. 断言进院供应商、业务模式、是否默认与该行一致 +4. 返回不提交","编辑回填与列表行一致。无待提交或无编辑则 skip。","" +"ADMIT-SUP-012","基础设置 · 准入管理","供货目录申请 · 新增与编辑","编辑取消不保存","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe);存在可编辑的待提交行","1. 打开编辑,把备注改为 `${uniqRemark}` 后返回不提交 +2. 再次打开,断言备注仍为原值","取消后备注不变。","" +"ADMIT-SUP-013","基础设置 · 准入管理","供货目录申请 · 新增与编辑","进院供应商为空不可提交(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe);已打开新增页,进院供应商未选","1. 不选进院供应商,点击「提 交」 +2. 断言必填提示,页面不关闭 +3. 返回列表,断言总数不变","提交被拦截;不产生申请单。","" +"ADMIT-SUP-014","基础设置 · 准入管理","供货目录申请 · 新增与编辑","未添加产品不可提交","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe);已打开新增页,产品明细为 0","1. 若进院供应商、二级供应商、业务模式可从样本选择则先选上,但不添加产品 +2. 点击「提 交」 +3. 断言拦截提示,明细仍为 0,列表总数不变","没有产品明细时不能提交。","" +"ADMIT-SUP-015","基础设置 · 准入管理","供货目录申请 · 批量提交与详情","未勾选时批量提交不可用","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe),无勾选行","1. 断言「批量提交」计数为 0,或按钮不可用 +2. 若仍可点击,断言出现请选择提示,且无单据离开待提交","未勾选不提交任何申请单。","" +"ADMIT-SUP-016","基础设置 · 准入管理","供货目录申请 · 批量提交与详情","勾选后批量提交取消","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe);待提交至少 1 条","1. 勾选 1 行,记录申请单号与状态 +2. 点击「批量提交」 +3. 若出现确认框,点「取 消」 +4. 断言该单仍为待提交","取消批量提交不改变状态。本条不点击确定。无待提交则 skip。","" +"ADMIT-SUP-017","基础设置 · 准入管理","供货目录申请 · 批量提交与详情","打开详情后返回","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入供货目录申请页(/spd/basic/dataSync/dataSync/supplyApply/list,无 iframe);默认查询至少 1 条数据","1. 点击首行「详情」 +2. 断言可见「审批进程」「单据日志」「返 回」 +3. 返回后 URL 仍含 `supplyApply/list`","详情可打开;返回后仍在供货目录申请列表。","" +"ADMIT-SUP-018","基础设置 · 准入管理","供货目录申请 · 筛选与状态","多条件组合查询(制单日期等6项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 准入管理」权限;已进入准入管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「制单日期」日期区间选择当天 +3. 「销售商」下拉选择任一可选项 +4. 「供应商」下拉选择任一可选项 +5. 「变更单号」填写关键字 +6. 「物料名称」填写关键字 +7. 「规格型号」填写关键字 +8. 点击「查询」 +9. 检查列表返回结果","多条件同时生效:制单日期、销售商、供应商、变更单号、物料名称、规格型号 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..7bd5815 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,54 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"ADMIT-TSK-001","基础设置 · 准入管理","准入任务分配 · 页面进入","菜单进入准入任务分配页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录(已登录);具备「基础设置 → 准入管理」权限;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe)","1. 按菜单「基础设置 → 准入管理 → 准入任务分配」进入 +2. 断言 URL 含 `/spd/basic/dataSync/dataSync/accessKind/list` +3. 断言可见「查 询」、「待分类」「已分类」、「批量分配」及列表表头","进入准入任务分配页;不误入供货目录申请。","" +"ADMIT-TSK-002","基础设置 · 准入管理","准入任务分配 · 筛选与分类","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe),当前为待分类","1. 点击「查 询」并等待列表稳定 +2. 断言出现数据行,或「暂无数据」且分页总数为 0","查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。","" +"ADMIT-TSK-003","基础设置 · 准入管理","准入任务分配 · 筛选与分类","产品名称筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe);待分类至少 1 条数据","1. 从首行读取 `${sampleProductName}` +2. 填入产品名称,新增日期与操作日期保持默认,点击「查 询」 +3. 断言结果行「产品名称」均包含该关键字","产品名称与已填关键字匹配。","" +"ADMIT-TSK-004","基础设置 · 准入管理","准入任务分配 · 筛选与分类","待分类与已分类切换","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe)","1. 记录「待分类」括号数量 `${pendingCount}` +2. 点击「已分类」,断言分页总数等于已分类括号数量;为 0 时为空态 +3. 再点「待分类」,断言分页总数等于 `${pendingCount}`","两个 Tab 的列表范围与各自计数一致,不串数据。","" +"ADMIT-TSK-005","基础设置 · 准入管理","准入任务分配 · 筛选与分类","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe);已填入 `${sampleProductName}`","1. 点击「重 置」 +2. 断言产品名称等筛选恢复默认","筛选恢复默认;列表按默认规则刷新。","" +"ADMIT-TSK-006","基础设置 · 准入管理","准入任务分配 · 筛选与分类","无匹配结果空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe);当前在待分类","1. 产品名称填入 `__NOT_EXIST_GOODS__99999` +2. 点击「查 询」 +3. 断言空态且分页总数为 0","无匹配时不报错;总数为 0。","" +"ADMIT-TSK-007","基础设置 · 准入管理","准入任务分配 · 列表分页","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe);待分类至少 1 条数据","1. 断言表头含准入类型、产品编号、产品名称、规格型号、规格、型号、器械注册人、操作 +2. 断言待分类行操作含「分配类型」","核心列可见;待分类行可发起分配类型。","" +"ADMIT-TSK-008","基础设置 · 准入管理","准入任务分配 · 列表分页","翻至第 2 页","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe);待分类总数大于每页条数","1. 记录第 1 页首行产品名称或产品编号 +2. 点击页码「2」或「下一页」 +3. 断言页码为 2,首行与第 1 页不同","翻页成功且数据更换。不足两页则 skip。","" +"ADMIT-TSK-009","基础设置 · 准入管理","准入任务分配 · 列表分页","跳至指定页","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe);总页数不少于 2,且存在「跳至」","1. 跳至输入 `2` 并回车 +2. 断言当前页为 2","跳转到第 2 页。无跳至或仅 1 页则 skip。","" +"ADMIT-TSK-010","基础设置 · 准入管理","准入任务分配 · 列表分页","第 1 页上一页不可用","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe);当前在第 1 页","1. 断言「上一页」禁用,或点击后仍停留在第 1 页","第 1 页不能再向前翻。","" +"ADMIT-TSK-011","基础设置 · 准入管理","准入任务分配 · 分配操作","单行分配类型取消","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe);待分类至少 1 行含「分配类型」","1. 记录该行产品名称,以及待分类计数 `${pendingCount}` +2. 点击「分配类型」 +3. 断言分配层打开 +4. 取消或关闭,不确认 +5. 断言该行仍在待分类,计数仍为 `${pendingCount}`","取消分配不改变分类。本条不点击确定。","" +"ADMIT-TSK-012","基础设置 · 准入管理","准入任务分配 · 分配操作","未勾选时批量分配不可用","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe);待分类页无勾选行","1. 点击「批量分配」 +2. 断言按钮不可用,或提示先选择数据 +3. 断言待分类计数不变","未勾选不分配任何行。","" +"ADMIT-TSK-013","基础设置 · 准入管理","准入任务分配 · 分配操作","勾选后批量分配取消","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe);待分类至少 1 条","1. 勾选 1 行,记录待分类计数 +2. 点击「批量分配」 +3. 若打开分配层或确认框,点取消或关闭 +4. 断言该行仍在待分类,计数不变","取消批量分配不改变分类。本条不确认分配。","" +"ADMIT-TSK-014","基础设置 · 准入管理","准入任务分配 · 分配操作","已分类行不再按待分类分配","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入准入任务分配页(/spd/basic/dataSync/dataSync/accessKind/list,无 iframe);已分类计数大于 0","1. 切换到「已分类」 +2. 断言首行无「分配类型」,或点击后被拒绝且该行仍留在已分类 +3. 断言待分类计数不因此增加","已分类数据不会被这次操作退回待分类。已分类为 0 则 skip。","" +"ADMIT-TSK-015","基础设置 · 准入管理","准入任务分配 · 筛选与分类","多条件组合查询(新增日期等11项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 准入管理」权限;已进入准入管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「新增日期」日期区间选择当天 +3. 「操作日期」日期区间选择当天 +4. 「销售商」填写关键字 +5. 「物料名称」填写关键字 +6. 「注册证号」填写关键字 +7. 「操作人」填写关键字 +8. 「器械注册人」填写关键字 +9. 「耗材类型」下拉选择任一可选项 +10. 「规格型号」填写关键字 +11. 「准入类型」下拉选择任一可选项 +12. 「准入状态」下拉选择任一可选项 +13. 点击「查询」 +14. 检查列表返回结果","多条件同时生效:新增日期、操作日期、销售商、物料名称、注册证号、操作人、器械注册人、耗材类型、规格型号、准入类型、准入状态 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..3019b57 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,48 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"ADMIT-MLS-001","基础设置 · 准入管理","物料准入单列表 · 页面进入","菜单进入物料准入单列表","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录(已登录);具备「基础设置 → 准入管理」权限;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe)","1. 按菜单「基础设置 → 准入管理 → 物料准入单列表」进入 +2. 断言 URL 含 `/spd/basic/dataSync/dataSync/productOrder/zrlist`,且不是 `productOrder/list` +3. 断言制单日期、查询、状态 Tab、列表表头可见","进入物料准入单列表;不误入产品准入单列表。","" +"ADMIT-MLS-002","基础设置 · 准入管理","物料准入单列表 · 筛选与状态","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 断言出现数据行,或「暂无数据」且分页总数为 0","查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。","" +"ADMIT-MLS-003","基础设置 · 准入管理","物料准入单列表 · 筛选与状态","按准入单号筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe);默认查询至少 1 条数据","1. 从首行读取 `${sampleOrderNo}` +2. 若页面有准入单号条件则填入;否则用注册证号或产品名称中首行非空字段 +3. 制单日期保持默认,点击「查 询」 +4. 断言结果行包含该单号或该关键字","结果与已填单号或关键字匹配。列表筛选区没有单号框时,改用首行注册证号,并在用例备注中记录实际字段。","" +"ADMIT-MLS-004","基础设置 · 准入管理","物料准入单列表 · 筛选与状态","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe)","1. 填入上一条使用的关键字 +2. 点击「重 置」 +3. 断言筛选恢复默认","筛选恢复默认;列表按默认规则刷新。","" +"ADMIT-MLS-005","基础设置 · 准入管理","物料准入单列表 · 筛选与状态","无匹配结果空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe)","1. 注册证号填入 `__NOT_EXIST_CERT__99999` +2. 点击「查 询」 +3. 断言空态且分页总数为 0","无匹配时不报错;总数为 0。","" +"ADMIT-MLS-006","基础设置 · 准入管理","物料准入单列表 · 筛选与状态","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe);默认查询至少 1 条数据","1. 断言表头含准入类型、准入单号、进院供应商、二级供应商、产品名称、注册证号、耗材类型、产品条数、状态、操作","核心列可见;操作列为「详情」。","" +"ADMIT-MLS-007","基础设置 · 准入管理","物料准入单列表 · 筛选与状态","切换已通过 Tab","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe)","1. 点击「已通过」 +2. 断言有数据时「状态」列为已通过;计数为 0 时为空态 +3. 读取 Tab 括号数量,断言与分页「共 N 条」一致","已通过 Tab 的状态列、计数与分页总数一致。","" +"ADMIT-MLS-008","基础设置 · 准入管理","物料准入单列表 · 单据详情","打开详情后返回","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe);默认查询至少 1 条数据","1. 记录首行 `${sampleOrderNo}`、`${sampleProductCount}` +2. 点击「详情」 +3. 断言可见「审批进程」「单据日志」「返 回」 +4. 点击「返 回」 +5. 断言 URL 仍含 `productOrder/zrlist`","进入单据详情并可返回列表。","" +"ADMIT-MLS-009","基础设置 · 准入管理","物料准入单列表 · 单据详情","详情产品条数与列表一致","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe);已打开首行详情,且 `${sampleProductCount}` 为正整数","1. 统计详情明细数据行数(跨页则读详情分页总数) +2. 断言等于列表「产品条数」`${sampleProductCount}`","详情明细条数与列表产品条数一致。","" +"ADMIT-MLS-010","基础设置 · 准入管理","物料准入单列表 · 单据详情","审批进程可打开","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe);已打开单据详情","1. 点击「审批进程」 +2. 断言进程层或区域可见 +3. 关闭后仍停留在该单据","审批进程可查看;单据不被关掉。","" +"ADMIT-MLS-011","基础设置 · 准入管理","物料准入单列表 · 单据详情","详情导出不白屏","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe);已打开单据详情","1. 点击「导 出」 +2. 断言出现下载,或出现导出任务/成功提示 +3. 断言页面仍为该单据详情,不白屏","导出有反馈;详情页仍在。无导出按钮则 skip。","" +"ADMIT-MLS-012","基础设置 · 准入管理","物料准入单列表 · 单据详情","详情搜索无匹配(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe);已打开单据详情","1. 在「请输入产品编码、名称」填入 `__NOT_EXIST_GOODS__99999` +2. 触发搜索 +3. 断言明细空态,单据头(审批进程、返回)仍在","无匹配时只清空明细,不销毁单据头。","" +"ADMIT-MLS-013","基础设置 · 准入管理","物料准入单列表 · 单据详情","已通过单据不确认通过","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入单列表页(/spd/basic/dataSync/dataSync/productOrder/zrlist,无 iframe);已通过 Tab 至少 1 条,并打开其详情","1. 若「通过」不可见或禁用,断言该状态保持已通过后返回 +2. 若「通过」可点,点击后出现确认框则点「取 消」 +3. 返回列表,断言该单状态仍为已通过","本条不把已通过单据再次审批成功。已通过 Tab 无数据则 skip。","" +"ADMIT-MLS-014","基础设置 · 准入管理","物料准入单列表 · 筛选与状态","多条件组合查询(制单日期等6项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 准入管理」权限;已进入准入管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「制单日期」日期区间选择当天 +3. 「销售商」填写关键字 +4. 「耗材类型」下拉选择任一可选项 +5. 「器械注册人」填写关键字 +6. 「注册证号」填写关键字 +7. 「供应商」填写关键字 +8. 点击「查询」 +9. 检查列表返回结果","多条件同时生效:制单日期、销售商、耗材类型、器械注册人、注册证号、供应商 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..f20e915 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,70 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"ADMIT-MAT-001","基础设置 · 准入管理","物料准入管理 · 页面进入","菜单进入物料准入管理页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录(已登录);具备「基础设置 → 准入管理」权限;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe)","1. 按菜单「基础设置 → 准入管理 → 物料准入管理」进入 +2. 断言 URL 含 `/spd/basic/dataSync/dataSync/productApply/ZRlist` +3. 断言筛选区、「查 询」、状态 Tab、「关联产品」「生成申请单」「一键生成」「作 废」及列表表头可见 +4. 断言当前高亮不是「产品准入申请」或「物料准入单列表」","进入物料准入管理页;不误入同组其它准入页;筛选、状态 Tab 与工具栏可见。","" +"ADMIT-MAT-002","基础设置 · 准入管理","物料准入管理 · 筛选查询","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 断言出现数据行,或「暂无数据」且分页总数为 0","查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。","" +"ADMIT-MAT-003","基础设置 · 准入管理","物料准入管理 · 筛选查询","产品名称筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);默认查询至少 1 条数据","1. 从首行读取 `${sampleProductName}` +2. 填入「产品名称」,新增日期保持页面默认,点击「查 询」 +3. 断言结果行「产品名称」均包含该关键字","查询结果产品名称与已填关键字匹配;无未捕获异常。","" +"ADMIT-MAT-004","基础设置 · 准入管理","物料准入管理 · 筛选查询","耗材类型筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);默认查询至少 1 条且首行耗材类型非空","1. 从首行读取 `${sampleConsumableType}` +2. 在耗材类型下拉选择该值,点击「查 询」 +3. 断言结果行「耗材类型」与所选值一致","耗材类型列与所选条件一致。首行类型为空则 skip。","" +"ADMIT-MAT-005","基础设置 · 准入管理","物料准入管理 · 筛选查询","名称与耗材类型组合查询","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);默认查询至少 1 条数据","1. 读取首行 `${sampleProductName}`、`${sampleConsumableType}` +2. 同时填入名称与耗材类型(新增日期保持默认,勿改成单日) +3. 点击「查 询」 +4. 断言结果同时满足两个条件","组合查询无报错;结果同时满足产品名称与耗材类型。","" +"ADMIT-MAT-006","基础设置 · 准入管理","物料准入管理 · 筛选查询","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe)","1. 填入 `${sampleProductName}` +2. 点击「重 置」 +3. 断言产品名称等筛选恢复默认","筛选字段恢复默认;列表按默认规则刷新。","" +"ADMIT-MAT-007","基础设置 · 准入管理","物料准入管理 · 筛选查询","收起后再展开保留条件","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe)","1. 填入 `${sampleProductName}` +2. 点击「收起」,再展开筛选区 +3. 断言产品名称仍为已填值","收起不丢失已填条件;展开后值仍在。","" +"ADMIT-MAT-008","基础设置 · 准入管理","物料准入管理 · 筛选查询","无匹配结果空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe)","1. 产品名称填入 mock 值 `__NOT_EXIST_GOODS__99999` +2. 点击「查 询」 +3. 断言空态或「暂无数据」,分页总数为 0","无匹配时不报错;空态明确;总数为 0。","" +"ADMIT-MAT-009","基础设置 · 准入管理","物料准入管理 · 状态与列表","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);默认查询至少 1 条数据","1. 点击「查 询」 +2. 断言表头含准入类型、产品编号、产品名称、规格型号、器械注册人、单位、耗材类型、默认进院供应商、操作","核心列可见;操作列含「详情」「日志」,待申请行另可见「编辑」。","" +"ADMIT-MAT-010","基础设置 · 准入管理","物料准入管理 · 状态与列表","切换待申请 Tab","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe)","1. 点击状态 Tab「待申请」 +2. 断言列表刷新;若有数据,行状态属于待申请(不出现已生效、已作废) +3. 再点「全部」,断言列表不再只限定待申请","待申请 Tab 只展示待申请数据;「全部」不额外收窄。计数为 0 时为空态且不报错。","" +"ADMIT-MAT-011","基础设置 · 准入管理","物料准入管理 · 状态与列表","待申请 Tab 计数与分页总数一致","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe)","1. 读取「待申请」括号内数量 `${tabCount}` +2. 点击该 Tab 并查询稳定 +3. 断言分页「共 N 条」的 N 等于 `${tabCount}`","Tab 计数与该状态下分页总数一致。","" +"ADMIT-MAT-012","基础设置 · 准入管理","物料准入管理 · 状态与列表","未勾选时生成申请单不可用或提示","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe),且当前无勾选行","1. 确认表头复选框与行复选框均未选中 +2. 点击「生成申请单」 +3. 断言按钮不可用,或出现「请选择」类提示,且列表不新增申请单","未勾选不产生申请单。","" +"ADMIT-MAT-013","基础设置 · 准入管理","物料准入管理 · 详情与编辑","打开详情后返回","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);默认查询至少 1 条数据","1. 记录首行 `${sampleProductName}` +2. 点击该行「详情」 +3. 断言详情中产品名称、规格型号、注册证号可见,且名称包含 `${sampleProductName}` +4. 点击「返回」 +5. 断言回到列表,URL 仍含 `productApply/ZRlist`","详情展示该行产品主数据;返回后仍在物料准入管理列表。","" +"ADMIT-MAT-014","基础设置 · 准入管理","物料准入管理 · 详情与编辑","详情关键字段不可直接改写","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);已打开首行详情","1. 断言产品名称等主数据无可用的保存按钮,或输入框为只读 +2. 返回列表,断言首行名称仍为 `${sampleProductName}`","详情态不能把产品名称改写入库。","" +"ADMIT-MAT-015","基础设置 · 准入管理","物料准入管理 · 详情与编辑","待申请行编辑回填","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);待申请 Tab 至少 1 行含「编辑」","1. 在待申请列表读取首行 `${sampleProductName}`、`${sampleSpec}` +2. 点击「编辑」 +3. 断言产品名称、规格型号与该行一致 +4. 返回且不保存","编辑页回填与列表行一致。无「编辑」则 skip。","" +"ADMIT-MAT-016","基础设置 · 准入管理","物料准入管理 · 详情与编辑","编辑取消不保存","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);存在可编辑行","1. 打开编辑,记录原产品名称 +2. 将产品名称改为 `${uniqProductName}`(建议 `AUTO-MAT-${timestamp}`)后返回或取消,不保存 +3. 再次打开编辑,断言名称仍为原值","取消后列表与回看均为修改前名称。","" +"ADMIT-MAT-017","基础设置 · 准入管理","物料准入管理 · 详情与编辑","编辑清空产品名称拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);存在可编辑行","1. 打开编辑,将产品名称清空为 mock 空值 +2. 点击保存 +3. 断言必填提示,页面不关闭 +4. 取消后再次打开,断言名称仍为原值","保存被拦截;原记录不被写成空。","" +"ADMIT-MAT-018","基础设置 · 准入管理","物料准入管理 · 详情与编辑","已生效行不提供编辑","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);已生效 Tab 计数大于 0","1. 切换到「已生效」 +2. 断言首行操作无「编辑」,或点击后保存被拒绝","已生效行不能改写成新的产品名称。计数为 0 则 skip。","" +"ADMIT-MAT-019","基础设置 · 准入管理","物料准入管理 · 关联与作废","关联产品取消不落库","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe)","1. 记录当前分页总数 `${sampleTotal}` 与首行默认进院供应商 +2. 点击「关联产品」 +3. 断言关联层打开 +4. 取消或关闭,不确认 +5. 断言总数不变,首行供应商不变","取消关联不产生申请单,也不改默认进院供应商。","" +"ADMIT-MAT-020","基础设置 · 准入管理","物料准入管理 · 关联与作废","作废确认后取消","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);待申请至少 1 条可勾选","1. 勾选 1 行,记录其产品名称与当前状态 +2. 点击「作 废」 +3. 断言出现确认框,含取消与确定 +4. 点击「取 消」 +5. 断言该行仍在,状态未变为已作废","作废需确认;取消后记录仍在。本条不点击确定,避免作废生产数据。","" +"ADMIT-MAT-021","基础设置 · 准入管理","物料准入管理 · 关联与作废","打开操作日志后关闭","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入物料准入管理页(/spd/basic/dataSync/dataSync/productApply/ZRlist,无 iframe);默认查询至少 1 条数据","1. 点击首行「日志」 +2. 断言出现操作日志层 +3. 关闭后断言仍在列表页","日志层可打开可关闭;列表数据不变。","" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..9beb5b5 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,55 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"BASIC-NTC-001","基础设置 · 医院资料","公告管理 · 页面进入","菜单进入公告管理页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录(已登录);具备医院资料权限;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe)","1. 按菜单「基础设置 → 医院资料 → 公告管理」进入 +2. 检查当前 URL 是否含 /spd/basic/hospital/paltform/notice +3. 检查筛选区、新增按钮、列表表头是否可见","进入公告管理页;不误入医院信息登记/院区管理/财务月份设置;筛选区与列表可见。","" +"BASIC-NTC-002","基础设置 · 医院资料","公告管理 · 筛选查询","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 检查是否出现数据行,或「暂无数据」且分页总数为 0","查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。","" +"BASIC-NTC-003","基础设置 · 医院资料","公告管理 · 筛选查询","公告标题模糊查询","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe);默认查询至少 1 条数据","1. 从首行读取 ${sampleNoticeTitle}(禁止写死业务标题) +2. 填入筛选「公告标题」,点击「查 询」 +3. 检查结果行「公告标题」均包含该关键字(或与产品模糊规则一致)","查询结果标题与已填关键字匹配;无未捕获异常。","" +"BASIC-NTC-004","基础设置 · 医院资料","公告管理 · 筛选查询","发布范围下拉查询","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe)","1. 打开发布范围下拉,选择与首行 ${samplePublishScope} 相同的选项(勿写死) +2. 点击「查 询」 +3. 检查结果行「发布范围」列与所选值一致(选「全部」时不额外收窄)","下拉选项含全部/全院/中心库/进院供应商;查询结果范围列与条件一致。","" +"BASIC-NTC-005","基础设置 · 医院资料","公告管理 · 筛选查询","标题与范围组合查询","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe);默认查询至少 1 条数据","1. 读取首行 ${sampleNoticeTitle}、${samplePublishScope} +2. 填入标题与范围(发布日期保持页面默认,勿 fallback 单日) +3. 点击「查 询」 +4. 检查结果同时满足两个已填条件","组合查询无报错;结果同时满足标题与发布范围。","" +"BASIC-NTC-006","基础设置 · 医院资料","公告管理 · 筛选查询","快捷区重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe)","1. 填入 ${sampleNoticeTitle} 等筛选条件 +2. 点击「重 置」 +3. 检查标题/范围/日期是否恢复默认","筛选字段恢复默认;列表按默认规则刷新。","" +"BASIC-NTC-007","基础设置 · 医院资料","公告管理 · 筛选查询","无匹配结果空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe)","1. 公告标题填入 mock 值 __NOT_EXIST_NOTICE__99999(唯一写死的反例 mock) +2. 点击「查 询」 +3. 检查空态或暂无数据,分页总数为 0","无匹配时不报错;空态明确;总数为 0。","" +"BASIC-NTC-008","基础设置 · 医院资料","公告管理 · 列表分页","查询后核心列展示","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe)","1. 点击「查 询」 +2. 检查表头是否含公告编号、公告标题、发布范围、发布科室、发布人、操作","核心列完整;操作列可见「编辑」「删除」。","" +"BASIC-NTC-009","基础设置 · 医院资料","公告管理 · 列表分页","分页翻至第 2 页","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe);默认查询总条数大于一页","1. 记录第 1 页首行公告编号 +2. 点击页码「2」或「下一页」 +3. 检查页码高亮为 2,首行编号与第 1 页不同","翻页成功;数据更换;筛选条件保持。总条数不足一页则 skip。","" +"BASIC-NTC-010","基础设置 · 医院资料","公告管理 · 列表分页","跳至指定页","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe);总页数≥2","1. 在分页「跳至」输入 2 并回车 +2. 检查当前页为 2","跳转至指定页;列表刷新为该页数据。无 jumper 或仅 1 页则 skip。","" +"BASIC-NTC-011","基础设置 · 医院资料","公告管理 · 新增公告","打开新增公告弹窗","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe)","1. 点击「新 增」 +2. 检查弹窗标题是否为「新增公告」 +3. 检查是否可见标题、发布范围、置顶、截止日期、富文本、取消、发布","新增弹窗可见;必填/选填控件齐全;富文本容器 .ql-editor 可见。","" +"BASIC-NTC-012","基础设置 · 医院资料","公告管理 · 新增公告","新增取消不落库","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe)","1. 打开新增,标题填 ${uniqNoticeTitle}(建议 AUTO-NTC-${timestamp}),内容填简短正文 +2. 点击「取 消」 +3. 按标题查询,检查无该记录","弹窗关闭;列表不出现 ${uniqNoticeTitle}。","" +"BASIC-NTC-013","基础设置 · 医院资料","公告管理 · 新增公告","合法必填发布成功","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe);允许自动化写公告","1. 打开新增,标题 ${uniqNoticeTitle},发布范围选「中心库」,内容写入 .ql-editor +2. 点击「发 布」 +3. 按标题查询,检查出现该行且发布范围为中心库 +4. 后置:删除该测试公告","弹窗关闭;列表出现新记录;字段与提交一致。环境禁止写则 skip。","" +"BASIC-NTC-014","基础设置 · 医院资料","公告管理 · 新增公告","标题为空发布拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe)","1. 打开新增,标题填 mock 空值 """",内容填 mock 正文 __MOCK_NOTICE_BODY__ +2. 点击「发 布」 +3. 检查必填提示出现,弹窗仍打开","发布被拦截;不落库;弹窗不关闭。","" +"BASIC-NTC-015","基础设置 · 医院资料","公告管理 · 新增公告","内容为空发布拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe)","1. 打开新增,标题填 mock 值 __MOCK_EMPTY_CONTENT_TITLE__,富文本留空 """" +2. 点击「发 布」 +3. 检查内容必填提示,弹窗仍打开","发布被拦截;不落库。","" +"BASIC-NTC-016","基础设置 · 医院资料","公告管理 · 编辑与删除","打开编辑公告并回填","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe);列表至少 1 条数据","1. 点击首行「编辑」 +2. 检查弹窗标题是否为「编辑公告」 +3. 检查标题输入框值与该行 ${sampleNoticeTitle} 一致(允许规范化空白)","编辑弹窗可见;标题/范围/内容回填正确。","" +"BASIC-NTC-017","基础设置 · 医院资料","公告管理 · 编辑与删除","编辑取消不保存","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe);列表至少 1 条数据","1. 打开首行编辑,记录原标题 +2. 将标题改为 ${uniqNoticeTitle} 后点「取 消」 +3. 再次打开编辑,检查标题仍为原值","取消后列表与回看均为修改前标题。","" +"BASIC-NTC-018","基础设置 · 医院资料","公告管理 · 编辑与删除","删除弹出确认框","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入公告管理页(/spd/basic/hospital/paltform/notice,无 iframe);列表至少 1 条数据","1. 点击首行「删除」 +2. 检查出现确认框(标题含「提示」),可见「取 消」「确 定」 +3. 点击「取 消」 +4. 检查该行仍在","删除需二次确认;取消后记录仍在。本条不断言真实删除,避免污染数据。","" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..902516c --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,32 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"BASIC-HOS-001","基础设置 · 医院资料","医院信息登记 · 页面进入与查看","菜单进入医院信息登记页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录(已登录);具备医院资料权限;已进入医院信息登记页(/spd/basic/hospital/paltform/info,无 iframe)","1. 按菜单进入「医院信息登记」 +2. 检查当前 URL 是否含 /spd/basic/hospital/paltform/info +3. 检查可见「编 辑」及医院编码/名称/地址等详情标签","进入详情页;不误入公告/院区/财务月份页;查看态「编辑」可见。","" +"BASIC-HOS-002","基础设置 · 医院资料","医院信息登记 · 页面进入与查看","查看态只读字段不可改","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入医院信息登记页(/spd/basic/hospital/paltform/info,无 iframe);未点编辑","1. 检查医院编码、医院名称、地址区域无可用输入框 +2. 检查「保 存」「取 消」不可见","查看模式下编码/名称/地址只读;不出现保存按钮。","" +"BASIC-HOS-003","基础设置 · 医院资料","医院信息登记 · 页面进入与查看","已上传图片可预览","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入医院信息登记页(/spd/basic/hospital/paltform/info,无 iframe);当前医院已有登录页背景或 logo","1. 点击「预览文件」(背景或 logo 任一可见项) +2. 检查预览打开或新预览层可见","预览可打开;不报错。均无预览入口则 skip。","" +"BASIC-HOS-004","基础设置 · 医院资料","医院信息登记 · 编辑保存","进入编辑模式","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入医院信息登记页(/spd/basic/hospital/paltform/info,无 iframe);当前为查看态","1. 点击「编 辑」 +2. 检查「保 存」「取 消」可见 +3. 检查 #form_item_linkman、#form_item_contactWay、#form_item_anotherName 可编辑","进入编辑态;三个可改文本框可输入;编码/名称/地址仍不可改。","" +"BASIC-HOS-005","基础设置 · 医院资料","医院信息登记 · 编辑保存","编辑态只读字段仍不可改","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入医院信息登记页(/spd/basic/hospital/paltform/info,无 iframe);已进入编辑模式","1. 点击「编 辑」 +2. 检查医院编码、医院名称、地址仍无可用输入框","编辑模式下系统分配字段保持只读。","" +"BASIC-HOS-006","基础设置 · 医院资料","医院信息登记 · 编辑保存","取消不保存","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入医院信息登记页(/spd/basic/hospital/paltform/info,无 iframe);当前为查看态","1. 点击「编 辑」,读取原联系人 ${sampleLinkman} +2. 将联系人改为 ${uniqLinkman} 后点「取 消」 +3. 检查回到查看态,联系人展示仍为 ${sampleLinkman}","取消后不落库;查看态为修改前值。","" +"BASIC-HOS-007","基础设置 · 医院资料","医院信息登记 · 编辑保存","保存后字段持久化","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入医院信息登记页(/spd/basic/hospital/paltform/info,无 iframe);允许写医院资料","1. 记录原联系人 ${sampleLinkman} +2. 编辑为 ${uniqLinkman} 并「保 存」 +3. 刷新或重新打开本页,检查联系人为 ${uniqLinkman} +4. 后置:再编辑回 ${sampleLinkman} 并保存","保存后刷新仍为新值;用例结束恢复原值。环境禁止写则 skip。","" +"BASIC-HOS-008","基础设置 · 医院资料","医院信息登记 · 编辑保存","联系人为空保存拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入医院信息登记页(/spd/basic/hospital/paltform/info,无 iframe);当前为查看态","1. 点击「编 辑」,将联系人清空为 mock 空值 """" +2. 点击「保 存」 +3. 检查必填提示,仍停留编辑态","保存被拦截;原联系人不被写成空。","" +"BASIC-HOS-009","基础设置 · 医院资料","医院信息登记 · 编辑保存","联系方式为空保存拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入医院信息登记页(/spd/basic/hospital/paltform/info,无 iframe);当前为查看态","1. 进入编辑态,将联系方式清空为 mock 空值 """" +2. 点击「保 存」 +3. 检查必填提示,编辑态保持","保存被拦截;联系方式不被清空落库。","" +"BASIC-HOS-010","基础设置 · 医院资料","医院信息登记 · 编辑保存","医院简称为空保存拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入医院信息登记页(/spd/basic/hospital/paltform/info,无 iframe);当前为查看态","1. 进入编辑态,将医院简称清空为 mock 空值 """" +2. 点击「保 存」 +3. 检查必填提示","保存被拦截;简称不被写成空。","" +"BASIC-HOS-011","基础设置 · 医院资料","医院信息登记 · 图片上传","编辑态存在背景与 logo 上传控件","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入医院信息登记页(/spd/basic/hospital/paltform/info,无 iframe);当前为查看态","1. 点击「编 辑」 +2. 检查存在登录页背景、医院 logo 对应 input[type=file](loginBgUploadInput / logoUploadInput) +3. 点「取 消」,不实际上传","编辑态两个上传控件可定位;本条不强制替换线上图片。","" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..22aed47 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,41 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"BASIC-FMN-001","基础设置 · 医院资料","财务月份设置 · 页面进入","菜单进入财务月份设置页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录(已登录);具备医院资料权限;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe)","1. 按菜单「基础设置 → 医院资料 → 财务月份设置」进入 +2. 检查当前 URL 是否含 /spd/basic/hospital/balance/month +3. 检查筛选区(#form_item_settleMonthDate)、查询/重置、列表列头是否可见","进入财务月份设置页;不误入公告/医院信息/院区页;筛选与列表可见。","" +"BASIC-FMN-002","基础设置 · 医院资料","财务月份设置 · 筛选查询","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 检查是否出现数据行,或「暂无数据」且分页总数为 0","查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。","" +"BASIC-FMN-003","基础设置 · 医院资料","财务月份设置 · 筛选查询","按财务月份筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe);默认查询至少 1 条数据","1. 从首行或当前月份行读取 ${sampleFinanceMonth}(禁止写死月份) +2. 在 #form_item_settleMonthDate 选择该月份,点击「查 询」 +3. 检查结果行「财务月份」均等于该月份(或与产品筛选规则一致)","结果仅含所选财务月份;无未捕获异常。","" +"BASIC-FMN-004","基础设置 · 医院资料","财务月份设置 · 筛选查询","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe)","1. 选择 ${sampleFinanceMonth} 并查询 +2. 点击「重 置」 +3. 检查月份筛选恢复默认,列表按默认规则刷新","重置后筛选清空或回到默认;列表可再查询。","" +"BASIC-FMN-005","基础设置 · 医院资料","财务月份设置 · 列表与状态","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe);默认查询至少 1 条数据","1. 检查表头是否包含财务月份、开始日期、结束日期、自动结算日期、状态、操作","核心列均可见,不缺「状态」「操作」。","" +"BASIC-FMN-006","基础设置 · 医院资料","财务月份设置 · 列表与状态","仅当前月份行可修改","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe);列表同时存在当前月份与历史月份(若仅一行当前月份则只断言该行)","1. 定位状态为「当前月份」的行,检查操作列可见「修 改」(modifyBtn) +2. 对「历史月份」行检查无「修 改」或操作为「--」","可修改入口只出现在当前月份行。无历史月份则只校验当前月份入口。","" +"BASIC-FMN-007","基础设置 · 医院资料","财务月份设置 · 列表与状态","当前月份至多一行","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe);默认查询已出列表","1. 统计状态为「当前月份」的行数 +2. 检查行数 ≤ 1","同一时点至多一行「当前月份」。","" +"BASIC-FMN-008","基础设置 · 医院资料","财务月份设置 · 列表与状态","历史月份无法进入编辑","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe);列表至少 1 条历史月份","1. 对历史月份行尝试点击操作区 +2. 检查不出现行内「保 存」「取 消」,自动结算日期仍为文本","历史月份不能改开始/结束/自动结算日期。无历史月份则 skip。","" +"BASIC-FMN-009","基础设置 · 医院资料","财务月份设置 · 当前月份修改","修改进入行内编辑","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe);存在状态为「当前月份」的行","1. 点击该行「修 改」 +2. 检查出现「保 存」「取 消」 +3. 检查自动结算日期变为日期框(autoSettleDateInput)","进入行内编辑;仅自动结算日可改。","" +"BASIC-FMN-010","基础设置 · 医院资料","财务月份设置 · 当前月份修改","编辑态开始结束日期只读","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe);已进入当前月份行内编辑","1. 点击「修 改」 +2. 检查开始日期、结束日期无可用输入框 +3. 检查仅 autoSettleDateInput 可编辑 +4. 点击「取 消」退出","开始/结束日期保持只读;账期区间不被改写。","" +"BASIC-FMN-011","基础设置 · 医院资料","财务月份设置 · 当前月份修改","取消不保存自动结算日","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe);存在当前月份行","1. 读取 ${sampleAutoSettleDate} +2. 点「修 改」,将自动结算日改为当月区间内另一天 ${uniqAutoSettleDate} +3. 点击「取 消」 +4. 检查该行自动结算日仍为 ${sampleAutoSettleDate}","取消后不落库;日期恢复修改前值。","" +"BASIC-FMN-012","基础设置 · 医院资料","财务月份设置 · 当前月份修改","保存自动结算日期持久化","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe);存在当前月份行;允许写财务月份","1. 记录 ${sampleAutoSettleDate}、${sampleStartDate}、${sampleEndDate} +2. 将自动结算日改为区间内另一天 ${uniqAutoSettleDate} 并「保 存」 +3. 刷新或重新查询,检查该行自动结算日为 ${uniqAutoSettleDate} +4. 后置:再改为 ${sampleAutoSettleDate} 并保存","保存后刷新仍为新日期;用例结束恢复原值。环境禁止写则 skip。","" +"BASIC-FMN-013","基础设置 · 医院资料","财务月份设置 · 当前月份修改","自动结算日为空拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe);存在当前月份行","1. 点「修 改」,将 autoSettleDateInput 清空为 mock 空值 """" +2. 点击「保 存」 +3. 检查必填或非法提示,行上日期仍为 ${sampleAutoSettleDate}","保存被拦截;原日期不被写成空。","" +"BASIC-FMN-014","基础设置 · 医院资料","财务月份设置 · 当前月份修改","自动结算日超出当月区间拦截(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入财务月份设置页(/spd/basic/hospital/balance/month,无 iframe);存在当前月份行且能读到开始/结束日","1. 点「修 改」,将日期改为 mock 越界值:早于 ${sampleStartDate} 或晚于 ${sampleEndDate}(如 __MOCK_OUT_OF_RANGE_DATE__) +2. 点击「保 存」 +3. 检查拒绝保存并提示,原日期不变 +4. 点「取 消」退出","超出账期区间时拒绝保存;不脏写。日期控件无法选出区间外日期则 skip 并记录产品限制。","" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..d570793 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,55 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"BASIC-BRN-001","基础设置 · 医院资料","院区管理 · 页面进入","菜单进入院区管理页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录(已登录);具备医院资料权限;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe)","1. 按菜单「基础设置 → 医院资料 → 院区管理」进入 +2. 检查当前 URL 是否含 /spd/basic/hospital/paltform/area +3. 检查「新 增」、院区名称搜索框(campusNameInput)、列表列头可见 +4. 检查页面无独立「查 询」「重 置」按钮","进入院区管理页;不误入公告/医院信息/财务月份页;搜索框与新增可见,无查询/重置。","" +"BASIC-BRN-002","基础设置 · 医院资料","院区管理 · 列表搜索","默认列出院区或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe)","1. 等待列表稳定 +2. 检查是否出现数据行,或「暂无数据」且分页总数为 0","加载无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。","" +"BASIC-BRN-003","基础设置 · 医院资料","院区管理 · 列表搜索","院区名称搜索过滤","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);默认列表至少 1 条数据","1. 从首行读取 ${sampleCampusName}(禁止写死院区名称) +2. 在 campusNameInput 填入该关键字(即时过滤或回车,以现网为准) +3. 检查结果行「院区名称」均包含该关键字","过滤结果名称与已填关键字匹配;无未捕获异常。","" +"BASIC-BRN-004","基础设置 · 医院资料","院区管理 · 列表搜索","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);默认列表至少 1 条数据","1. 检查表头是否包含序号、院区编号、院区名称、院区地址、状态、启用云仓、操作","核心列均可见,操作列含「编 辑」。","" +"BASIC-BRN-005","基础设置 · 医院资料","院区管理 · 列表搜索","清空搜索恢复全量","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);已用 ${sampleCampusName} 过滤","1. 记录过滤后条数 +2. 清空 campusNameInput 并等待列表刷新 +3. 检查条数回到未过滤的默认集合(或不少于过滤后条数)","清空后列表恢复默认集合。","" +"BASIC-BRN-006","基础设置 · 医院资料","院区管理 · 列表搜索","搜索无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe)","1. 在院区名称框输入 mock 值 __NOT_EXIST_CAMPUS__99999(唯一写死的反例 mock) +2. 检查「暂无数据」且分页总数为 0","无匹配时空态明确,不白屏。","" +"BASIC-BRN-007","基础设置 · 医院资料","院区管理 · 新增院区","打开新增院区弹窗","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe)","1. 点击「新 增」 +2. 检查弹窗标题是否为「新增院区」 +3. 检查可见院区名称、所在地区、详细地址、主院区标志、状态、取消/确定 +4. 检查无 #form_item_id、无启用云仓 +5. 点击「取 消」关闭","新增弹窗字段完整;不含院区编号与启用云仓。","" +"BASIC-BRN-008","基础设置 · 医院资料","院区管理 · 新增院区","新增取消不落库","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe)","1. 记录当前分页总数 ${sampleTotal} +2. 打开新增,填写名称 ${uniqCampusName}(建议 AUTO-BRN-${timestamp})后点「取 消」 +3. 检查列表总数不变,且无该名称行","取消后不新增院区。","" +"BASIC-BRN-009","基础设置 · 医院资料","院区管理 · 新增院区","合法必填新增成功","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);允许写院区;名称使用 ${uniqCampusName}","1. 打开新增,填写名称 ${uniqCampusName}、所在地区、详细地址,主院区选「否」,状态选「启用」 +2. 点击「确 定」 +3. 检查弹窗关闭,列表出现该名称行且字段一致 +4. 后置:按环境约定停用或清理该院区,避免污染主数据","新增成功且列表可见;用例结束清理或停用,不留生产脏数据。环境禁止写则 skip。","" +"BASIC-BRN-010","基础设置 · 医院资料","院区管理 · 新增院区","院区名称为空拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);已打开新增弹窗","1. 名称填 mock 空值 """",补齐地区与地址后点「确 定」 +2. 检查必填提示,弹窗仍在,列表无新行","确定被拦截;不落库。","" +"BASIC-BRN-011","基础设置 · 医院资料","院区管理 · 新增院区","所在地区为空拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);已打开新增弹窗","1. 名称填 mock 值 __MOCK_CAMPUS_NO_REGION__,地址填 __MOCK_ADDR__,不选所在地区 +2. 点击「确 定」 +3. 检查必填提示,弹窗仍在","确定被拦截;不落库。","" +"BASIC-BRN-012","基础设置 · 医院资料","院区管理 · 新增院区","详细地址为空拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);已打开新增弹窗","1. 名称填 mock 值 __MOCK_CAMPUS_NO_ADDR__ 并选择地区,地址留空 """" +2. 点击「确 定」 +3. 检查必填提示,弹窗仍在","确定被拦截;不落库。","" +"BASIC-BRN-013","基础设置 · 医院资料","院区管理 · 编辑院区","打开编辑弹窗并回填","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);列表至少 1 条数据","1. 从首行读取 ${sampleCampusId}、${sampleCampusName}、${sampleCampusAddress} +2. 点击「编 辑」 +3. 检查弹窗标题是否为「编辑院区」 +4. 检查 #form_item_id 等于 ${sampleCampusId},名称/地址回填正确 +5. 检查额外可见「启用云仓」 +6. 点击「取 消」","编辑弹窗回填正确;相对新增多出院区编号与启用云仓。","" +"BASIC-BRN-014","基础设置 · 医院资料","院区管理 · 编辑院区","编辑态院区编号不可换号","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);已打开首行编辑","1. 检查 #form_item_id 可见且值等于列表「院区编号」 +2. 若编号只读则直接检查 disabled/readonly;若可输入则改写为 mock 值 __MOCK_CAMPUS_ID__ 后点确定,刷新后编号仍为原值 +3. 未改其它字段时点「取 消」","院区编号与列表一致;保存后编号不变。","" +"BASIC-BRN-015","基础设置 · 医院资料","院区管理 · 编辑院区","编辑取消不保存","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);列表至少 1 条数据","1. 打开首行编辑,记录原名称 +2. 将名称改为 ${uniqCampusName} 后点「取 消」 +3. 再次打开编辑,检查名称仍为原值","取消后列表与回看均为修改前名称。","" +"BASIC-BRN-016","基础设置 · 医院资料","院区管理 · 编辑院区","编辑状态为禁用启用","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);已打开编辑弹窗","1. 检查状态选项文案为「禁用 / 启用」(不是新增弹窗的「停用 / 启用」) +2. 点「取 消」,不改状态","编辑态状态文案与新增不同;本条不真实切换生产院区状态。","" +"BASIC-BRN-017","基础设置 · 医院资料","院区管理 · 编辑院区","编辑可见启用云仓","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);已打开编辑弹窗","1. 检查「启用云仓」单选「是 / 否」可见 +2. 点「取 消」,不改云仓标志","编辑可看到启用云仓;本条不真实改生产院区云仓开关。","" +"BASIC-BRN-018","基础设置 · 医院资料","院区管理 · 编辑院区","编辑清空必填拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;已进入院区管理页(/spd/basic/hospital/paltform/area,无 iframe);列表至少 1 条数据","1. 打开首行编辑,将院区名称清空为 mock 空值 """" 后点「确 定」 +2. 检查必填提示,弹窗仍在 +3. 点「取 消」后再打开,检查名称仍为原值","确定被拦截;原记录不被写成空。","" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..12e0011 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,19 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"VENDOR-HLD-001","基础设置 · 厂商管理","供应商节假日管理 · 页面进入","菜单进入供应商节假日管理页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商节假日管理页(/spd/basic/mfrs/basic/prov/holiday,无 iframe)","1. 按菜单进入供应商节假日管理 +2. 断言 URL 含 `prov/holiday` +3. 断言可见节假日主区与新增","进入供应商节假日管理页。","" +"VENDOR-HLD-002","基础设置 · 厂商管理","供应商节假日管理 · 左右分栏","选中左侧节假日展示详情","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商节假日管理页(/spd/basic/mfrs/basic/prov/holiday,无 iframe);左侧有数据","1. 点击左侧首行 +2. 断言默认假日详情或自定义列表区可见","详情联动成功。","" +"VENDOR-HLD-003","基础设置 · 厂商管理","供应商节假日管理 · 左右分栏","左侧搜索无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商节假日管理页(/spd/basic/mfrs/basic/prov/holiday,无 iframe)","1. 左侧搜索框填 mock 并回车 +2. 断言左侧列表空态","无匹配空态明确。","" +"VENDOR-HLD-004","基础设置 · 厂商管理","供应商节假日管理 · 维护","左侧新增取消","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商节假日管理页(/spd/basic/mfrs/basic/prov/holiday,无 iframe)","1. 点击新增 +2. 取消/关闭不保存 +3. 断言仍停留本页且无异常","取消不落库。","" +"VENDOR-HLD-005","基础设置 · 厂商管理","供应商节假日管理 · 左右分栏","自定义节假日列表区可见","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商节假日管理页(/spd/basic/mfrs/basic/prov/holiday,无 iframe)","1. 选中左侧行 +2. 断言「供应商自定义节假日列表」区域可见(可为空态)","自定义区可见。","" +"VENDOR-HLD-006","基础设置 · 厂商管理","供应商节假日管理 · 维护","结束早于开始日期拦截","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商节假日管理页(/spd/basic/mfrs/basic/prov/holiday,无 iframe)","1. 打开新增/编辑,结束日期早于开始日期尝试保存 +2. 断言拒绝保存并提示","非法日期被拦截。","" +"VENDOR-HLD-007","基础设置 · 厂商管理","供应商节假日管理 · 维护","开始结束为同一天","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商节假日管理页(/spd/basic/mfrs/basic/prov/holiday,无 iframe)","1. 打开新增/编辑,开始与结束日期选同一天尝试保存(或仅校验控件) +2. 断言允许保存或明确提示规则(按产品规则),不报错白屏","同日区间规则明确。","" +"VENDOR-HLD-008","基础设置 · 厂商管理","供应商节假日管理 · 左右分栏","进院供应商主数据一致","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商节假日管理页(/spd/basic/mfrs/basic/prov/holiday,无 iframe)","1. 在自定义列表抽样进院供应商(若有数据) +2. 断言可在厂商/供应商主数据中找到对应记录","自定义节假日供应商与主数据一致。","" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..b4ca495 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,33 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"VENDOR-QUL-001","基础设置 · 厂商管理","供应商资质管理 · 页面进入","菜单进入供应商资质管理页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe)","1. 按菜单「基础设置 → 厂商管理 → 供应商资质管理」进入 +2. 断言 URL 含 `/spd/basic/mfrs/basic/qcManage` +3. 断言可见「查 询」「重 置」及证件效期提醒相关入口","进入供应商资质管理;不误入厂商查询。","" +"VENDOR-QUL-002","基础设置 · 厂商管理","供应商资质管理 · 筛选与 Tab","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 断言出现数据行或空态明确","查询无报错;有数据时分页 N≥1。","" +"VENDOR-QUL-003","基础设置 · 厂商管理","供应商资质管理 · 筛选与 Tab","切换授权链路效期预警 Tab","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe)","1. 点击「授权链路效期预警」 +2. 断言列表有数据或空态明确","Tab 切换成功,列表字段可随 Tab 变化。","" +"VENDOR-QUL-004","基础设置 · 厂商管理","供应商资质管理 · 筛选与 Tab","近三个月到期计数一致","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe)","1. 切换「近三个月到期」(或等价文案) +2. 若可读到括号/冒号计数,断言等于分页「共 N 条」","计数与分页一致(可读时)。","" +"VENDOR-QUL-005","基础设置 · 厂商管理","供应商资质管理 · 筛选与 Tab","按进院供应商筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe);证件效期提醒至少 1 条且进院供应商非空","1. 从首行读取 `${sampleHospitalSupplier}` 取关键字 +2. 在进院供应商名称填入关键字并「查 询」 +3. 断言结果行「进院供应商」包含关键字","进院供应商列与条件一致。","" +"VENDOR-QUL-006","基础设置 · 厂商管理","供应商资质管理 · 筛选与 Tab","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe)","1. 填写筛选条件后点击「重 置」 +2. 断言列表按默认规则刷新","筛选恢复默认。","" +"VENDOR-QUL-007","基础设置 · 厂商管理","供应商资质管理 · 筛选与 Tab","无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe)","1. 进院供应商名称填入 `__NOT_EXIST_SUPPLIER__99999` +2. 点击「查 询」 +3. 断言空态且分页总数为 0","无匹配时不报错;总数为 0。","" +"VENDOR-QUL-008","基础设置 · 厂商管理","供应商资质管理 · 筛选与 Tab","证件效期提醒核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe)","1. 处于证件效期提醒 Tab +2. 断言表头含序号、数据来源、进院供应商、有效期至、证件名称、证件类型、效期提醒、操作","核心列可见。","" +"VENDOR-QUL-009","基础设置 · 厂商管理","供应商资质管理 · 查看与导出","打开查看后关闭","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe);列表至少 1 行含「查看」","1. 点击首行「查 看」 +2. 断言详情层可见 +3. 关闭/返回,断言回到列表","详情可打开;关闭后回到列表。","" +"VENDOR-QUL-010","基础设置 · 厂商管理","供应商资质管理 · 查看与导出","导出按钮可见","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe)","1. 断言「导出」或「导出明细」按钮可见(本批不确认下载)","导出入口可见。","" +"VENDOR-QUL-011","基础设置 · 厂商管理","供应商资质管理 · 筛选与 Tab","制单/效期日期起止同一天","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe)","1. 若存在日期区间筛选,起止选同一天并查询 +2. 断言仅返回该日数据或空态明确,不报错","同日区间行为明确。","" +"VENDOR-QUL-012","基础设置 · 厂商管理","供应商资质管理 · 筛选与 Tab","效期提醒与有效期至抽样一致","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入供应商资质管理页(/spd/basic/mfrs/basic/qcManage,无 iframe)","1. 在近三个月到期列表抽样一行 +2. 核对「效期提醒」文案与「有效期至」日期逻辑一致","提醒文案与有效期至不矛盾。","" +"VENDOR-QUL-013","基础设置 · 厂商管理","供应商资质管理 · 筛选与 Tab","多条件组合查询(销售商名称等2项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 厂商管理」权限;已进入厂商管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「销售商名称」填写关键字 +3. 「证件类型」下拉选择任一可选项 +4. 点击「查询」 +5. 检查列表返回结果","多条件同时生效:销售商名称、证件类型 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..9c161e6 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,25 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"VENDOR-QRY-001","基础设置 · 厂商管理","厂商查询 · 页面进入","菜单进入厂商查询页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入厂商查询页(/spd/basic/mfrs/paltform/mfrsName/factory,无 iframe)","1. 按菜单「基础设置 → 厂商管理 → 厂商查询」进入 +2. 断言 URL 含 `/spd/basic/mfrs/paltform/mfrsName/factory` +3. 断言可见「器械注册人」「经销商」Tab、厂商名称搜索框及列表表头","进入厂商查询页;不误入供应商资质管理。","" +"VENDOR-QRY-002","基础设置 · 厂商管理","厂商查询 · Tab 与搜索","默认器械注册人列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入厂商查询页(/spd/basic/mfrs/paltform/mfrsName/factory,无 iframe)","1. 切换或停留「器械注册人」 +2. 断言出现数据行,或「暂无数据」且分页总数为 0","器械注册人列表稳定;有数据时分页「共 N 条」N≥1。","" +"VENDOR-QRY-003","基础设置 · 厂商管理","厂商查询 · Tab 与搜索","切换经销商 Tab","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入厂商查询页(/spd/basic/mfrs/paltform/mfrsName/factory,无 iframe)","1. 点击「经销商」 +2. 断言 Tab 激活,列表有数据或空态明确","经销商列表加载成功;总数可与器械注册人不同。","" +"VENDOR-QRY-004","基础设置 · 厂商管理","厂商查询 · Tab 与搜索","按厂商名称搜索","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入厂商查询页(/spd/basic/mfrs/paltform/mfrsName/factory,无 iframe);器械注册人至少 1 条且厂商名称非空","1. 从首行读取 `${sampleMfrsName}` 取关键字 +2. 在厂商名称搜索框填入关键字并回车 +3. 断言结果行「厂商名称」包含关键字","厂商名称列与关键字匹配。","" +"VENDOR-QRY-005","基础设置 · 厂商管理","厂商查询 · Tab 与搜索","清空搜索恢复","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入厂商查询页(/spd/basic/mfrs/paltform/mfrsName/factory,无 iframe);已按厂商名称过滤","1. 在已过滤状态下清空搜索框并回车 +2. 断言分页总数不少于过滤后","恢复更宽结果集。","" +"VENDOR-QRY-006","基础设置 · 厂商管理","厂商查询 · Tab 与搜索","无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入厂商查询页(/spd/basic/mfrs/paltform/mfrsName/factory,无 iframe)","1. 厂商名称填入 `__NOT_EXIST_MFRS__99999` 并回车 +2. 断言空态且分页总数为 0","无匹配时不报错;总数为 0。","" +"VENDOR-QRY-007","基础设置 · 厂商管理","厂商查询 · Tab 与搜索","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入厂商查询页(/spd/basic/mfrs/paltform/mfrsName/factory,无 iframe)","1. 断言表头含序号、厂商编号、厂商名称、产地、详细地址、简称、开户银行、银行账号","核心列可见。","" +"VENDOR-QRY-008","基础设置 · 厂商管理","厂商查询 · Tab 与搜索","分页翻至第 2 页","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入厂商查询页(/spd/basic/mfrs/paltform/mfrsName/factory,无 iframe);器械注册人总数足够翻页","1. 总数足够时翻到第 2 页 +2. 断言列表可加载;不足两页则 skip","第 2 页可加载。","" +"VENDOR-QRY-009","基础设置 · 厂商管理","厂商查询 · Tab 与搜索","仅空格关键字搜索","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入厂商查询页(/spd/basic/mfrs/paltform/mfrsName/factory,无 iframe)","1. 厂商名称仅填空格并回车 +2. 断言按空查询处理或提示,不白屏","不报错;行为明确。","" +"VENDOR-QRY-010","基础设置 · 厂商管理","厂商查询 · 页面进入","未登录访问拦截","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入厂商查询页(/spd/basic/mfrs/paltform/mfrsName/factory,无 iframe)","1. 清除登录态后直接打开厂商查询 URL +2. 断言跳转登录或不展示业务列表","未登录不可见厂商数据。","" +"VENDOR-QRY-011","基础设置 · 厂商管理","厂商查询 · Tab 与搜索","厂商名称与资质管理进院供应商可对照","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入厂商查询页(/spd/basic/mfrs/paltform/mfrsName/factory,无 iframe)","1. 记录厂商查询中某厂商名称(若有) +2. 打开供应商资质管理,用同源关键字检索或目视对照 +3. 断言不出现明显主数据串名(允许一方无记录)","厂商主数据跨菜单可对照,不串名。","" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..15cb0ce --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,36 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"VENDOR-AF1-001","基础设置 · 厂商管理","授权变更初审 · 页面进入","菜单进入授权变更初审页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更初审页(/spd/basic/mfrs/paltform/mfrsName/authAudit,无 iframe)","1. 按菜单「基础设置 → 厂商管理 → 授权变更初审」进入 +2. 断言 URL 含 `/spd/basic/mfrs/paltform/mfrsName/authAudit` +3. 断言可见「查 询」「重 置」及制单日期筛选","进入授权变更初审页;不误入其它变更审批菜单。","" +"VENDOR-AF1-002","基础设置 · 厂商管理","授权变更初审 · 筛选与列表","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更初审页(/spd/basic/mfrs/paltform/mfrsName/authAudit,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 断言出现数据行或空态明确","查询无报错;有数据时分页 N≥1。","" +"VENDOR-AF1-003","基础设置 · 厂商管理","授权变更初审 · 筛选与列表","按单号筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更初审页(/spd/basic/mfrs/paltform/mfrsName/authAudit,无 iframe);列表至少 1 条且单号非空","1. 从首行读取 `${sampleOrderNo}` 取关键字 +2. 单号填入关键字并「查 询」 +3. 断言结果行「单号」包含关键字","单号列与条件一致。","" +"VENDOR-AF1-004","基础设置 · 厂商管理","授权变更初审 · 筛选与列表","按进院供应商筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更初审页(/spd/basic/mfrs/paltform/mfrsName/authAudit,无 iframe);列表至少 1 条","1. 按样本填写或选择「进院供应商」并查询 +2. 断言结果列与条件匹配","筛选条件生效。","" +"VENDOR-AF1-005","基础设置 · 厂商管理","授权变更初审 · 筛选与列表","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更初审页(/spd/basic/mfrs/paltform/mfrsName/authAudit,无 iframe)","1. 填写筛选项后点击「重 置」 +2. 断言文本条件清空、列表按默认规则刷新","重置成功。","" +"VENDOR-AF1-006","基础设置 · 厂商管理","授权变更初审 · 筛选与列表","无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更初审页(/spd/basic/mfrs/paltform/mfrsName/authAudit,无 iframe)","1. 单号填入 `__NOT_EXIST_ORDER__99999` 并查询 +2. 断言空态且分页总数为 0","无匹配时不报错;总数为 0。","" +"VENDOR-AF1-007","基础设置 · 厂商管理","授权变更初审 · 筛选与列表","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更初审页(/spd/basic/mfrs/paltform/mfrsName/authAudit,无 iframe)","1. 断言表头含 进院供应商名称、单号、状态、制单时间、制单人、变更类型、原供应商、预转供应商、操作 +2. 断言存在「详情」操作","核心列与详情入口可见。","" +"VENDOR-AF1-008","基础设置 · 厂商管理","授权变更初审 · 详情","打开详情后返回","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更初审页(/spd/basic/mfrs/paltform/mfrsName/authAudit,无 iframe);存在含「详情」的行","1. 点击行内「详 情」 +2. 断言详情层可见且含单号片段 +3. 返回/关闭,不提交审批","详情只读可返回;列表状态不变。","" +"VENDOR-AF1-009","基础设置 · 厂商管理","授权变更初审 · 筛选与列表","制单日期起止同一天","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更初审页(/spd/basic/mfrs/paltform/mfrsName/authAudit,无 iframe)","1. 制单日期起止选同一天并查询 +2. 断言仅返回该日单据或空态明确","同日区间行为明确。","" +"VENDOR-AF1-010","基础设置 · 厂商管理","授权变更初审 · 详情","与终审单据衔接不串单","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更初审页(/spd/basic/mfrs/paltform/mfrsName/authAudit,无 iframe)","1. 记录本页某单号后切换到对应终审菜单 +2. 断言同业务单据状态流转合理、不串到非本业务菜单","初审/终审衔接正确。","" +"VENDOR-AF1-011","基础设置 · 厂商管理","授权变更初审 · 筛选与列表","多条件组合查询(制单日期等9项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 厂商管理」权限;已进入厂商管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「制单日期」日期区间选择当天 +3. 「单号」填写关键字 +4. 「销售商」下拉选择任一可选项 +5. 「预转供应商」填写关键字 +6. 「状态」下拉选择任一可选项 +7. 「变更类型」下拉选择任一可选项 +8. 「原供应商」填写关键字 +9. 「医院ID」填写关键字 +10. 「医院名称」填写关键字 +11. 点击「查询」 +12. 检查列表返回结果","多条件同时生效:制单日期、单号、销售商、预转供应商、状态、变更类型、原供应商、医院ID、医院名称 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..e627544 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,34 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"VENDOR-AF2-001","基础设置 · 厂商管理","授权变更终审 · 页面进入","菜单进入授权变更终审页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更终审页(/spd/basic/mfrs/paltform/mfrsName/authAuditFinal,无 iframe)","1. 按菜单「基础设置 → 厂商管理 → 授权变更终审」进入 +2. 断言 URL 含 `/spd/basic/mfrs/paltform/mfrsName/authAuditFinal` +3. 断言可见「查 询」「重 置」及制单日期筛选","进入授权变更终审页;不误入其它变更审批菜单。","" +"VENDOR-AF2-002","基础设置 · 厂商管理","授权变更终审 · 筛选与列表","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更终审页(/spd/basic/mfrs/paltform/mfrsName/authAuditFinal,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 断言出现数据行或空态明确","查询无报错;有数据时分页 N≥1。","" +"VENDOR-AF2-003","基础设置 · 厂商管理","授权变更终审 · 筛选与列表","按单号筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更终审页(/spd/basic/mfrs/paltform/mfrsName/authAuditFinal,无 iframe);列表至少 1 条且单号非空","1. 从首行读取 `${sampleOrderNo}` 取关键字 +2. 单号填入关键字并「查 询」 +3. 断言结果行「单号」包含关键字","单号列与条件一致。","" +"VENDOR-AF2-004","基础设置 · 厂商管理","授权变更终审 · 筛选与列表","按进院供应商筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更终审页(/spd/basic/mfrs/paltform/mfrsName/authAuditFinal,无 iframe);列表至少 1 条","1. 按样本填写或选择「进院供应商」并查询 +2. 断言结果列与条件匹配","筛选条件生效。","" +"VENDOR-AF2-005","基础设置 · 厂商管理","授权变更终审 · 筛选与列表","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更终审页(/spd/basic/mfrs/paltform/mfrsName/authAuditFinal,无 iframe)","1. 填写筛选项后点击「重 置」 +2. 断言文本条件清空、列表按默认规则刷新","重置成功。","" +"VENDOR-AF2-006","基础设置 · 厂商管理","授权变更终审 · 筛选与列表","无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更终审页(/spd/basic/mfrs/paltform/mfrsName/authAuditFinal,无 iframe)","1. 单号填入 `__NOT_EXIST_ORDER__99999` 并查询 +2. 断言空态且分页总数为 0","无匹配时不报错;总数为 0。","" +"VENDOR-AF2-007","基础设置 · 厂商管理","授权变更终审 · 筛选与列表","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更终审页(/spd/basic/mfrs/paltform/mfrsName/authAuditFinal,无 iframe)","1. 断言表头含 进院供应商名称、单号、状态、制单时间、制单人、变更类型、原供应商、预转供应商、操作 +2. 断言存在「详情」操作","核心列与详情入口可见。","" +"VENDOR-AF2-008","基础设置 · 厂商管理","授权变更终审 · 详情","打开详情后返回","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更终审页(/spd/basic/mfrs/paltform/mfrsName/authAuditFinal,无 iframe);存在含「详情」的行","1. 点击行内「详 情」 +2. 断言详情层可见且含单号片段 +3. 返回/关闭,不提交审批","详情只读可返回;列表状态不变。","" +"VENDOR-AF2-009","基础设置 · 厂商管理","授权变更终审 · 筛选与列表","制单日期起止同一天","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更终审页(/spd/basic/mfrs/paltform/mfrsName/authAuditFinal,无 iframe)","1. 制单日期起止选同一天并查询 +2. 断言仅返回该日单据或空态明确","同日区间行为明确。","" +"VENDOR-AF2-010","基础设置 · 厂商管理","授权变更终审 · 详情","与初审单据衔接不串单","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入授权变更终审页(/spd/basic/mfrs/paltform/mfrsName/authAuditFinal,无 iframe)","1. 记录本页某单号后切换到对应初审菜单 +2. 断言同业务单据状态流转合理、不串到非本业务菜单","初审/终审衔接正确。","" +"VENDOR-AF2-011","基础设置 · 厂商管理","授权变更终审 · 筛选与列表","多条件组合查询(制单日期等7项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 厂商管理」权限;已进入厂商管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「制单日期」日期区间选择当天 +3. 「单号」填写关键字 +4. 「销售商」下拉选择任一可选项 +5. 「预转供应商」填写关键字 +6. 「状态」下拉选择任一可选项 +7. 「变更类型」下拉选择任一可选项 +8. 「原供应商」填写关键字 +9. 点击「查询」 +10. 检查列表返回结果","多条件同时生效:制单日期、单号、销售商、预转供应商、状态、变更类型、原供应商 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..b3c7deb --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,33 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"VENDOR-CF1-001","基础设置 · 厂商管理","注册证变更初审 · 页面进入","菜单进入注册证变更初审页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更初审页(/spd/basic/mfrs/paltform/mfrsName/regCertAudit,无 iframe)","1. 按菜单「基础设置 → 厂商管理 → 注册证变更初审」进入 +2. 断言 URL 含 `/spd/basic/mfrs/paltform/mfrsName/regCertAudit` +3. 断言可见「查 询」「重 置」及制单日期筛选","进入注册证变更初审页;不误入其它变更审批菜单。","" +"VENDOR-CF1-002","基础设置 · 厂商管理","注册证变更初审 · 筛选与列表","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更初审页(/spd/basic/mfrs/paltform/mfrsName/regCertAudit,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 断言出现数据行或空态明确","查询无报错;有数据时分页 N≥1。","" +"VENDOR-CF1-003","基础设置 · 厂商管理","注册证变更初审 · 筛选与列表","按单号筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更初审页(/spd/basic/mfrs/paltform/mfrsName/regCertAudit,无 iframe);列表至少 1 条且单号非空","1. 从首行读取 `${sampleOrderNo}` 取关键字 +2. 单号填入关键字并「查 询」 +3. 断言结果行「单号」包含关键字","单号列与条件一致。","" +"VENDOR-CF1-004","基础设置 · 厂商管理","注册证变更初审 · 筛选与列表","按注册证号(新)筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更初审页(/spd/basic/mfrs/paltform/mfrsName/regCertAudit,无 iframe);列表至少 1 条","1. 按样本填写或选择「注册证号(新)」并查询 +2. 断言结果列与条件匹配","筛选条件生效。","" +"VENDOR-CF1-005","基础设置 · 厂商管理","注册证变更初审 · 筛选与列表","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更初审页(/spd/basic/mfrs/paltform/mfrsName/regCertAudit,无 iframe)","1. 填写筛选项后点击「重 置」 +2. 断言文本条件清空、列表按默认规则刷新","重置成功。","" +"VENDOR-CF1-006","基础设置 · 厂商管理","注册证变更初审 · 筛选与列表","无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更初审页(/spd/basic/mfrs/paltform/mfrsName/regCertAudit,无 iframe)","1. 单号填入 `__NOT_EXIST_ORDER__99999` 并查询 +2. 断言空态且分页总数为 0","无匹配时不报错;总数为 0。","" +"VENDOR-CF1-007","基础设置 · 厂商管理","注册证变更初审 · 筛选与列表","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更初审页(/spd/basic/mfrs/paltform/mfrsName/regCertAudit,无 iframe)","1. 断言表头含 进院供应商名称、单号、状态、制单时间、制单人、变更类型、注册证号(新)、操作 +2. 断言存在「详情」操作","核心列与详情入口可见。","" +"VENDOR-CF1-008","基础设置 · 厂商管理","注册证变更初审 · 详情","打开详情后返回","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更初审页(/spd/basic/mfrs/paltform/mfrsName/regCertAudit,无 iframe);存在含「详情」的行","1. 点击行内「详 情」 +2. 断言详情层可见且含单号片段 +3. 返回/关闭,不提交审批","详情只读可返回;列表状态不变。","" +"VENDOR-CF1-009","基础设置 · 厂商管理","注册证变更初审 · 筛选与列表","制单日期起止同一天","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更初审页(/spd/basic/mfrs/paltform/mfrsName/regCertAudit,无 iframe)","1. 制单日期起止选同一天并查询 +2. 断言仅返回该日单据或空态明确","同日区间行为明确。","" +"VENDOR-CF1-010","基础设置 · 厂商管理","注册证变更初审 · 详情","与终审单据衔接不串单","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更初审页(/spd/basic/mfrs/paltform/mfrsName/regCertAudit,无 iframe)","1. 记录本页某单号后切换到对应终审菜单 +2. 断言同业务单据状态流转合理、不串到非本业务菜单","初审/终审衔接正确。","" +"VENDOR-CF1-011","基础设置 · 厂商管理","注册证变更初审 · 筛选与列表","多条件组合查询(制单日期等6项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 厂商管理」权限;已进入厂商管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「制单日期」日期区间选择当天 +3. 「单号」填写关键字 +4. 「变更类型」下拉选择任一可选项 +5. 「注册证号(新)」填写关键字 +6. 「状态」下拉选择任一可选项 +7. 「变更情况具体说明」填写关键字 +8. 点击「查询」 +9. 检查列表返回结果","多条件同时生效:制单日期、单号、变更类型、注册证号(新)、状态、变更情况具体说明 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..2fe0195 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,32 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"VENDOR-CF2-001","基础设置 · 厂商管理","注册证变更终审 · 页面进入","菜单进入注册证变更终审页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更终审页(/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal,无 iframe)","1. 按菜单「基础设置 → 厂商管理 → 注册证变更终审」进入 +2. 断言 URL 含 `/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal` +3. 断言可见「查 询」「重 置」及制单日期筛选","进入注册证变更终审页;不误入其它变更审批菜单。","" +"VENDOR-CF2-002","基础设置 · 厂商管理","注册证变更终审 · 筛选与列表","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更终审页(/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal,无 iframe)","1. 点击「查 询」并等待列表稳定 +2. 断言出现数据行或空态明确","查询无报错;有数据时分页 N≥1。","" +"VENDOR-CF2-003","基础设置 · 厂商管理","注册证变更终审 · 筛选与列表","按单号筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更终审页(/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal,无 iframe);列表至少 1 条且单号非空","1. 从首行读取 `${sampleOrderNo}` 取关键字 +2. 单号填入关键字并「查 询」 +3. 断言结果行「单号」包含关键字","单号列与条件一致。","" +"VENDOR-CF2-004","基础设置 · 厂商管理","注册证变更终审 · 筛选与列表","按注册证号(新)筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更终审页(/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal,无 iframe);列表至少 1 条","1. 按样本填写或选择「注册证号(新)」并查询 +2. 断言结果列与条件匹配","筛选条件生效。","" +"VENDOR-CF2-005","基础设置 · 厂商管理","注册证变更终审 · 筛选与列表","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更终审页(/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal,无 iframe)","1. 填写筛选项后点击「重 置」 +2. 断言文本条件清空、列表按默认规则刷新","重置成功。","" +"VENDOR-CF2-006","基础设置 · 厂商管理","注册证变更终审 · 筛选与列表","无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更终审页(/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal,无 iframe)","1. 单号填入 `__NOT_EXIST_ORDER__99999` 并查询 +2. 断言空态且分页总数为 0","无匹配时不报错;总数为 0。","" +"VENDOR-CF2-007","基础设置 · 厂商管理","注册证变更终审 · 筛选与列表","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更终审页(/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal,无 iframe)","1. 断言表头含 进院供应商名称、单号、状态、制单时间、制单人、变更类型、注册证号(新)、操作 +2. 断言存在「详情」操作","核心列与详情入口可见。","" +"VENDOR-CF2-008","基础设置 · 厂商管理","注册证变更终审 · 详情","打开详情后返回","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更终审页(/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal,无 iframe);存在含「详情」的行","1. 点击行内「详 情」 +2. 断言详情层可见且含单号片段 +3. 返回/关闭,不提交审批","详情只读可返回;列表状态不变。","" +"VENDOR-CF2-009","基础设置 · 厂商管理","注册证变更终审 · 筛选与列表","制单日期起止同一天","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更终审页(/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal,无 iframe)","1. 制单日期起止选同一天并查询 +2. 断言仅返回该日单据或空态明确","同日区间行为明确。","" +"VENDOR-CF2-010","基础设置 · 厂商管理","注册证变更终审 · 详情","与初审单据衔接不串单","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证变更终审页(/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal,无 iframe)","1. 记录本页某单号后切换到对应初审菜单 +2. 断言同业务单据状态流转合理、不串到非本业务菜单","初审/终审衔接正确。","" +"VENDOR-CF2-011","基础设置 · 厂商管理","注册证变更终审 · 筛选与列表","多条件组合查询(制单日期等5项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 厂商管理」权限;已进入厂商管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「制单日期」日期区间选择当天 +3. 「单号」填写关键字 +4. 「变更类型」下拉选择任一可选项 +5. 「注册证号(新)」填写关键字 +6. 「状态」下拉选择任一可选项 +7. 点击「查询」 +8. 检查列表返回结果","多条件同时生效:制单日期、单号、变更类型、注册证号(新)、状态 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..4bbbcd7 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,34 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"VENDOR-CDF-001","基础设置 · 厂商管理","注册证差异对比 · 页面进入","菜单进入注册证差异对比页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证差异对比页(/spd/basic/mfrs/scjs/yc/certCompare,无 iframe)","1. 按菜单「基础设置 → 厂商管理 → 注册证差异对比」进入 +2. 断言 URL 含 `certCompare` +3. 断言外壳或 iframe 内可见物料名称/全部/一致/不一致等主区","进入差异对比页;iframe 或主区加载成功。","" +"VENDOR-CDF-002","基础设置 · 厂商管理","注册证差异对比 · 筛选与对比","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证差异对比页(/spd/basic/mfrs/scjs/yc/certCompare,无 iframe)","1. 在对比页执行查询(若有查询按钮) +2. 断言列表有数据或空态明确","查询无报错。","" +"VENDOR-CDF-003","基础设置 · 厂商管理","注册证差异对比 · 筛选与对比","切换不一致 Tab","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证差异对比页(/spd/basic/mfrs/scjs/yc/certCompare,无 iframe)","1. 点击「不一致」 +2. 断言列表稳定","不一致 Tab 切换成功。","" +"VENDOR-CDF-004","基础设置 · 厂商管理","注册证差异对比 · 筛选与对比","切换一致 Tab","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证差异对比页(/spd/basic/mfrs/scjs/yc/certCompare,无 iframe)","1. 点击「一致」 +2. 断言列表稳定","一致 Tab 切换成功。","" +"VENDOR-CDF-005","基础设置 · 厂商管理","注册证差异对比 · 筛选与对比","按物料名称筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证差异对比页(/spd/basic/mfrs/scjs/yc/certCompare,无 iframe);对比列表至少 1 条","1. 从首行读取 `${sampleMaterialName}` 取关键字 +2. 物料名称填入并查询 +3. 断言结果行物料名称包含关键字","物料名称匹配。","" +"VENDOR-CDF-006","基础设置 · 厂商管理","注册证差异对比 · 筛选与对比","无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证差异对比页(/spd/basic/mfrs/scjs/yc/certCompare,无 iframe)","1. 物料名称填 mock 关键字并查询 +2. 断言空态或总数为 0","无匹配时不报错。","" +"VENDOR-CDF-007","基础设置 · 厂商管理","注册证差异对比 · 筛选与对比","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证差异对比页(/spd/basic/mfrs/scjs/yc/certCompare,无 iframe)","1. 断言表头含物料编号/物料名称/规格型号/生产厂家等关键列","核心对比列可见。","" +"VENDOR-CDF-008","基础设置 · 厂商管理","注册证差异对比 · 导出","导出按钮可见","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证差异对比页(/spd/basic/mfrs/scjs/yc/certCompare,无 iframe)","1. 断言「导出」按钮可见(不确认下载)","导出入口可见。","" +"VENDOR-CDF-009","基础设置 · 厂商管理","注册证差异对比 · 筛选与对比","仅空格物料名称搜索","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证差异对比页(/spd/basic/mfrs/scjs/yc/certCompare,无 iframe)","1. 物料名称仅填空格并查询 +2. 断言按空查询处理或提示,不白屏","空格关键字不导致页面异常。","" +"VENDOR-CDF-010","基础设置 · 厂商管理","注册证差异对比 · 筛选与对比","一致行两侧字段抽样一致","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证差异对比页(/spd/basic/mfrs/scjs/yc/certCompare,无 iframe)","1. 在「一致」Tab 抽样一行 +2. 核对望海与 FLI+ 注册证号/效期关键字段一致","标记一致的行两侧关键字段抽样相同。","" +"VENDOR-CDF-011","基础设置 · 厂商管理","注册证差异对比 · 筛选与对比","不一致行至少一侧差异","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入注册证差异对比页(/spd/basic/mfrs/scjs/yc/certCompare,无 iframe)","1. 在「不一致」Tab 抽样一行 +2. 断言望海与 FLI+ 至少一处字段不同","不一致结论与字段差异相符。","" +"VENDOR-CDF-012","基础设置 · 厂商管理","注册证差异对比 · 筛选与对比","多条件组合查询(物料名称等8项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 厂商管理」权限;已进入厂商管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「物料名称」填写关键字 +3. 「规格型号」填写关键字 +4. 「状态」下拉选择任一可选项 +5. 「望海注册证号」填写关键字 +6. 「是否医疗器械」下拉选择任一可选项 +7. 「生产厂家」填写关键字 +8. 「FLI+注册证号」填写关键字 +9. 「27位医保编码」填写关键字 +10. 点击「查询」 +11. 检查列表返回结果","多条件同时生效:物料名称、规格型号、状态、望海注册证号、是否医疗器械、生产厂家、FLI+注册证号、27位医保编码 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..40a8541 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,25 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"VENDOR-EVI-001","基础设置 · 厂商管理","评价指标维护 · 页面进入","菜单进入评价指标维护页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价指标维护页(/spd/basic/mfrs/paltform/mfrsName/indicators,无 iframe)","1. 按菜单进入评价指标维护 +2. 断言 URL 含 `indicators` +3. 断言可见「指标分类」与查询","进入评价指标维护页。","" +"VENDOR-EVI-002","基础设置 · 厂商管理","评价指标维护 · 树与列表","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价指标维护页(/spd/basic/mfrs/paltform/mfrsName/indicators,无 iframe)","1. 点击查询 +2. 断言右表有数据或空态","查询无报错。","" +"VENDOR-EVI-003","基础设置 · 厂商管理","评价指标维护 · 树与列表","点击左侧分类树","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价指标维护页(/spd/basic/mfrs/paltform/mfrsName/indicators,无 iframe)","1. 点击左侧首个分类节点 +2. 断言右侧列表刷新","分类切换成功。","" +"VENDOR-EVI-004","基础设置 · 厂商管理","评价指标维护 · 树与列表","按指标编码筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价指标维护页(/spd/basic/mfrs/paltform/mfrsName/indicators,无 iframe);右表有数据","1. 取样本指标编码/编号筛选并查询 +2. 断言编码列匹配","指标编码匹配。","" +"VENDOR-EVI-005","基础设置 · 厂商管理","评价指标维护 · 树与列表","无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价指标维护页(/spd/basic/mfrs/paltform/mfrsName/indicators,无 iframe)","1. 指标编码填 mock 后查询 +2. 断言空态","无匹配不报错。","" +"VENDOR-EVI-006","基础设置 · 厂商管理","评价指标维护 · 树与列表","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价指标维护页(/spd/basic/mfrs/paltform/mfrsName/indicators,无 iframe)","1. 断言表头含评价类型、指标编码或指标编号、指标名称、状态、操作","核心列可见。","" +"VENDOR-EVI-007","基础设置 · 厂商管理","评价指标维护 · 详情","打开详情后返回","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价指标维护页(/spd/basic/mfrs/paltform/mfrsName/indicators,无 iframe);存在详情操作行","1. 点击行内详情 +2. 断言详情层可见 +3. 返回不保存","详情只读可返回。","" +"VENDOR-EVI-008","基础设置 · 厂商管理","评价指标维护 · 树与列表","分类下无指标空态","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价指标维护页(/spd/basic/mfrs/paltform/mfrsName/indicators,无 iframe)","1. 选择无指标的分类(若有) +2. 断言右表空态明确","空分类下列表空态正确。","" +"VENDOR-EVI-009","基础设置 · 厂商管理","评价指标维护 · 树与列表","启用指标可供方案使用","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价指标维护页(/spd/basic/mfrs/paltform/mfrsName/indicators,无 iframe)","1. 确认状态为启用的指标 +2. 在评价方案维护中核对其可选(只读核对或抽样)","启用指标与方案引用衔接正确。","" +"VENDOR-EVI-010","基础设置 · 厂商管理","评价指标维护 · 树与列表","多条件组合查询(指标编码等2项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 厂商管理」权限;已进入厂商管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「指标编码」填写关键字 +3. 「指标名称」填写关键字 +4. 点击「查询」 +5. 检查列表返回结果","多条件同时生效:指标编码、指标名称 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..c5a164a --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,30 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"VENDOR-EVS-001","基础设置 · 厂商管理","评价方案维护 · 页面进入","菜单进入评价方案维护页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价方案维护页(/spd/basic/mfrs/paltform/mfrsName/evaluationPlan,无 iframe)","1. 按菜单进入评价方案维护 +2. 断言 URL 含 `evaluationPlan` +3. 断言可见查询与新增","进入评价方案维护页。","" +"VENDOR-EVS-002","基础设置 · 厂商管理","评价方案维护 · 筛选与维护","默认查询列表有数据或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价方案维护页(/spd/basic/mfrs/paltform/mfrsName/evaluationPlan,无 iframe)","1. 点击查询 +2. 断言列表有数据或「暂无数据」空态","查询无报错。","" +"VENDOR-EVS-003","基础设置 · 厂商管理","评价方案维护 · 筛选与维护","按方案编码筛选","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价方案维护页(/spd/basic/mfrs/paltform/mfrsName/evaluationPlan,无 iframe);列表有数据","1. 取样本方案编码筛选并查询 +2. 断言方案编码列匹配","方案编码匹配。","" +"VENDOR-EVS-004","基础设置 · 厂商管理","评价方案维护 · 筛选与维护","重置恢复默认","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价方案维护页(/spd/basic/mfrs/paltform/mfrsName/evaluationPlan,无 iframe)","1. 填写筛选后重置 +2. 断言条件清空、列表刷新","重置成功。","" +"VENDOR-EVS-005","基础设置 · 厂商管理","评价方案维护 · 筛选与维护","无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价方案维护页(/spd/basic/mfrs/paltform/mfrsName/evaluationPlan,无 iframe)","1. 方案编码填 mock 后查询 +2. 断言空态总数 0","无匹配不报错。","" +"VENDOR-EVS-006","基础设置 · 厂商管理","评价方案维护 · 筛选与维护","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价方案维护页(/spd/basic/mfrs/paltform/mfrsName/evaluationPlan,无 iframe)","1. 断言表头含方案编码、方案名称、创建人、创建时间、状态、操作","核心列可见。","" +"VENDOR-EVS-007","基础设置 · 厂商管理","评价方案维护 · 新增","打开新增后取消","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价方案维护页(/spd/basic/mfrs/paltform/mfrsName/evaluationPlan,无 iframe)","1. 记录总数 +2. 点击新增打开编辑层 +3. 取消/返回不提交 +4. 断言总数不变","取消不落库。","" +"VENDOR-EVS-008","基础设置 · 厂商管理","评价方案维护 · 新增","必填为空提交拦截","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价方案维护页(/spd/basic/mfrs/paltform/mfrsName/evaluationPlan,无 iframe)","1. 打开新增不填必填项尝试提交(若可点) +2. 断言拦截提示,页面不关闭或总数不变","空提交被拦截。","" +"VENDOR-EVS-009","基础设置 · 厂商管理","评价方案维护 · 筛选与维护","方案名称超长输入","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价方案维护页(/spd/basic/mfrs/paltform/mfrsName/evaluationPlan,无 iframe)","1. 打开新增,方案名称输入超长字符串(如 200+ 字符) +2. 断言前端截断/校验提示或后端拒绝,不白屏","超长输入被约束且页面可用。","" +"VENDOR-EVS-010","基础设置 · 厂商管理","评价方案维护 · 筛选与维护","方案与评价指标可关联","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 厂商管理」权限;已进入评价方案维护页(/spd/basic/mfrs/paltform/mfrsName/evaluationPlan,无 iframe)","1. 在方案配置中查看指标选择(只读打开) +2. 断言可选指标来源于评价指标维护启用项或规则允许集合","方案与指标主数据衔接正确。","" +"VENDOR-EVS-011","基础设置 · 厂商管理","评价方案维护 · 筛选与维护","多条件组合查询(创建日期等4项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 厂商管理」权限;已进入厂商管理对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「创建日期」日期区间选择当天 +3. 「启用日期」日期区间选择当天 +4. 「方案编码」填写关键字 +5. 「方案名称」填写关键字 +6. 点击「查询」 +7. 检查列表返回结果","多条件同时生效:创建日期、启用日期、方案编码、方案名称 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..fcb517a --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,77 @@ +用例编号,功能模块,功能测试点,用例标题,优先级,前置条件,测试步骤,预期结果,实际结果 +WH-ZON-001,基础设置 · 库房管理,库区设置 · 页面进入,菜单进入库区设置页,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 按菜单「基础设置 → 库房管理 → 库区设置」进入,或 `openPath` 打开 `/spd/basic/storehouse/platform/storehouse/area` +3. 断言 URL 含 `/spd/basic/storehouse/platform/storehouse/area` +4. 断言左侧「库房信息」树、右侧库区列表、「新 增」与分页可见",进入库区设置页;不误入探针绑定;无报错遮罩。, +WH-ZON-002,基础设置 · 库房管理,库区设置 · 页面进入,未登录访问拦截,高,测试环境可访问;使用独立 context,storageState 为空(不加载 test_1 登录态),"1. 新建独立 BrowserContext,不加载 auth/user.json +2. 清除登录态后直接打开 `/spd/basic/storehouse/platform/storehouse/area` +3. 断言跳转登录或不展示库房/库区业务数据",未登录不可见库区数据。, +WH-ZON-003,基础设置 · 库房管理,库区设置 · 左侧库房树,库房树默认有选中项,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 断言左侧「库房信息」至少 1 个节点且有选中项 +3. 读取选中节点 `${sampleWhName}` +4. 断言右侧列表为该库房库区,或空态且「共 0 条」",树有选中;右侧对应当前库房。, +WH-ZON-004,基础设置 · 库房管理,库区设置 · 左侧库房树,按库房名称过滤树,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe);库房树至少 1 个节点,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 读取当前选中 `${sampleWhName}` 取关键字(可截取前若干字) +3. 在左侧「库房名称」输入关键字 +4. 断言可见节点名称均包含该关键字",树过滤结果与关键字匹配。, +WH-ZON-005,基础设置 · 库房管理,库区设置 · 左侧库房树,清空库房名称恢复全量树,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录过滤前节点数 +3. 输入关键字过滤后再清空 +4. 断言节点数恢复为过滤前",清空后树恢复全量。, +WH-ZON-006,基础设置 · 库房管理,库区设置 · 左侧库房树,切换库房刷新右侧列表,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录当前库房 `${sampleWhName}` 与右侧首行库区编号(若有) +3. 点选另一库房节点(仅 1 个则 skip) +4. 断言「共 N 条」可变化;若有行则库区编号/名称属于新库房",切换库房后右侧列表刷新,不残留上一库房库区。, +WH-ZON-007,基础设置 · 库房管理,库区设置 · 左侧库房树,无匹配库房空树(mock反例),中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 库房名称填入 `__NOT_EXIST_ZONE__99999` +3. 断言树为空或明确无数据,右侧不展示上一库房库区为成功态",无匹配不报错;不残留上一库房列表当成功。, +WH-ZON-008,基础设置 · 库房管理,库区设置 · 右侧库区列表与分页,列表核心列完整,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 断言表头含库区编号、库区名称、库区地址、是否专项库区、操作 +3. 若有数据行,断言操作列可见「编辑」「人员管理」「货位管理」及「停用」或「启用」",核心列完整;行内操入口可见。, +WH-ZON-009,基础设置 · 库房管理,库区设置 · 右侧库区列表与分页,默认库区以标签展示,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 在当前库房列表中查找「默认库区」标签 +3. 断言该标签出现在库区名称旁,不是独立列 +4. 断言当前库房至多 1 个默认库区标签(无默认则本条只断言不是独立列)",默认库区为名称旁标签;同一库房至多一个。, +WH-ZON-010,基础设置 · 库房管理,库区设置 · 库区搜索,按库区编号搜索,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe);当前库房至少 1 条库区且编号非空,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 从首行读取 `${sampleZoneNo}` +3. 在「库区编号/名称」填入该编号并触发搜索 +4. 断言结果包含该编号行,且均属于当前选中库房",搜索命中该编号;不串其它库房。, +WH-ZON-011,基础设置 · 库房管理,库区设置 · 库区搜索,按库区名称搜索,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe);当前库房至少 1 条且名称非空,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 从首行读取 `${sampleZoneName}` 取关键字 +3. 在「库区编号/名称」填入关键字并搜索 +4. 断言结果行名称匹配",名称关键字命中;无未捕获异常。, +WH-ZON-012,基础设置 · 库房管理,库区设置 · 库区搜索,无匹配库区空态(mock反例),中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 「库区编号/名称」填入 `__NOT_EXIST_ZONE__99999` 并搜索 +3. 断言列表空且分页为 0",无匹配不报错;总数为 0。, +WH-ZON-013,基础设置 · 库房管理,库区设置 · 新增库区,打开新增库区弹窗,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击「新 增」 +3. 断言弹出「新增库区」,可见库区编号、库区名称、库区地址、联系人、联系方式及「取 消」「确 定」",新增弹窗可见;关键字段与按钮齐全。, +WH-ZON-014,基础设置 · 库房管理,库区设置 · 新增库区,新增取消不落库,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开新增,编号填 `AUTO${timestamp}`,名称填任意合法值 +3. 点击「取 消」 +4. 按该编号搜索,断言无该记录",弹窗关闭;列表不出现该编号。, +WH-ZON-015,基础设置 · 库房管理,库区设置 · 新增库区,必填为空拦截,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开新增,库区编号或名称留空后点击「确 定」 +3. 断言出现必填提示,弹窗仍打开 +4. 点「取 消」",确定被拦截;不落库;弹窗不关闭。, +WH-ZON-016,基础设置 · 库房管理,库区设置 · 编辑库区,打开编辑并回填,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe);当前库房至少 1 条库区,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击首行「编辑」 +3. 断言弹窗标题含「编辑库区信息」 +4. 断言库区编号、名称与该行 `${sampleZoneNo}`、`${sampleZoneName}` 一致(允许规范化空白) +5. 点「取 消」",编辑弹窗可见;回填与列表行一致。, +WH-ZON-017,基础设置 · 库房管理,库区设置 · 编辑库区,编辑取消不保存,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe);当前库房至少 1 条库区,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开首行编辑,记录原名称 +3. 将名称改为临时值后点「取 消」 +4. 再次打开编辑,断言名称仍为原值",取消后列表与回看均为修改前名称。, +WH-ZON-018,基础设置 · 库房管理,库区设置 · 编辑库区,编辑清空必填拦截,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe);当前库房至少 1 条库区,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开首行编辑,清空库区名称后点「确 定」 +3. 断言被拦截并提示,弹窗仍开 +4. 点「取 消」,断言列表名称未被写成空",清空必填不能保存;原记录名称保持。, +WH-ZON-019,基础设置 · 库房管理,库区设置 · 人员管理,打开人员管理后关闭,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe);当前库房至少 1 条库区,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击首行「人员管理」 +3. 断言打开弹层或子页,标题含人员管理或对应当前库区 +4. 关闭后断言仍在库区设置,当前库房选中保持",人员管理可打开;关闭后回到当前库房列表。本条不改人员。, +WH-ZON-020,基础设置 · 库房管理,库区设置 · 货位管理,打开货位管理后关闭,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 库区设置」权限;已进入库区设置页(/spd/basic/storehouse/platform/storehouse/area,无 iframe);当前库房至少 1 条库区,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击首行「货位管理」 +3. 断言打开弹层或子页,标题含货位且对应当前库区 +4. 关闭后断言库房选中与库区列表保持",货位管理可打开;关闭后列表状态保持。本条不改货位。, diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..f01bfe5 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,66 @@ +用例编号,功能模块,功能测试点,用例标题,优先级,前置条件,测试步骤,预期结果,实际结果 +WH-PRB-001,基础设置 · 库房管理,探针绑定 · 页面进入,菜单进入探针绑定页,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 按菜单「基础设置 → 库房管理 → 探针绑定」进入,或 `openPath` 打开 `/spd/basic/storehouse/storehouse/probe` +3. 断言 URL 含 `/spd/basic/storehouse/storehouse/probe` +4. 断言探针ID、主机编码、是否绑定货位、查询、重置、列表与分页可见",进入探针绑定页;不误入库区设置。, +WH-PRB-002,基础设置 · 库房管理,探针绑定 · 页面进入,未登录访问拦截,高,测试环境可访问;使用独立 context,storageState 为空(不加载 test_1 登录态),"1. 新建独立 BrowserContext,不加载 auth/user.json +2. 清除登录态后直接打开 `/spd/basic/storehouse/storehouse/probe` +3. 断言跳转登录或不展示探针业务列表",未登录不可见探针数据。, +WH-PRB-003,基础设置 · 库房管理,探针绑定 · 筛选与查询,默认查询列表有数据或空态明确,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击「查 询」并等待列表稳定 +3. 断言出现数据行,或「暂无数据」且分页总数为 0",查询无报错;有数据时「共 N 条」N≥1。, +WH-PRB-004,基础设置 · 库房管理,探针绑定 · 筛选与查询,按探针ID查询,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe);默认查询至少 1 条且探针ID非空,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 从首行读取 `${sampleProbeId}` +3. 探针ID填入该值,点击「查 询」 +4. 断言结果探针ID匹配,精确命中时行数合理",探针ID条件生效。, +WH-PRB-005,基础设置 · 库房管理,探针绑定 · 筛选与查询,按主机编码查询,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe);默认查询至少 1 条且主机编码非空,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 从首行读取 `${sampleHostCode}` +3. 主机编码填入该值,点击「查 询」 +4. 断言结果行「主机编码」与条件一致",主机编码条件生效;允许多探针共用同一主机。, +WH-PRB-006,基础设置 · 库房管理,探针绑定 · 筛选与查询,按是否绑定货位查询,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 是否绑定货位选择「已绑定」,点击「查 询」 +3. 断言结果「是否绑定货位」均为已绑定,且「绑定的货位」>0 +4. 再选「未绑定」查询,断言均为未绑定且数量为 0(无数据则空态)",已绑定数量大于 0;未绑定数量为 0。, +WH-PRB-007,基础设置 · 库房管理,探针绑定 · 筛选与查询,探针ID+主机编码组合查询,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe);默认查询至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 填写 `${sampleProbeId}` 与 `${sampleHostCode}` 后点击「查 询」 +3. 断言结果同时满足已填条件",组合条件同时生效。, +WH-PRB-008,基础设置 · 库房管理,探针绑定 · 筛选与查询,重置恢复默认,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 填写探针ID后点击「重 置」 +3. 断言条件恢复默认,列表按默认规则刷新",条件恢复默认;列表刷新。, +WH-PRB-009,基础设置 · 库房管理,探针绑定 · 筛选与查询,无匹配空态(mock反例),中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 探针ID填入 `__NOT_EXIST_PROBE__99999`,点击「查 询」 +3. 断言空态且分页总数为 0",无匹配不报错;总数为 0。, +WH-PRB-010,基础设置 · 库房管理,探针绑定 · 列表与分页,列表核心列与数量规则,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 断言表头含序号、探针ID、主机编码、是否绑定货位、绑定的货位、操作 +3. 抽样已绑定行:绑定数量为数字且 >0;抽样未绑定行:数量为 0 +4. 断言「绑定的货位」列不是货位号格式",核心列完整;数量列是数字不是货位号。, +WH-PRB-011,基础设置 · 库房管理,探针绑定 · 打开绑定货位弹窗,行内打开货位绑定探针,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击首行「绑定货位」 +3. 断言弹出「货位绑定探针」,可见当前库房/库区/货位查询及货位列表",弹窗标题正确;筛选与列表可见。, +WH-PRB-012,基础设置 · 库房管理,探针绑定 · 打开绑定货位弹窗,取消关闭不改主列表,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录主列表首行绑定数量与当前筛选 +3. 打开绑定弹窗后点「取 消」 +4. 断言主列表绑定数量与筛选条件保持",取消不改绑定关系。, +WH-PRB-013,基础设置 · 库房管理,探针绑定 · 打开绑定货位弹窗,未选货位时确定计数为 0,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开绑定弹窗,不勾选货位 +3. 断言底部按钮文案为「确定(0)」或等价 +4. 点「取 消」,不点确定保存",未选时计数为 0;本条不保存绑定。, +WH-PRB-014,基础设置 · 库房管理,探针绑定 · 打开绑定货位弹窗,弹窗分页独立于主列表,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录主列表「共 N 条」 +3. 打开绑定弹窗,记录弹窗分页「共 M 条」 +4. 断言不得用 M 断言主列表 N(货位量级可远大于探针条数) +5. 点「取 消」",弹窗总条数与主列表探针条数相互独立。, +WH-PRB-015,基础设置 · 库房管理,探针绑定 · 弹窗筛选货位,按当前库房筛选货位,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开绑定弹窗,记录默认 `${sampleModalWh}` +3. 点「查 询」 +4. 断言可见行「当前库房」与所选库房一致(有数据时) +5. 点「取 消」",弹窗库房条件生效。, +WH-PRB-016,基础设置 · 库房管理,探针绑定 · 弹窗筛选货位,弹窗内重置,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开绑定弹窗,填写货位查询关键字后点「重 置」 +3. 断言货位查询条件恢复,列表按弹窗默认规则刷新 +4. 点「取 消」",弹窗重置不影响主列表筛选。, +WH-PRB-017,基础设置 · 库房管理,探针绑定 · 勾选货位并保存绑定,勾选后确定计数增加再取消勾选,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 库房管理 → 探针绑定」权限;已进入探针绑定页(/spd/basic/storehouse/storehouse/probe,无 iframe);弹窗货位列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开绑定弹窗,勾选 1 个货位 +3. 断言确定按钮计数为 1 +4. 取消该勾选,断言计数回到 0 +5. 点「取 消」,不保存",计数随勾选变化;取消勾选不落库。, diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..12272f3 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,72 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"SURG-BASE-001","基础设置 · 手术套包设置","基础包维护 · 左侧分类树","分类名称搜索过滤","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe)","1. 从左树读取 `${sampleCategoryName}` +2. 在分类名称搜索框填入该关键字(即时过滤或回车,以现网为准) +3. 断言树节点文本均包含该关键字","过滤后树节点名称与关键字匹配;无未捕获异常。","" +"SURG-BASE-002","基础设置 · 手术套包设置","基础包维护 · 左侧分类树","选择分类联动右侧列表","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);默认列表至少 1 条数据","1. 点击 `${sampleCategoryName}` 分类节点 +2. 等待右侧列表刷新稳定 +3. 断言列表加载无报错,分页「共 N 条」与当前分类规模一致","右侧仅展示该分类下基础包;总数与筛选一致。","" +"SURG-BASE-003","基础设置 · 手术套包设置","基础包维护 · 左侧分类树","打开新增分类弹窗","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe)","1. 点击「新增分类」 +2. 断言弹窗可见,含分类名称输入(#form_item_kindName)与「取 消」「确 认」 +3. 点击「取 消」关闭","新增分类弹窗字段完整;取消可关闭。","" +"SURG-BASE-004","基础设置 · 手术套包设置","基础包维护 · 左侧分类树","分类搜索清空恢复全量","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe)","1. 用 `${sampleCategoryName}` 过滤并记录过滤后节点数 +2. 清空分类搜索框 +3. 断言节点数回到未过滤的默认集合(或不少于过滤后节点数)","清空后分类树恢复默认集合。","" +"SURG-BASE-005","基础设置 · 手术套包设置","基础包维护 · 右侧筛选与列表","默认列出基础包或空态明确","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe)","1. 等待列表稳定 +2. 断言出现数据行,或「暂无数据」且分页总数为 0","加载无报错;有数据时「共 N 条」N≥1;无数据时空态明确。","" +"SURG-BASE-006","基础设置 · 手术套包设置","基础包维护 · 右侧筛选与列表","产品名称筛选过滤","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);默认列表至少 1 条数据","1. 从首行读取 `${sampleProductName}` +2. 在 `#table_search_form_keyword` 填入该关键字并点击「查 询」 +3. 断言结果行产品名称均包含该关键字","过滤结果与关键字匹配;「共 N 条」随之更新。","" +"SURG-BASE-007","基础设置 · 手术套包设置","基础包维护 · 右侧筛选与列表","规格型号筛选过滤","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);默认列表至少 1 条数据","1. 从首行读取 `${sampleSpecModel}` +2. 在 `#table_search_form_goodsSpec` 填入该关键字并点击「查 询」 +3. 断言结果行规格型号均包含该关键字","按规格型号过滤生效。","" +"SURG-BASE-008","基础设置 · 手术套包设置","基础包维护 · 右侧筛选与列表","器械注册人筛选过滤","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);默认列表至少 1 条数据","1. 从首行读取 `${sampleMfrsName}` +2. 在 `#table_search_form_goodsMfrsName` 填入该关键字并点击「查 询」 +3. 断言结果行器械注册人均包含该关键字","按器械注册人过滤生效。","" +"SURG-BASE-009","基础设置 · 手术套包设置","基础包维护 · 右侧筛选与列表","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);默认列表至少 1 条数据","1. 断言表头包含序号、产品编号、产品名称、规格型号、器械注册人、单位、产地、耗材类型、操作","核心列均可见,操作列含「编 辑」「移 除」。","" +"SURG-BASE-010","基础设置 · 手术套包设置","基础包维护 · 右侧筛选与列表","查询与重置","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);默认列表至少 1 条数据","1. 用关键字过滤后点击「重 置」 +2. 断言筛选输入框被清空、列表恢复默认集合","重置清空筛选并恢复默认列表。","" +"SURG-BASE-011","基础设置 · 手术套包设置","基础包维护 · 右侧筛选与列表","分页与页大小切换","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);默认列表至少 1 条数据;列表总数大于单页行数上限","1. 切换每页条数(如 50 → 100) +2. 断言单页行数不超过所选值,分页总数不变","页大小切换生效;总数保持稳定。","" +"SURG-BASE-012","基础设置 · 手术套包设置","基础包维护 · 右侧筛选与列表","首页上一页不可用","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe)","1. 处于第 1 页 +2. 断言「上一页」置灰或点击无效果","第 1 页无法回到上一页。","" +"SURG-BASE-013","基础设置 · 手术套包设置","基础包维护 · 右侧筛选与列表","搜索无匹配空态(mock反例)","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe)","1. 在 `#table_search_form_keyword` 输入 mock 不存在关键字 `__NOT_EXIST_BASE__99999` +2. 点击「查 询」 +3. 断言「暂无数据」且分页总数为 0","无匹配时空态明确,不白屏;总数为 0。","" +"SURG-BASE-014","基础设置 · 手术套包设置","基础包维护 · 新增基础包","打开新增基础包弹窗","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe)","1. 点击「新增基础包」 +2. 断言弹窗可见,含基础包名称(#form_item_pkgName)、所属分类(#form_item_kindId)、包编码(#form_item_pkgCode)、备注(#form_item_remark) +3. 断言含「取 消」「确 认」","新增基础包弹窗字段完整(名称/所属分类/包编码/备注)。","" +"SURG-BASE-015","基础设置 · 手术套包设置","基础包维护 · 新增基础包","新增取消不落库","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe)","1. 记录当前分页总数 `${sampleTotal}` +2. 打开新增,填写名称 `${uniqPkgName}` 后点「取 消」 +3. 断言列表总数不变,且无该名称行","取消后不新增基础包。","" +"SURG-BASE-016","基础设置 · 手术套包设置","基础包维护 · 新增基础包","合法必填新增成功","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);允许写基础包;名称 `${uniqPkgName}`(建议 AUTO-BASE-${timestamp})、编码 `${uniqPkgCode}`","1. 打开新增,填写名称、所属分类、包编码 +2. 点击「确 认」 +3. 断言弹窗关闭,列表出现该名称行且字段一致 +4. 后置:按环境约定清理该基础包,避免污染主数据","新增成功且列表可见;用例结束清理,不留生产脏数据。","" +"SURG-BASE-017","基础设置 · 手术套包设置","基础包维护 · 新增基础包","基础包名称为空拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe)","1. 打开新增基础包弹窗,名称留空,补齐所属分类与包编码 +2. 点击「确 认」 +3. 断言必填提示,弹窗仍在,列表无新行","确定被拦截;不落库。","" +"SURG-BASE-018","基础设置 · 手术套包设置","基础包维护 · 新增基础包","所属分类为空拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe)","1. 打开新增基础包弹窗,填写名称与包编码,不选所属分类 +2. 点击「确 认」 +3. 断言必填提示,弹窗仍在","确定被拦截;不落库。","" +"SURG-BASE-019","基础设置 · 手术套包设置","基础包维护 · 新增基础包","重复点击确定防重","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);允许写基础包;使用唯一名称 `${uniqPkgName}`","1. 打开新增弹窗并填写合法数据 +2. 快速连点「确 认」两次 +3. 断言只产生一条基础包(或无重复名称/编码行)","连点只落一条;无重复脏数据。","" +"SURG-BASE-020","基础设置 · 手术套包设置","基础包维护 · 行内操作(编辑 / 移除)","行内编辑回填","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);默认列表至少 1 条数据","1. 点击首行「编 辑」 +2. 断言编辑弹窗可见且回填当前行数据 +3. 点击「取 消」关闭","编辑弹窗回填正确;取消不影响数据。","" +"SURG-BASE-021","基础设置 · 手术套包设置","基础包维护 · 行内操作(编辑 / 移除)","移除二次确认防误删","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);默认列表至少 1 条数据","1. 记录当前总数 `${sampleTotal}` +2. 点击首行「移 除」 +3. 若出现确认框则点「取 消」 +4. 断言总数仍为 `${sampleTotal}`","移除有二次确认;取消不删除。","" +"SURG-BASE-022","基础设置 · 手术套包设置","基础包维护 · 行内操作(编辑 / 移除)","新增基础包可在标准套包被引用","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入基础包维护页(/spd/basic/operationSetting/baseMaintenance,无 iframe);已新增 `${uniqPkgName}` 且未清理;允许跨页只读访问","1. 进入「标准套包维护」,点击「添加基础包」 +2. 在弹窗搜索框输入 `${uniqPkgName}` +3. 断言可检索到该基础包并可勾选 +4. 后置:清理该基础包","新增基础包在标准套包可选;跨页数据一致。","" +"SURG-BASE-023","基础设置 · 手术套包设置","基础包维护 · 右侧筛选与列表","多条件组合查询(物料名称等5项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 手术套包设置」权限;已进入手术套包设置对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「物料名称」填写关键字 +3. 「规格型号」填写关键字 +4. 「器械注册人」填写关键字 +5. 「耗材类型」下拉选择任一可选项 +6. 「是否收费」下拉选择任一可选项 +7. 点击「查询」 +8. 检查列表返回结果","多条件同时生效:物料名称、规格型号、器械注册人、耗材类型、是否收费 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..f4dd640 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,72 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"SURG-TYPE-001","基础设置 · 手术套包设置","术种维护 · 页面进入","菜单进入术种维护页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 按菜单「基础设置 → 手术套包设置 → 术种维护」进入 +2. 断言 URL 含 `/spd/basic/operationSetting/typeMaintenance` +3. 断言左侧分类树(术种分类名称搜索框)、右侧筛选区、状态 Tab、列表与分页可见","进入术种维护页;不误入基础包维护/标准套包维护;左右分栏渲染完整。","" +"SURG-TYPE-002","基础设置 · 手术套包设置","术种维护 · 页面进入","同组页签切换后返回","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 点击页内 Tab「标准套包维护」,等待 URL 切换 +2. 返回点击 Tab「术种维护」 +3. 断言 URL 回到 typeMaintenance,左树与状态 Tab 重新渲染","三页签互切正常;返回后本页数据恢复。","" +"SURG-TYPE-003","基础设置 · 手术套包设置","术种维护 · 页面进入","未登录访问拦截","高","测试环境可访问;使用 test.use({ storageState: { cookies: [], origins: [] } }) 无登录态;直接访问页面进入 URL(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 不加载登录态(空 storageState) +2. 直接访问 `/spd/basic/operationSetting/typeMaintenance` +3. 断言跳转登录页或不展示术种列表数据","未登录被拦截,不泄露术种数据。","" +"SURG-TYPE-004","基础设置 · 手术套包设置","术种维护 · 左侧术种分类树","术种分类名称搜索过滤","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 从左树读取 `${sampleOperCategoryName}` +2. 在分类名称搜索框填入该关键字(即时过滤或回车,以现网为准) +3. 断言树节点文本均包含该关键字","过滤后树节点名称与关键字匹配;无未捕获异常。","" +"SURG-TYPE-005","基础设置 · 手术套包设置","术种维护 · 左侧术种分类树","选择分类联动右侧列表","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);默认列表至少 1 条数据","1. 点击 `${sampleOperCategoryName}` 分类节点 +2. 等待右侧列表刷新稳定 +3. 断言列表加载无报错,分页「共 N 条」与当前分类规模一致","右侧仅展示该分类下术种;总数与筛选一致。","" +"SURG-TYPE-006","基础设置 · 手术套包设置","术种维护 · 左侧术种分类树","分类树计数与下钻","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);至少 1 个带计数的分类节点","1. 读取某分类节点括号内计数 `${nodeCount}` +2. 点击该节点 +3. 断言右侧「共 N 条」与 `${nodeCount}` 一致","分类计数与该分类下术种条数一致。","" +"SURG-TYPE-007","基础设置 · 手术套包设置","术种维护 · 左侧术种分类树","打开分类维护弹窗","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 点击左侧「新增」 +2. 断言弹窗可见,含分类名称输入(#basic_kindName)与「取 消」「保 存」 +3. 点击「取 消」关闭","分类维护弹窗字段完整;取消可关闭。","" +"SURG-TYPE-008","基础设置 · 手术套包设置","术种维护 · 左侧术种分类树","分类搜索清空恢复全量","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 用 `${sampleOperCategoryName}` 过滤并记录过滤后节点数 +2. 清空分类搜索框 +3. 断言节点数回到未过滤的默认集合","清空后分类树恢复默认集合。","" +"SURG-TYPE-009","基础设置 · 手术套包设置","术种维护 · 筛选与状态 Tab","术种名称筛选过滤","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);默认列表至少 1 条数据","1. 从首行读取 `${sampleOperName}` +2. 在 `#table_search_form_operationName` 填入该关键字并点击「查 询」 +3. 断言结果行术种名称均包含该关键字","过滤结果与关键字匹配;「共 N 条」随之更新。","" +"SURG-TYPE-010","基础设置 · 手术套包设置","术种维护 · 筛选与状态 Tab","病种名称筛选过滤","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);默认列表至少 1 条数据","1. 从首行读取 `${sampleDiseaseName}` +2. 在 `#table_search_form_disease` 填入该关键字并点击「查 询」 +3. 断言结果行病种名称均包含该关键字","按病种名称过滤生效。","" +"SURG-TYPE-011","基础设置 · 手术套包设置","术种维护 · 筛选与状态 Tab","状态 Tab 切换与计数一致","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 读取「全部」Tab 括号计数 `${totalCount}` +2. 依次点击「停用」「未建分类」「全部」 +3. 每次断言列表条数与「共 N 条」和该 Tab 括号计数一致","Tab 切换后列表仅展示对应状态;括号计数与结果规模一致。","" +"SURG-TYPE-012","基础设置 · 手术套包设置","术种维护 · 筛选与状态 Tab","左树 + 筛选 + Tab 组合过滤","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);默认列表至少 1 条数据","1. 选择 `${sampleOperCategoryName}` 分类节点 +2. 填入术种名称关键字并点「查 询」 +3. 切到「全部」Tab +4. 断言结果为三者交集且无未捕获异常","三条件叠加后结果正确,总数一致。","" +"SURG-TYPE-013","基础设置 · 手术套包设置","术种维护 · 筛选与状态 Tab","重置清空筛选","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);默认列表至少 1 条数据","1. 用关键字过滤后点击「重 置」 +2. 断言筛选输入框被清空、列表恢复默认集合","重置清空筛选并恢复默认列表。","" +"SURG-TYPE-014","基础设置 · 手术套包设置","术种维护 · 筛选与状态 Tab","无匹配组合空态(mock反例)","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 在 `#table_search_form_operationName` 输入 mock 不存在关键字 `__NOT_EXIST_OPER__99999` +2. 点击「查 询」 +3. 断言「暂无数据」且分页总数为 0","无匹配时空态明确,不白屏;总数为 0。","" +"SURG-TYPE-015","基础设置 · 手术套包设置","术种维护 · 列表与编码展示","列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);默认列表至少 1 条数据","1. 断言表头包含序号、术种名称、病种名称、诊断编码(ICD-10)、诊断名称(ICD-10)、操作编码(ICD-9-CM-3)、操作名称(ICD-9-CM-3)、操作","核心列均可见,操作列含「编 辑」「停用/启用」。","" +"SURG-TYPE-016","基础设置 · 手术套包设置","术种维护 · 列表与编码展示","ICD-10 诊断编码展示完整","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);默认列表至少 1 条数据;至少 1 条含诊断编码的数据","1. 读取含 ICD-10 编码的行 `${sampleIcd10Code}` +2. 断言编码文本完整(如 A02.000x010)不截断、无乱码","ICD-10 编码完整展示。","" +"SURG-TYPE-017","基础设置 · 手术套包设置","术种维护 · 列表与编码展示","ICD-9-CM-3 操作编码展示完整","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);默认列表至少 1 条数据;至少 1 条含操作编码的数据","1. 读取含操作编码的行 `${sampleIcd9Code}` +2. 断言操作编码(如 00.0200)与操作名称(如 心脏治疗性超声)对应展示无乱码","ICD-9-CM-3 编码与操作名称对应正确。","" +"SURG-TYPE-018","基础设置 · 手术套包设置","术种维护 · 列表与编码展示","分页与页大小切换","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);默认列表至少 1 条数据;列表总数大于单页行数上限","1. 切换每页条数(如 50 → 100) +2. 断言单页行数不超过所选值,分页总数不变","页大小切换生效;总数保持稳定。","" +"SURG-TYPE-019","基础设置 · 手术套包设置","术种维护 · 列表与编码展示","首页上一页不可用","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 处于第 1 页 +2. 断言「上一页」置灰或点击无效果","第 1 页无法回到上一页。","" +"SURG-TYPE-020","基础设置 · 手术套包设置","术种维护 · 新增术种","打开新增术种入口","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 点击「新增术种」 +2. 断言新增弹窗/页面可见,含术种名称、病种名称、分类与编码相关字段 +3. 断言含「取 消」「保 存」","新增术种入口字段完整。","" +"SURG-TYPE-021","基础设置 · 手术套包设置","术种维护 · 新增术种","新增取消不落库","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 记录当前分页总数 `${sampleTotal}` +2. 打开新增,填写名称 `${uniqOperName}` 后点「取 消」 +3. 断言列表总数不变,且无该名称行","取消后不新增术种。","" +"SURG-TYPE-022","基础设置 · 手术套包设置","术种维护 · 新增术种","术种名称为空拦截(mock反例)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe)","1. 打开新增术种弹窗,名称留空 +2. 点击「保 存」 +3. 断言必填提示,弹窗仍在,列表无新行","保存被拦截;不落库。","" +"SURG-TYPE-023","基础设置 · 手术套包设置","术种维护 · 行内操作(编辑 / 停用)","行内编辑回填","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);默认列表至少 1 条数据","1. 点击首行「编 辑」 +2. 断言编辑弹窗可见且回填当前术种数据 +3. 点击「取 消」关闭","编辑弹窗回填正确;取消不影响数据。","" +"SURG-TYPE-024","基础设置 · 手术套包设置","术种维护 · 行内操作(编辑 / 停用)","停用状态流转","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);存在启用状态术种(优先选 `${uniqOperName}` 自建数据);允许写术种","1. 记录「停用」Tab 当前计数 `${disabledCount}` +2. 点击目标行「停用」,若有确认框则点「确 定」 +3. 断言「停用」Tab 计数 +1 +4. 后置:对该术种执行「启用」恢复","术种状态变为停用;「停用」计数 +1;结束恢复原状态。","" +"SURG-TYPE-025","基础设置 · 手术套包设置","术种维护 · 行内操作(编辑 / 停用)","停用二次确认防误操作","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入术种维护页(/spd/basic/operationSetting/typeMaintenance,无 iframe);存在启用状态术种","1. 记录「停用」Tab 当前计数 +2. 点击首行「停用」 +3. 若出现确认框则点「取 消」 +4. 断言计数未变化","停用有二次确认;取消不改状态。","" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..78379c5 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,85 @@ +"用例编号","功能模块","功能测试点","用例标题","优先级","前置条件","测试步骤","预期结果","实际结果" +"SURG-STD-001","基础设置 · 手术套包设置","标准套包维护 · 页面进入","菜单进入标准套包维护页","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 按菜单「基础设置 → 手术套包设置 → 标准套包维护」进入 +2. 断言 URL 含 `/spd/basic/operationSetting/standardMaintenance` +3. 断言左侧分类树、右侧筛选区、构成列表与分页可见","进入标准套包维护页;不误入基础包维护/术种维护;左右分栏渲染完整。","" +"SURG-STD-002","基础设置 · 手术套包设置","标准套包维护 · 页面进入","同组页签切换后返回","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 点击页内 Tab「术种维护」,等待 URL 切换 +2. 返回点击 Tab「标准套包维护」 +3. 断言 URL 回到 standardMaintenance,左树与构成列表重新渲染","三页签互切正常;返回后本页数据恢复。","" +"SURG-STD-003","基础设置 · 手术套包设置","标准套包维护 · 页面进入","未登录访问拦截","高","测试环境可访问;使用 test.use({ storageState: { cookies: [], origins: [] } }) 无登录态;直接访问页面进入 URL(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 不加载登录态(空 storageState) +2. 直接访问 `/spd/basic/operationSetting/standardMaintenance` +3. 断言跳转登录页或不展示套包列表数据","未登录被拦截,不泄露套包数据。","" +"SURG-STD-004","基础设置 · 手术套包设置","标准套包维护 · 左侧术种分类树","术种分类名称搜索过滤","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 从左树读取 `${sampleOperCategoryName}` +2. 在分类搜索框填入该关键字(即时过滤或回车,以现网为准) +3. 断言树节点文本均包含该关键字","过滤后树节点名称与关键字匹配;无未捕获异常。","" +"SURG-STD-005","基础设置 · 手术套包设置","标准套包维护 · 左侧术种分类树","选择分类联动构成列表","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);默认列表至少 1 条数据","1. 点击 `${sampleOperCategoryName}` 分类节点 +2. 等待右侧构成列表刷新稳定 +3. 断言列表加载无报错,分页「共 N 条」与当前分类规模一致","右侧仅展示该分类下标准套包;总数与筛选一致。","" +"SURG-STD-006","基础设置 · 手术套包设置","标准套包维护 · 左侧术种分类树","分类搜索清空恢复全量","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 用 `${sampleOperCategoryName}` 过滤并记录过滤后节点数 +2. 清空分类搜索框 +3. 断言节点数回到未过滤的默认集合","清空后分类树恢复默认集合。","" +"SURG-STD-007","基础设置 · 手术套包设置","标准套包维护 · 筛选与构成列表","默认展示标准套包构成","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 等待列表稳定 +2. 断言按「包名称」分组出现数据行,或「暂无数据」且分页总数为 0","加载无报错;有数据时「共 N 条」N≥1;无数据时空态明确。","" +"SURG-STD-008","基础设置 · 手术套包设置","标准套包维护 · 筛选与构成列表","产品名称筛选过滤","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);默认列表至少 1 条数据","1. 从首行读取 `${sampleProductName}` +2. 在 `#table_search_form_keyword` 填入该关键字并点击「查 询」 +3. 断言结果行产品名称均包含该关键字","过滤结果与关键字匹配;「共 N 条」随之更新。","" +"SURG-STD-009","基础设置 · 手术套包设置","标准套包维护 · 筛选与构成列表","规格型号筛选过滤","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);默认列表至少 1 条数据","1. 从首行读取 `${sampleSpecModel}` +2. 在 `#table_search_form_goodsSpec` 填入该关键字并点击「查 询」 +3. 断言结果行规格型号均包含该关键字","按规格型号过滤生效。","" +"SURG-STD-010","基础设置 · 手术套包设置","标准套包维护 · 筛选与构成列表","器械注册人筛选过滤","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);默认列表至少 1 条数据","1. 从首行读取 `${sampleMfrsName}` +2. 在 `#table_search_form_goodsMfrsName` 填入该关键字并点击「查 询」 +3. 断言结果行器械注册人均包含该关键字","按器械注册人过滤生效。","" +"SURG-STD-011","基础设置 · 手术套包设置","标准套包维护 · 筛选与构成列表","构成列表核心列完整","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);默认列表至少 1 条数据;存在分组","1. 断言表头/首行包含包名称、包数量/每包含量、产品编号、产品名称、规格型号、器械注册人、单位、产地、耗材类型、操作","核心列均可见,操作列含「编 辑」「移 除」。","" +"SURG-STD-012","基础设置 · 手术套包设置","标准套包维护 · 筛选与构成列表","包名称展开/收起子项","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);默认列表至少 1 条数据;存在可展开的包分组","1. 读取首个包分组名称 `${samplePkgGroupName}` +2. 点击展开该分组,断言出现子项行 +3. 再次点击收起,断言子项行隐藏","展开显示子项、收起隐藏子项;分组计数不受影响。","" +"SURG-STD-013","基础设置 · 手术套包设置","标准套包维护 · 筛选与构成列表","查询与重置","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);默认列表至少 1 条数据","1. 用关键字过滤后点击「重 置」 +2. 断言筛选输入框被清空、列表恢复默认集合","重置清空筛选并恢复默认列表。","" +"SURG-STD-014","基础设置 · 手术套包设置","标准套包维护 · 筛选与构成列表","分页与页大小切换","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);默认列表至少 1 条数据;列表总数大于单页行数上限","1. 切换每页条数(如 50 → 100) +2. 断言单页行数不超过所选值,分页总数不变","页大小切换生效;总数保持稳定。","" +"SURG-STD-015","基础设置 · 手术套包设置","标准套包维护 · 筛选与构成列表","无匹配组合空态(mock反例)","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 在 `#table_search_form_keyword` 输入 mock 不存在关键字 `__NOT_EXIST_STD__99999` +2. 点击「查 询」 +3. 断言「暂无数据」且分页总数为 0","无匹配时空态明确,不白屏;总数为 0。","" +"SURG-STD-016","基础设置 · 手术套包设置","标准套包维护 · 添加基础包弹窗","打开添加基础包弹窗","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 点击「添加基础包」 +2. 断言弹窗可见,含左侧基础包分类树与右侧基础包明细 +3. 断言含「取 消」「确定(N)」","添加基础包弹窗字段完整;确定按钮带选择计数。","" +"SURG-STD-017","基础设置 · 手术套包设置","标准套包维护 · 添加基础包弹窗","弹窗内筛选与选择计数联动","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 打开添加基础包弹窗,在搜索框输入基础包关键字 +2. 勾选 1 个基础包 +3. 断言「确定(N)」计数由 0 变为 1","筛选生效;选择数与确定按钮计数联动。","" +"SURG-STD-018","基础设置 · 手术套包设置","标准套包维护 · 添加基础包弹窗","弹窗内构成数量可编辑","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);已勾选至少 1 个基础包","1. 定位明细行「构成数量」步进器,点击「Increase Value」 +2. 断言数量 +1 且合计随之更新","构成数量可编辑;加减生效。","" +"SURG-STD-019","基础设置 · 手术套包设置","标准套包维护 · 添加基础包弹窗","添加基础包取消不落库","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 记录当前分页总数 `${sampleTotal}` +2. 打开添加基础包,勾选 1 个基础包后点「取 消」 +3. 断言构成列表总数不变","取消后不新增构成行。","" +"SURG-STD-020","基础设置 · 手术套包设置","标准套包维护 · 添加基础包弹窗","确定按钮零选禁用","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);已打开添加基础包弹窗且未选择","1. 断言「确定(0)」置灰或点击无效果","未选择时确定不可用。","" +"SURG-STD-021","基础设置 · 手术套包设置","标准套包维护 · 添加基础包弹窗","重复添加同一基础包","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);标准套包已含某基础包(或先添加一个);允许写套包","1. 再次打开添加基础包,选择同一基础包并确定 +2. 断言按产品规则拒绝/合并/累加,不产生重复脏行","重复添加不产生重复行(策略以现网为准)。","" +"SURG-STD-022","基础设置 · 手术套包设置","标准套包维护 · 添加散件弹窗","打开添加散件弹窗","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 点击「添加散件」 +2. 断言弹窗可见并展示「产品列表」视图,含产品名称/规格型号/器械注册人/耗材类型筛选与分页","添加散件弹窗产品列表视图完整。","" +"SURG-STD-023","基础设置 · 手术套包设置","标准套包维护 · 添加散件弹窗","产品列表筛选与选择散件","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);已打开添加散件弹窗","1. 在产品名称筛选框输入 `${sampleProductName}` +2. 勾选首个匹配散件 +3. 断言散件进入应拣明细,计数同步变化","筛选生效;选中散件进入应拣明细。","" +"SURG-STD-024","基础设置 · 手术套包设置","标准套包维护 · 添加散件弹窗","散件规格/应拣明细视图","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);已勾选至少 1 个散件","1. 切换到规格明细/应拣明细视图 +2. 断言展示耗材品种数、耗材总数,以及应拣数量、产品编号/名称/规格/注册人/单位/耗材类型/是否收费列","应拣明细视图字段完整。","" +"SURG-STD-025","基础设置 · 手术套包设置","标准套包维护 · 添加散件弹窗","明细统计与实际一致","中","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);应拣明细含至少 1 个散件","1. 读取「耗材品种数」`${itemKindCount}` 与「耗材总数」`${itemTotalCount}` +2. 断言品种数 = 明细去重产品数;总数 = 应拣数量合计","统计口径与实际所选一致。","" +"SURG-STD-026","基础设置 · 手术套包设置","标准套包维护 · 添加散件弹窗","添加散件取消不落库","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe)","1. 记录当前分页总数 `${sampleTotal}` +2. 打开添加散件,勾选散件后点「取 消」 +3. 断言构成列表总数不变","取消后不新增散件行。","" +"SURG-STD-027","基础设置 · 手术套包设置","标准套包维护 · 添加散件弹窗","万级散件列表首屏可交互","低","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);添加散件产品列表总量达万级(示例「共 16299 条」)","1. 打开添加散件弹窗 +2. 断言首屏可交互(筛选/分页可用),不卡死 +3. 避免全量断言,仅校验首行与总数格式","万级列表首屏可用;无超时白屏。","" +"SURG-STD-028","基础设置 · 手术套包设置","标准套包维护 · 行内操作(编辑 / 移除)","行内编辑构成项","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);默认列表至少 1 条数据;先展开目标包","1. 点击子项行「编 辑」 +2. 断言编辑弹窗/表单可见并回填当前值 +3. 点击「取 消」关闭","编辑回填正确;取消不影响数据。","" +"SURG-STD-029","基础设置 · 手术套包设置","标准套包维护 · 行内操作(编辑 / 移除)","移除二次确认防误删整包","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 → 手术套包设置」权限;已进入标准套包维护页(/spd/basic/operationSetting/standardMaintenance,无 iframe);默认列表至少 1 条数据;先展开目标包","1. 记录当前行数 +2. 点击子项行「移 除」 +3. 若出现确认框则点「取 消」 +4. 断言行数未变化","移除有二次确认;取消不删除。","" +"SURG-STD-030","基础设置 · 手术套包设置","标准套包维护 · 筛选与构成列表","多条件组合查询(物料名称等5项)","高","测试环境可访问;已通过 auth.setup 写入 storageState 登录;具备「基础设置 · 手术套包设置」权限;已进入手术套包设置对应功能页;筛选区已展开","1. 进入页面并等待列表加载 +2. 「物料名称」填写关键字 +3. 「规格型号」填写关键字 +4. 「器械注册人」填写关键字 +5. 「耗材类型」下拉选择任一可选项 +6. 「是否收费」下拉选择任一可选项 +7. 点击「查询」 +8. 检查列表返回结果","多条件同时生效:物料名称、规格型号、器械注册人、耗材类型、是否收费 均成功录入/选中;点击查询后请求携带全部条件(页面无报错提示),列表按条件刷新并呈现匹配数据或明确空态,条件之间为「且」关系","待执行" diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..0035c00 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,62 @@ +用例编号,功能模块,功能测试点,用例标题,优先级,前置条件,测试步骤,预期结果,实际结果 +FLOW-INS-001,基础设置 · 流程设置,审批流实例 · 页面进入,菜单进入审批流实例页,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 按菜单「基础设置 → 流程设置 → 审批流实例」进入,或 `openPath` 打开 `/spd/basic/flowSet/workflow/flowCase` +3. 断言 URL 含 `/spd/basic/flowSet/workflow/flowCase` +4. 断言单据编号、审批状态、发起时间、业务类型、摘要、查询、重置、列表与分页可见",进入审批流实例;不误入审批流程设置。, +FLOW-INS-002,基础设置 · 流程设置,审批流实例 · 页面进入,未登录访问拦截,高,测试环境可访问;使用独立 context,storageState 为空(不加载 test_1 登录态),"1. 新建独立 BrowserContext,不加载 auth/user.json +2. 清除登录态后直接打开 `/spd/basic/flowSet/workflow/flowCase` +3. 断言跳转登录或不展示审批单据列表",未登录不可见审批实例。, +FLOW-INS-003,基础设置 · 流程设置,审批流实例 · 筛选与查询,默认日期区间查询,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击「查 询」并等待列表稳定 +3. 断言出现数据行或空态明确,「共 N 条」与结果逻辑一致",默认查询无报错;有数据时 N≥1。, +FLOW-INS-004,基础设置 · 流程设置,审批流实例 · 筛选与查询,单据编号精确查询,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe);默认查询至少 1 条且单据编号非空,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 从首行读取 `${sampleOrderNo}` +3. 单据编号填入完整单号,点击「查 询」 +4. 断言至多 1 条,且编号、业务类型与该行一致",精确单号命中当前行。, +FLOW-INS-005,基础设置 · 流程设置,审批流实例 · 筛选与查询,按审批状态筛选,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 审批状态下拉选择「审批通过」(无该选项则选列表中已出现的状态) +3. 点击「查 询」 +4. 断言结果「审批状态」列与所选一致",状态筛选生效。, +FLOW-INS-006,基础设置 · 流程设置,审批流实例 · 筛选与查询,按业务类型筛选,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe);默认查询至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 从首行读取 `${sampleBizType}` +3. 业务类型选择该值,点击「查 询」 +4. 断言结果「业务类型」均为该类型",业务类型筛选生效。, +FLOW-INS-007,基础设置 · 流程设置,审批流实例 · 筛选与查询,按发起时间区间筛选,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录当前默认起止日期 +3. 点击「查 询」 +4. 抽样结果行「发起时间」落在起止日内(含边界)",发起时间落在区间内。, +FLOW-INS-008,基础设置 · 流程设置,审批流实例 · 筛选与查询,按摘要关键字筛选,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 找到摘要非 `--` 的行读取 `${sampleSummary}` 关键字;若全为 `--` 则填 mock 关键字 +3. 点击「查 询」 +4. 有关键字命中时摘要包含该关键字;mock 无命中时空态且 `--` 行不被命中",非空摘要可检索;`--` 不被非空关键字命中。, +FLOW-INS-009,基础设置 · 流程设置,审批流实例 · 筛选与查询,状态+类型组合查询,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe);默认查询至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 填写 `${sampleStatus}` 与 `${sampleBizType}` 后点击「查 询」 +3. 断言抽样行同时满足全部已填条件",组合条件同时生效。, +FLOW-INS-010,基础设置 · 流程设置,审批流实例 · 筛选与查询,重置恢复默认,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 填写单据编号等条件后点击「重 置」 +3. 断言日期回到默认区间,其余条件清空,列表刷新",重置后日期与其它条件恢复默认。, +FLOW-INS-011,基础设置 · 流程设置,审批流实例 · 筛选与查询,无匹配空态(mock反例),中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 单据编号填入 `__NOT_EXIST_FLOW__99999`,点击「查 询」 +3. 断言空态且分页总数为 0",无匹配不报错;总数为 0。, +FLOW-INS-012,基础设置 · 流程设置,审批流实例 · 列表与分页,列表核心列完整,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 断言表头含序号、单据编号、业务类型、发起人、发起部门、发起时间、摘要、审批状态、操作 +3. 断言行内可见「详情」",核心列完整;详情入口可见。, +FLOW-INS-013,基础设置 · 流程设置,审批流实例 · 列表与分页,单据编号前缀与业务类型对应,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe);默认查询有数据,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 抽样 `PLAN`/`DB`/`GC`/`BUY` 前缀行(有则断言) +3. 断言 PLAN→采购计划、DB→二级库调拨、GC→产品信息变更、BUY→请领单(以现网枚举为准,缺前缀 skip 该项)",前缀与类型对应。, +FLOW-INS-014,基础设置 · 流程设置,审批流实例 · 列表与分页,分页翻至第 2 页,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe);总条数大于一页,否则 skip,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录第 1 页首行单据编号 +3. 翻到第 2 页 +4. 断言数据更换;筛选条件保持",翻页成功;筛选不丢失。, +FLOW-INS-015,基础设置 · 流程设置,审批流实例 · 审批详情,打开详情对应当前行,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录首行 `${sampleOrderNo}` +3. 点击「详情」 +4. 断言打开该单审批过程,标题或单号含 `${sampleOrderNo}`",详情单号与当前行一致。, +FLOW-INS-016,基础设置 · 流程设置,审批流实例 · 审批详情,关闭详情保留列表状态,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 填写单据编号查询后打开详情 +3. 点返回/关闭 +4. 断言筛选条件与列表仍是打开前的查询结果",返回后条件与页码保持。, +FLOW-INS-017,基础设置 · 流程设置,审批流实例 · 审批详情,详情只读,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流实例」权限;已进入审批流实例页(/spd/basic/flowSet/workflow/flowCase,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开首行详情 +3. 断言无保存审批人/改条件的提交按钮,或此类控件不可用 +4. 返回列表",详情只读;改配置须到审批流程设置。, diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..1dbc132 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,51 @@ +用例编号,功能模块,功能测试点,用例标题,优先级,前置条件,测试步骤,预期结果,实际结果 +FLOW-DEF-001,基础设置 · 流程设置,审批流程设置 · 页面进入,菜单进入审批流程设置页,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 按菜单「基础设置 → 流程设置 → 审批流程设置」进入,或 `openPath` 打开 `/spd/basic/flowSet/workflow/flowSet` +3. 断言 URL 含 `/spd/basic/flowSet/workflow/flowSet` +4. 断言左侧业务类型、右侧默认审批流与自定义审批流、「保 存」「重 置」可见",进入审批流程设置;不误入审批流实例。, +FLOW-DEF-002,基础设置 · 流程设置,审批流程设置 · 页面进入,未登录访问拦截,高,测试环境可访问;使用独立 context,storageState 为空(不加载 test_1 登录态),"1. 新建独立 BrowserContext,不加载 auth/user.json +2. 清除登录态后直接打开 `/spd/basic/flowSet/workflow/flowSet` +3. 断言跳转登录或不展示审批流配置",未登录不可见审批流配置。, +FLOW-DEF-003,基础设置 · 流程设置,审批流程设置 · 左侧业务类型,业务类型列表可见,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限;已进入审批流程设置页(/spd/basic/flowSet/workflow/flowSet,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 断言左侧可见采购计划等类型且有当前高亮项 +3. 读取 `${sampleBizType}`",类型列表可见;当前项高亮。, +FLOW-DEF-004,基础设置 · 流程设置,审批流程设置 · 左侧业务类型,按流程名称过滤类型,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限;已进入审批流程设置页(/spd/basic/flowSet/workflow/flowSet,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 在「流程名称」输入 `${sampleBizType}` 关键字 +3. 断言可见类型名称均匹配关键字",类型过滤与关键字匹配。, +FLOW-DEF-005,基础设置 · 流程设置,审批流程设置 · 左侧业务类型,清空关键字恢复全量,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限;已进入审批流程设置页(/spd/basic/flowSet/workflow/flowSet,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录过滤前类型数量 +3. 输入关键字后再清空 +4. 断言数量恢复",清空后类型列表恢复全量。, +FLOW-DEF-006,基础设置 · 流程设置,审批流程设置 · 左侧业务类型,切换类型刷新右侧配置,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限;已进入审批流程设置页(/spd/basic/flowSet/workflow/flowSet,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录当前类型默认流节点文案 +3. 点选另一业务类型(仅 1 个则 skip) +4. 断言右侧默认流/自定义流已刷新,不残留上一类型节点人员",切换类型后右侧与该类型已保存配置一致。, +FLOW-DEF-007,基础设置 · 流程设置,审批流程设置 · 左侧业务类型,无匹配类型(mock反例),中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限;已进入审批流程设置页(/spd/basic/flowSet/workflow/flowSet,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 流程名称填入 `__NOT_EXIST_FLOW__99999` +3. 断言左侧无匹配或空,右侧不把上一类型配置展示为当前成功配置",无匹配不报错。, +FLOW-DEF-008,基础设置 · 流程设置,审批流程设置 · 默认审批流,默认审批流节点可见,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限;已进入审批流程设置页(/spd/basic/flowSet/workflow/flowSet,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 选中「采购计划」(或当前默认类型) +3. 断言「默认审批流」区域可见节点卡片,含节点/账号/人员三行信息(按卡片容器取文本)",默认流节点结构可见;不点保存。, +FLOW-DEF-009,基础设置 · 流程设置,审批流程设置 · 自定义审批流,新增流程增加一行后重置,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限;已进入审批流程设置页(/spd/basic/flowSet/workflow/flowSet,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录当前自定义流行数 +3. 点击「新增流程」 +4. 断言行数 +1,新行含流程名称、条件、审批流程、删除 +5. 点击「重 置」,断言行数回到操作前,不点「保 存」",可新增行;重置放弃未保存行。, +FLOW-DEF-010,基础设置 · 流程设置,审批流程设置 · 自定义审批流,空行保存被拦截后重置,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限;已进入审批流程设置页(/spd/basic/flowSet/workflow/flowSet,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击「新增流程」得到空行 +3. 点击「保 存」 +4. 断言出现必填拦截提示,配置未按成功态关闭 +5. 点击「重 置」,放弃该空行",空名称/条件/节点不能落库;拦截后重置恢复。, +FLOW-DEF-011,基础设置 · 流程设置,审批流程设置 · 条件配置,打开条件配置后取消,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限;已进入审批流程设置页(/spd/basic/flowSet/workflow/flowSet,无 iframe);当前类型至少 1 条自定义流,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 若无自定义行则 skip +3. 点击首条自定义流条件列「编辑」 +4. 断言打开「条件配置」 +5. 点「取 消」,断言行内条件文案与打开前一致",条件配置可打开;取消不改行内条件。, +FLOW-DEF-012,基础设置 · 流程设置,审批流程设置 · 保存与重置,重置放弃未保存改动,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限;已进入审批流程设置页(/spd/basic/flowSet/workflow/flowSet,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击「新增流程」增加一行 +3. 点击「重 置」 +4. 断言自定义流行回到上次保存结果",重置不提交;未保存行消失。, +FLOW-DEF-013,基础设置 · 流程设置,审批流程设置 · 左侧业务类型,类型与审批流实例业务类型同源,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 流程设置 → 审批流程设置」权限;已进入审批流程设置页(/spd/basic/flowSet/workflow/flowSet,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录本页左侧类型名称集合(可见范围) +3. 打开审批流实例,展开「业务类型」下拉 +4. 断言选项与本页类型同源(允许按权限裁剪,本页有的关键类型如采购计划应能对应)",两页业务类型同源。, diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..4b4b309 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,57 @@ +用例编号,功能模块,功能测试点,用例标题,优先级,前置条件,测试步骤,预期结果,实际结果 +USER-UNI-001,基础设置 · 用户管理,医共体用户管理 · 页面进入,菜单进入医共体用户管理页,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 按菜单「基础设置 → 用户管理 → 医共体用户管理」进入,或 `openPath` 打开 `/spd/basic/user/platform/personCom` +3. 断言 URL 含 `/spd/basic/user/platform/personCom` +4. 断言所属医院筛选、工具栏、列表列头与分页可见",进入医共体用户管理页;不误入用户管理或角色管理。, +USER-UNI-002,基础设置 · 用户管理,医共体用户管理 · 页面进入,未登录访问拦截,高,测试环境可访问;使用独立 context,storageState 为空(不加载 test_1 登录态),"1. 新建独立 BrowserContext,不加载 auth/user.json +2. 清除登录态后直接打开 `/spd/basic/user/platform/personCom` +3. 断言跳转登录或不展示用户业务列表",未登录不可见医共体用户数据。, +USER-UNI-003,基础设置 · 用户管理,医共体用户管理 · 筛选与查询,默认查询列表有数据或空态明确,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击「查 询」并等待列表稳定 +3. 断言出现数据行,或空态且分页总数为 0",查询无报错;有数据时「共 N 条」N≥1。, +USER-UNI-004,基础设置 · 用户管理,医共体用户管理 · 筛选与查询,姓名或登录账号关键字查询,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe);默认查询至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 从首行读取 `${sampleUserName}` 或 `${sampleLoginName}` +3. 在「姓名」框填入关键字,点击「查 询」 +4. 断言结果行姓名或登录账号匹配",查询结果与关键字匹配。, +USER-UNI-005,基础设置 · 用户管理,医共体用户管理 · 筛选与查询,所属医院筛选,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录当前默认 `${sampleHospital}` 与分页总数 +3. 切换「所属医院」为另一可选项(仅 1 家则 skip) +4. 点击「查 询」 +5. 断言结果行「所属医院」均为所选值,「共 N 条」可与默认不同",列表仅该院用户;总数随医院变化。, +USER-UNI-006,基础设置 · 用户管理,医共体用户管理 · 筛选与查询,姓名+医院+联系方式组合查询,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe);默认查询至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 读取 `${sampleUserName}`、`${sampleHospital}`、`${samplePhone}`(电话非法占位如 `1` 时改用姓名+医院) +3. 填写后点击「查 询」 +4. 断言结果同时满足已填条件",组合条件同时生效。, +USER-UNI-007,基础设置 · 用户管理,医共体用户管理 · 筛选与查询,重置恢复默认医院,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 更改所属医院或姓名后点击「重 置」 +3. 断言所属医院恢复进入页时的默认值,列表刷新",医院等条件恢复默认。, +USER-UNI-008,基础设置 · 用户管理,医共体用户管理 · 筛选与查询,展开高级筛选,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击「高级筛选」 +3. 断言可见所属医院、联系方式、科室、角色及「筛 选」",高级筛选含医院维度字段。, +USER-UNI-009,基础设置 · 用户管理,医共体用户管理 · 筛选与查询,无匹配空态(mock反例),中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 姓名框填入 `__NOT_EXIST_USER__99999`,点击「查 询」 +3. 断言空态且分页总数为 0",无匹配不报错;总数为 0。, +USER-UNI-010,基础设置 · 用户管理,医共体用户管理 · 列表与分页,列表列顺序含所属医院,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 断言列顺序为登录账号、姓名、联系方式、所属医院、所属科室、医院员工编号、角色、科室任职数、操作 +3. 勿用用户管理页列断言(用户管理无所属医院且姓名在登录账号前)",列顺序与用户管理页不同;含所属医院。, +USER-UNI-011,基础设置 · 用户管理,医共体用户管理 · 列表与分页,分页翻至第 2 页,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe);总条数大于一页,否则 skip,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录第 1 页首行登录账号 +3. 翻到第 2 页 +4. 断言数据更换且所属医院筛选仍保持",翻页成功;医院条件不丢失。, +USER-UNI-012,基础设置 · 用户管理,医共体用户管理 · 列表与分页,与用户管理页同一用户字段一致,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe);两页都能查到同一账号,否则 skip,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录本页首行 `${sampleLoginName}`、姓名、所属科室、角色 +3. 打开用户管理页,按登录账号查询同一用户 +4. 断言姓名、角色、所属科室一致;本页仅多「所属医院」",同一登录账号跨页字段一致。, +USER-UNI-013,基础设置 · 用户管理,医共体用户管理 · 新增与编辑,打开新增弹窗,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击「新 增」 +3. 断言弹出用户表单,含账号、姓名、联系方式及取消/确定",新增弹窗可见。, +USER-UNI-014,基础设置 · 用户管理,医共体用户管理 · 新增与编辑,新增取消不落库,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开新增,账号填 `${uniqLogin}` 后点「取 消」 +3. 当前医院下列表查询该账号,断言不存在",取消后当前医院列表不出现该账号。, +USER-UNI-015,基础设置 · 用户管理,医共体用户管理 · 新增与编辑,必填为空拦截,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开新增,账号或姓名留空后点「确 定」 +3. 断言被拦截,弹窗仍打开,不落库",必填为空不可保存。, +USER-UNI-016,基础设置 · 用户管理,医共体用户管理 · 新增与编辑,打开编辑并回填,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 医共体用户管理」权限;已进入医共体用户管理页(/spd/basic/user/platform/personCom,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击首行「编辑」 +3. 断言弹窗回填与该行登录账号、姓名、所属医院一致 +4. 点「取 消」",编辑回填正确;本条不保存。, diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..32213b1 --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,85 @@ +用例编号,功能模块,功能测试点,用例标题,优先级,前置条件,测试步骤,预期结果,实际结果 +USER-USR-001,基础设置 · 用户管理,用户管理 · 页面进入,菜单进入用户管理页,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 按菜单「基础设置 → 用户管理(子菜单)→ 用户管理(叶子)」进入,或 `openPath` 打开 `/spd/basic/user/platform/person` +3. 断言 URL 含 `/spd/basic/user/platform/person` +4. 断言筛选区、工具栏「新 增」「Excel导入」、列表列头与分页可见",进入用户管理页;不误入医共体用户管理或角色管理;无报错遮罩。, +USER-USR-002,基础设置 · 用户管理,用户管理 · 页面进入,未登录访问拦截,高,测试环境可访问;使用独立 context,storageState 为空(不加载 test_1 登录态),"1. 新建独立 BrowserContext,不加载 auth/user.json +2. 清除登录态后直接打开 `/spd/basic/user/platform/person` +3. 断言跳转登录或不展示用户业务列表",未登录不可见用户数据。, +USER-USR-003,基础设置 · 用户管理,用户管理 · 筛选与查询,默认查询列表有数据或空态明确,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击「查 询」并等待列表稳定 +3. 断言出现数据行,或「暂无数据」且分页总数为 0",查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。, +USER-USR-004,基础设置 · 用户管理,用户管理 · 筛选与查询,姓名或登录账号关键字查询,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe);默认查询至少 1 条且姓名或登录账号非空,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 从首行读取 `${sampleUserName}` 或 `${sampleLoginName}` 取关键字 +3. 在「姓名」框(占位「姓名、登录账号」)填入关键字,点击「查 询」 +4. 断言结果行姓名或登录账号匹配该关键字",查询结果与已填关键字匹配;无未捕获异常。, +USER-USR-005,基础设置 · 用户管理,用户管理 · 筛选与查询,科室下拉筛选,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe);列表存在所属科室非空行,否则 skip,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 取首条所属科室非 `--` 的行,读取 `${sampleDept}` +3. 科室下拉选择该值,点击「查 询」 +4. 断言结果行「所属科室」与所选一致,且不含未分配 `--` 行",所属科室列与筛选条件一致。, +USER-USR-006,基础设置 · 用户管理,用户管理 · 筛选与查询,姓名+科室+角色组合查询,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe);默认查询至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 读取 `${sampleUserName}`、`${sampleDept}`、`${sampleRole}`(角色可从该行截断前列名) +3. 填写姓名、科室、角色后点击「查 询」 +4. 断言结果同时满足已填条件",组合查询无报错;结果同时满足全部已填条件。, +USER-USR-007,基础设置 · 用户管理,用户管理 · 筛选与查询,重置恢复默认,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 填写姓名等筛选条件后点击「重 置」 +3. 断言筛选字段恢复默认,列表按默认规则刷新",条件恢复默认;列表刷新。, +USER-USR-008,基础设置 · 用户管理,用户管理 · 筛选与查询,展开高级筛选,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击「高级筛选」 +3. 断言展开后面板可见姓名/科室/角色/状态及「重 置」「筛 选」",高级筛选面板展开;关键字段与筛选按钮可见。, +USER-USR-009,基础设置 · 用户管理,用户管理 · 筛选与查询,高级筛选按状态过滤,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 展开高级筛选,状态下拉选择「启用」或「禁用」 +3. 点击「筛 选」 +4. 断言列表仅保留对应状态用户(无匹配则空态且总数为 0)",按状态过滤成功;无报错。, +USER-USR-010,基础设置 · 用户管理,用户管理 · 筛选与查询,无匹配结果空态(mock反例),中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 姓名框填入 `__NOT_EXIST_USER__99999` +3. 点击「查 询」 +4. 断言空态或「暂无数据」,分页总数为 0",无匹配时不报错;空态明确;总数为 0。, +USER-USR-011,基础设置 · 用户管理,用户管理 · 列表与分页,列表核心列完整,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 断言表头含序号、姓名、登录账号、联系方式、所属科室、医院员工编号、角色、科室任职数、操作 +3. 断言操作列可见「编辑」「授权」「重置密码」",核心列完整;行内三个操入口可见。, +USER-USR-012,基础设置 · 用户管理,用户管理 · 列表与分页,列表不含所属医院列,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 断言表头不含「所属医院」",本页无所属医院列,与医共体用户管理页区分。, +USER-USR-013,基础设置 · 用户管理,用户管理 · 列表与分页,分页翻至第 2 页,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe);默认查询总条数大于一页,否则 skip,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录第 1 页首行登录账号 +3. 点击页码「2」或「下一页」 +4. 断言页码高亮为 2,首行账号与第 1 页不同",翻页成功;数据更换。, +USER-USR-014,基础设置 · 用户管理,用户管理 · 新增用户,打开新增用户弹窗,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击「新 增」 +3. 断言弹出用户表单,可见账号、姓名、联系方式、医院员工编号、启用/禁用、「取 消」「确 定」",新增弹窗可见;关键字段与按钮齐全。, +USER-USR-015,基础设置 · 用户管理,用户管理 · 新增用户,新增取消不落库,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开新增,账号填 `${uniqLogin}`,姓名填任意合法值 +3. 点击「取 消」 +4. 按账号查询,断言无该记录",弹窗关闭;列表不出现 `${uniqLogin}`。, +USER-USR-016,基础设置 · 用户管理,用户管理 · 新增用户,必填为空拦截,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开新增,账号或姓名留空后点击「确 定」 +3. 断言出现必填提示,弹窗仍打开",确定被拦截;不落库;弹窗不关闭。, +USER-USR-017,基础设置 · 用户管理,用户管理 · 新增用户,登录账号重复拦截,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe);默认查询至少 1 条且登录账号非空,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开新增,账号填入已存在的 `${sampleLoginName}`,补齐其它必填后点「确 定」 +3. 断言保存失败并提示,弹窗仍打开 +4. 查询该账号,断言原用户未被覆盖",重复账号被拒绝;不覆盖原用户。, +USER-USR-018,基础设置 · 用户管理,用户管理 · 编辑用户,打开编辑用户弹窗并回填,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击首行「编辑」 +3. 断言弹窗标题含「编辑用户」 +4. 断言账号、姓名与该行 `${sampleLoginName}`、`${sampleUserName}` 一致(允许规范化空白)",编辑弹窗可见;回填与列表行一致。, +USER-USR-019,基础设置 · 用户管理,用户管理 · 编辑用户,编辑取消不保存,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开首行编辑,记录原姓名 +3. 将姓名改为临时值后点「取 消」 +4. 再次打开编辑,断言姓名仍为原值",取消后列表与回看均为修改前姓名。, +USER-USR-020,基础设置 · 用户管理,用户管理 · 编辑用户,编辑时账号只读,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开首行编辑 +3. 断言登录账号不可改(disabled/readonly 或无法输入)",编辑态登录账号只读。, +USER-USR-021,基础设置 · 用户管理,用户管理 · 编辑用户,编辑弹窗可见签名图片,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 打开首行编辑 +3. 断言可见「签名图片」区域(有图则展示,无图则上传入口可见) +4. 点「取 消」,不保存",签名图片控件可见;本条不上传、不保存。, +USER-USR-022,基础设置 · 用户管理,用户管理 · 授权,打开授权后取消不生效,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击首行「授权」 +3. 断言打开授权层(弹窗或权限树),可见可勾选菜单/功能 +4. 如可勾选则改一处勾选后点取消/关闭 +5. 再次打开同一用户授权,断言勾选与操作前一致",授权层可打开;取消后勾选不落库。, +USER-USR-023,基础设置 · 用户管理,用户管理 · 重置密码,行内重置密码确认后取消,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 用户管理」权限;已进入用户管理页(/spd/basic/user/platform/person,无 iframe);列表至少 1 条,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点击首行「重置密码」 +3. 断言出现确认框或密码表单,含取消/关闭 +4. 点取消/关闭 +5. 断言仍停留本页且该行仍在",重置需确认;取消后不改密。本条不点确定,避免污染账号。, diff --git "a/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" new file mode 100644 index 0000000..c03013d --- /dev/null +++ "b/testcase/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.csv" @@ -0,0 +1,54 @@ +用例编号,功能模块,功能测试点,用例标题,优先级,前置条件,测试步骤,预期结果,实际结果 +USER-ROL-001,基础设置 · 用户管理,角色管理 · 页面进入,菜单进入角色管理页,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 按菜单「基础设置 → 用户管理 → 角色管理」进入,或 `openPath` 打开 `/spd/basic/user/platform/role` +3. 断言 URL 含 `/spd/basic/user/platform/role` +4. 断言左侧角色列表、右侧菜单权限树、「保 存」「刷 新」可见,无独立分页",进入角色管理;左右分栏渲染完成;不误入用户管理页。, +USER-ROL-002,基础设置 · 用户管理,角色管理 · 页面进入,未登录访问拦截,高,测试环境可访问;使用独立 context,storageState 为空(不加载 test_1 登录态),"1. 新建独立 BrowserContext,不加载 auth/user.json +2. 清除登录态后直接打开 `/spd/basic/user/platform/role` +3. 断言跳转登录或不展示角色/权限树业务数据",未登录不可见角色配置。, +USER-ROL-003,基础设置 · 用户管理,角色管理 · 左侧角色列表,角色列表加载并可点选,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 断言左栏列出角色名称(如医院管理员、护士等) +3. 点击首个 `${sampleRoleName}` +4. 断言该行处于选中态",左栏有角色且可点选。, +USER-ROL-004,基础设置 · 用户管理,角色管理 · 左侧角色列表,角色名称搜索过滤,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe);左栏至少 1 个角色,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 在角色名称框输入 `${sampleRoleName}` 的部分关键字 +3. 断言左栏收敛为匹配项(即时或回车,以现网为准)",搜索结果角色名包含关键字。, +USER-ROL-005,基础设置 · 用户管理,角色管理 · 左侧角色列表,选中角色刷新右侧权限树,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe);左栏至少 2 个角色,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 依次点选两个不同角色 +3. 断言右侧勾选随当前角色变化,无上一角色残留",权限树与当前角色配置一致。, +USER-ROL-006,基础设置 · 用户管理,角色管理 · 左侧角色列表,清空搜索恢复全量,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 先按关键字过滤,再清空搜索 +3. 断言左栏恢复未过滤的完整角色列表",清空后角色列表恢复全量。, +USER-ROL-007,基础设置 · 用户管理,角色管理 · 左侧角色列表,搜索无匹配空态(mock反例),中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 角色名称框填入 `__NOT_EXIST_ROLE__99999` +3. 断言左栏空态明确",无匹配不报错;空态明确。, +USER-ROL-008,基础设置 · 用户管理,角色管理 · 右侧菜单权限树,权限树按应用与模块展开,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 选中任一角色 +3. 断言右侧树根为「SPD内网系统PC端」,可见基础设置/运维管理/厂商管理/产品管理等模块 +4. 展开「基础设置」,断言可见子菜单(如医院资料)",权限树层级可见;节点可展开。, +USER-ROL-009,基础设置 · 用户管理,角色管理 · 右侧菜单权限树,勾选父节点联动子节点,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 选中一自定义或可编辑角色(预置被锁则 skip) +3. 记录「厂商管理」父节点原勾选 +4. 勾选「厂商管理」父节点 +5. 断言其子菜单/功能全部勾选 +6. 点击「刷 新」,不点「保 存」",父节点勾选联动子节点;本条不保存。, +USER-ROL-010,基础设置 · 用户管理,角色管理 · 右侧菜单权限树,刷新恢复未保存勾选,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 改一处勾选后点「刷 新」且未保存 +3. 断言树恢复为进入该角色时的已保存状态",刷新丢弃未保存勾选。, +USER-ROL-011,基础设置 · 用户管理,角色管理 · 角色增删改,删除需确认且取消保留,高,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 点一自定义角色的删除入口(预置角色无删除则改点自定义;都没有则 skip) +3. 断言出现确认框,含取消/确定 +4. 点「取 消」 +5. 断言该角色仍在左栏",删除需二次确认;取消后角色仍在。本条不点确定。, +USER-ROL-012,基础设置 · 用户管理,角色管理 · 角色增删改,新增角色入口打开后取消,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 通过新增或「角色」工具按钮打开新增表单(扫描未打开则 skip) +3. 不填或填写后点取消/关闭 +4. 断言左栏角色数量不增加",新增入口可达;取消不落库。, +USER-ROL-013,基础设置 · 用户管理,角色管理 · 角色增删改,预置角色不可删除,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe);左栏存在可识别的预置角色,否则 skip,"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 在左栏定位「医院管理员」(或其它预置角色,以现网为准) +3. 断言删除入口不可见;若可见则确定后被拒绝,角色仍在左栏 +4. 若弹出确认则点取消,避免误删",预置角色不可删除。, +USER-ROL-014,基础设置 · 用户管理,角色管理 · 左侧角色列表,角色名与用户管理角色下拉同源,中,测试环境可访问;正例账号 test_1 已通过 auth.setup 写入 storageState 登录(密码仅存 .env TEST_PASS,勿写入本表);具备「基础设置 → 用户管理 → 角色管理」权限;已进入角色管理页(/spd/basic/user/platform/role,无 iframe),"1. 确认 chromium 已加载 storageState(auth.setup,正例账号 test_1),无需在用例内重复登录 +2. 记录本页左栏角色名称集合 +3. 打开用户管理,展开「角色」筛选项 +4. 断言两边名称集合一致(允许按权限裁剪,本页有的名称用户页应能对应到)",角色主数据跨页一致,不串名。, diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..91eda21 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,242 @@ +# 基础管理-准入管理-产品准入单列表 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-准入管理-产品准入单列表-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-准入管理-产品准入单列表-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/dataSync/dataSync/productOrder/list` +**定位器 YAML**:`locators/spd/admit-product-list.locator.yaml` +**元素扫描**:`data/scan/admit-product-list-elements.json`(77 项) +**版本**:2026-09-22 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/admit-product-list.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 准入管理 → 产品准入单列表」权限。 +**页面约定**:整页渲染,**无报表 iframe**。与物料准入单列表 URL 不同,列表多「备注」「附件」列。 +**菜单导航**:展开「基础设置 → 准入管理」后,点击 `.ant-menu-item` 文案精确为「产品准入单列表」。 +**写操作**:不确认「通过」,避免推进真实审批。 + +**动态取数约定**: +1. 默认查询后从首行读取 `${sampleOrderNo}`、`${sampleProductName}`、`${sampleProductCount}`、`${sampleStatus}`、`${sampleAttachmentCount}`。 +2. **禁止写死**准入单号与产品名称。 +3. 状态 Tab:全部、审批中、已通过、已驳回。 + +--- + +## 基础设置 · 准入管理 + +### 产品准入单列表 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-PLS-001】【TP_ADMIT_PLS_001】菜单进入产品准入单列表 + +- 前置条件:已登录且具备准入管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 准入管理 → 产品准入单列表」进入 + 2. 断言 URL 含 `/spd/basic/dataSync/dataSync/productOrder/list`,且路径不是 `zrlist` + 3. 断言制单日期、查询、状态 Tab、列表表头可见 +- 自动化断言:规划与 CSV `ADMIT-PLS-001` 及 `tests/spd/admit-product-list.spec.ts` 一致 + +###### 预期结果 + +进入产品准入单列表;不误入物料准入单列表。 + +--- + +### 产品准入单列表 · 筛选与状态 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-PLS-002】【TP_ADMIT_PLS_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入产品准入单列表 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-PLS-002` 一致 + +###### 预期结果 + +查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-ADMIT-PLS-003】【TP_ADMIT_PLS_010】按产品名称筛选 + +- 前置条件:默认查询至少 1 条且产品名称非空 +- 操作步骤: + 1. 从首行读取 `${sampleProductName}` + 2. 若筛选区无产品名称,则用注册证号或器械注册人中首行非空字段 + 3. 制单日期保持默认,点击「查 询」 + 4. 断言结果包含该关键字 +- 自动化断言:规划与 CSV `ADMIT-PLS-003` 一致 + +###### 预期结果 + +结果与已填关键字匹配。 + +--- + +##### 【P1】【TC-UI-ADMIT-PLS-004】【TP_ADMIT_PLS_011】重置恢复默认 + +- 前置条件:已填入筛选关键字 +- 操作步骤: + 1. 点击「重 置」 + 2. 断言筛选恢复默认 +- 自动化断言:规划与 CSV `ADMIT-PLS-004` 一致 + +###### 预期结果 + +筛选恢复默认;列表按默认规则刷新。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-ADMIT-PLS-005】【TP_ADMIT_PLS_019】无匹配结果空态(mock反例) + +- 前置条件:已进入产品准入单列表 +- 操作步骤: + 1. 注册证号填入 `__NOT_EXIST_CERT__99999` + 2. 点击「查 询」 + 3. 断言空态且分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-PLS-005` 一致 + +###### 预期结果 + +无匹配时不报错;总数为 0。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-PLS-006】【TP_ADMIT_PLS_013】列表核心列与附件计数 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 断言表头含准入单号、进院供应商、二级供应商、产品名称、注册证号、耗材类型、产品条数、状态、备注、附件、操作 + 2. 断言首行附件列展示个数(可为 0 个) +- 自动化断言:规划与 CSV `ADMIT-PLS-006` 一致 + +###### 预期结果 + +核心列可见;附件列有个数;操作为「详情」。本页列表不要求「准入类型」列。 + +--- + +##### 【P0】【TC-UI-ADMIT-PLS-007】【TP_ADMIT_PLS_012】切换已通过 Tab + +- 前置条件:已进入产品准入单列表 +- 操作步骤: + 1. 点击「已通过」 + 2. 断言有数据时状态为已通过 + 3. 断言 Tab 括号数量等于分页「共 N 条」 +- 自动化断言:规划与 CSV `ADMIT-PLS-007` 一致 + +###### 预期结果 + +已通过 Tab 的状态与计数一致。 + +--- + +##### 【P1】【TC-UI-ADMIT-PLS-008】【TP_ADMIT_PLS_021】与物料准入单列表不串 URL + +- 前置条件:当前在产品准入单列表 +- 操作步骤: + 1. 记录当前 URL + 2. 断言 URL 含 `productOrder/list` 且不含 `zrlist` + 3. 不点击物料准入单列表菜单 +- 自动化断言:规划与 CSV `ADMIT-PLS-008` 一致 + +###### 预期结果 + +停留在产品准入单列表路径,不串到物料单路径。 + +--- + +### 产品准入单列表 · 单据详情 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-PLS-009】【TP_ADMIT_PLS_022】打开详情后返回 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 记录首行 `${sampleOrderNo}` + 2. 点击「详情」 + 3. 断言可见「审批进程」「单据日志」「返 回」 + 4. 返回后 URL 仍含 `productOrder/list` +- 自动化断言:规划与 CSV `ADMIT-PLS-009` 一致 + +###### 预期结果 + +详情可打开;返回后仍是产品准入单列表。 + +--- + +##### 【P0】【TC-UI-ADMIT-PLS-010】【TP_ADMIT_PLS_023】详情产品条数与列表一致 + +- 前置条件:已打开首行详情 +- 操作步骤: + 1. 读取详情明细分页总数或当前行数 + 2. 断言等于列表「产品条数」 +- 自动化断言:规划与 CSV `ADMIT-PLS-010` 一致 + +###### 预期结果 + +明细条数与产品条数一致。 + +--- + +##### 【P1】【TC-UI-ADMIT-PLS-011】【TP_ADMIT_PLS_025】单据日志可打开 + +- 前置条件:已打开单据详情 +- 操作步骤: + 1. 点击「单据日志」 + 2. 断言日志层可见 + 3. 关闭后仍在该单据 +- 自动化断言:规划与 CSV `ADMIT-PLS-011` 一致 + +###### 预期结果 + +单据日志可查看;详情不被销毁。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-ADMIT-PLS-012】【TP_ADMIT_PLS_028】详情搜索无匹配(mock反例) + +- 前置条件:已打开单据详情 +- 操作步骤: + 1. 搜索框填入 `__NOT_EXIST_GOODS__99999` + 2. 断言明细空态,返回按钮仍在 +- 自动化断言:规划与 CSV `ADMIT-PLS-012` 一致 + +###### 预期结果 + +无匹配只影响明细;单据头仍在。 + +--- + +##### 【P0】【TC-UI-ADMIT-PLS-013】【TP_ADMIT_PLS_033】已通过单据不确认通过 + +- 前置条件:已通过 Tab 至少 1 条并打开详情 +- 操作步骤: + 1. 「通过」禁用或不可见时,断言状态保持已通过 + 2. 若可点且出现确认框,点「取 消」 + 3. 返回列表,断言状态仍为已通过 +- 自动化断言:规划与 CSV `ADMIT-PLS-013` 一致 + +###### 预期结果 + +不把已通过单据再次审批成功。无已通过数据则 skip。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **13** 条(P0=7 / P1=6) | +| 来源 | 从 34 条测试点选取查询、附件列、详情与审批门禁 | +| 未纳入自动化 | 未登录/权限、确认「通过」、与产品准入申请的跨页追单、附件文件内容打开 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..9f1a6eb --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,339 @@ +# 基础管理-准入管理-产品准入申请 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-准入管理-产品准入申请-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-准入管理-产品准入申请-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/dataSync/dataSync/productApply/list` +**定位器 YAML**:`locators/spd/admit-product-apply.locator.yaml` +**元素扫描**:`data/scan/admit-product-apply-elements.json`(467 项) +**版本**:2026-09-22 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/admit-product-apply.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 准入管理 → 产品准入申请」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**菜单导航**:展开「基础设置 → 准入管理」后,点击 `.ant-menu-item` 文案精确为「产品准入申请」。 +**写操作**:不真实保存新产品、不导入 Excel、不确认生成申请单或作废。新增页只校验打开、取消和必填拦截。 + +**动态取数约定**: +1. 默认查询后从首行读取 `${sampleProductName}`、`${sampleSpec}`、`${sampleConsumableType}`、`${sampleOrderNo}`。 +2. 筛选使用样本占位,**禁止写死**产品名称与单号。 +3. 状态 Tab:全部、待申请、审批中、已通过、已驳回、已作废、生效失败、已生效。分页含「跳至」。总条数不足两页时翻页用例 skip。 + +--- + +## 基础设置 · 准入管理 + +### 产品准入申请 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-PAP-001】【TP_ADMIT_PAP_001】菜单进入产品准入申请页 + +- 前置条件:已登录且具备准入管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 准入管理 → 产品准入申请」进入 + 2. 断言 URL 含 `/spd/basic/dataSync/dataSync/productApply/list`,且不是 `ZRlist` + 3. 断言可见「查 询」、「Excel导入」「生成申请单」「一键生成」「新增产品」「作 废」及列表表头 +- 自动化断言:规划与 CSV `ADMIT-PAP-001` 及 `tests/spd/admit-product-apply.spec.ts` 一致 + +###### 预期结果 + +进入产品准入申请页;不误入物料准入管理(ZRlist)或产品准入单列表。 + +--- + +### 产品准入申请 · 筛选查询 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-PAP-002】【TP_ADMIT_PAP_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入产品准入申请页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-PAP-002` 一致 + +###### 预期结果 + +查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-ADMIT-PAP-003】【TP_ADMIT_PAP_012】产品名称筛选 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleProductName}` + 2. 填入产品名称,新增日期保持默认,点击「查 询」 + 3. 断言结果行「产品名称」均包含该关键字 +- 自动化断言:规划与 CSV `ADMIT-PAP-003` 一致 + +###### 预期结果 + +产品名称与已填关键字匹配。 + +--- + +##### 【P0】【TC-UI-ADMIT-PAP-004】【TP_ADMIT_PAP_013】名称与耗材类型组合查询 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 读取首行 `${sampleProductName}`、`${sampleConsumableType}` + 2. 同时填入后点击「查 询」(日期保持默认) + 3. 断言结果同时满足两个条件 +- 自动化断言:规划与 CSV `ADMIT-PAP-004` 一致 + +###### 预期结果 + +组合查询结果同时满足产品名称与耗材类型。类型为空则只断言名称。 + +--- + +##### 【P1】【TC-UI-ADMIT-PAP-005】【TP_ADMIT_PAP_014】重置恢复默认 + +- 前置条件:已进入产品准入申请页 +- 操作步骤: + 1. 填入 `${sampleProductName}` + 2. 点击「重 置」 + 3. 断言筛选恢复默认 +- 自动化断言:规划与 CSV `ADMIT-PAP-005` 一致 + +###### 预期结果 + +筛选字段恢复默认;列表按默认规则刷新。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-ADMIT-PAP-006】【TP_ADMIT_PAP_018】无匹配结果空态(mock反例) + +- 前置条件:已进入产品准入申请页 +- 操作步骤: + 1. 产品名称填入 `__NOT_EXIST_GOODS__99999` + 2. 点击「查 询」 + 3. 断言空态且分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-PAP-006` 一致 + +###### 预期结果 + +无匹配时不报错;总数为 0。 + +--- + +### 产品准入申请 · 状态与列表 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-PAP-007】【TP_ADMIT_PAP_022】列表核心列完整 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 断言表头含准入单号、产品编号、产品名称、规格型号、注册规格、注册型号、器械注册人、单位、操作 +- 自动化断言:规划与 CSV `ADMIT-PAP-007` 一致 + +###### 预期结果 + +核心列可见;操作含「详情」「日志」。 + +--- + +##### 【P0】【TC-UI-ADMIT-PAP-008】【TP_ADMIT_PAP_020】切换待申请 Tab + +- 前置条件:已进入产品准入申请页 +- 操作步骤: + 1. 点击「待申请」 + 2. 断言有数据时行不属于已生效/已作废;计数为 0 时为空态 +- 自动化断言:规划与 CSV `ADMIT-PAP-008` 一致 + +###### 预期结果 + +待申请 Tab 与列表状态一致。 + +--- + +##### 【P1】【TC-UI-ADMIT-PAP-009】【TP_ADMIT_PAP_023】翻至第 2 页 + +- 前置条件:分页总数大于每页条数 +- 操作步骤: + 1. 记录第 1 页首行准入单号 + 2. 点击页码「2」或「下一页」 + 3. 断言页码高亮为 2,首行单号与第 1 页不同 +- 自动化断言:规划与 CSV `ADMIT-PAP-009` 一致 + +###### 预期结果 + +翻页成功且数据更换。不足两页则 skip。 + +--- + +##### 【P1】【TC-UI-ADMIT-PAP-010】【TP_ADMIT_PAP_023】跳至指定页 + +- 前置条件:总页数不少于 2,分页存在「跳至」 +- 操作步骤: + 1. 在跳至输入框填入 `2` 并回车 + 2. 断言当前页为 2 +- 自动化断言:规划与 CSV `ADMIT-PAP-010` 一致 + +###### 预期结果 + +跳转到第 2 页。无跳至或仅 1 页则 skip。 + +--- + +### 产品准入申请 · 新增与导入 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-PAP-011】【TP_ADMIT_PAP_027】打开新增产品后返回 + +- 前置条件:已进入产品准入申请页 +- 操作步骤: + 1. 点击「新增产品」 + 2. 断言可见产品名称、器械注册人、规格型号、注册证号及返回 + 3. 点击返回,不保存 +- 自动化断言:规划与 CSV `ADMIT-PAP-011` 一致 + +###### 预期结果 + +新增页字段可见;返回后列表不新增记录。 + +--- + +##### 【P0】【TC-UI-ADMIT-PAP-012】【TP_ADMIT_PAP_029】新增取消不落库 + +- 前置条件:已进入产品准入申请页 +- 操作步骤: + 1. 记录分页总数 `${sampleTotal}` + 2. 打开新增,产品名称填 `${uniqProductName}`(建议 `AUTO-PAP-${timestamp}`)后返回不保存 + 3. 按该名称查询,断言无此行,总数不变 +- 自动化断言:规划与 CSV `ADMIT-PAP-012` 一致 + +###### 预期结果 + +取消后不产生新产品。 + +--- + +##### 【P1】【TC-UI-ADMIT-PAP-013】【TP_ADMIT_PAP_030】Excel 导入打开后取消 + +- 前置条件:已进入产品准入申请页 +- 操作步骤: + 1. 记录总数 `${sampleTotal}` + 2. 点击「Excel导入」 + 3. 断言导入层打开 + 4. 关闭或取消,不选择文件 + 5. 断言总数不变 +- 自动化断言:规划与 CSV `ADMIT-PAP-013` 一致 + +###### 预期结果 + +取消导入不增加产品。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-ADMIT-PAP-014】【TP_ADMIT_PAP_034】产品名称为空保存拦截(mock反例) + +- 前置条件:已打开新增产品页 +- 操作步骤: + 1. 产品名称保持 mock 空值,其它能填的非名称字段可留空 + 2. 点击保存 + 3. 断言必填提示,页面不关闭 + 4. 返回列表,断言总数不变 +- 自动化断言:规划与 CSV `ADMIT-PAP-014` 一致 + +###### 预期结果 + +保存被拦截;不落库。 + +--- + +### 产品准入申请 · 详情、编辑与作废 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-PAP-015】【TP_ADMIT_PAP_046】打开详情后返回 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 记录首行 `${sampleProductName}` + 2. 点击「详情」,断言名称一致 + 3. 返回列表 +- 自动化断言:规划与 CSV `ADMIT-PAP-015` 一致 + +###### 预期结果 + +详情与该行一致;返回后仍在产品准入申请列表。 + +--- + +##### 【P0】【TC-UI-ADMIT-PAP-016】【TP_ADMIT_PAP_047】待申请编辑回填 + +- 前置条件:待申请至少 1 行含「编辑」 +- 操作步骤: + 1. 读取该行 `${sampleProductName}`、`${sampleSpec}` + 2. 点击「编辑」 + 3. 断言名称与规格型号回填一致 + 4. 返回不保存 +- 自动化断言:规划与 CSV `ADMIT-PAP-016` 一致 + +###### 预期结果 + +编辑回填与列表行一致。无编辑按钮则 skip。 + +--- + +##### 【P1】【TC-UI-ADMIT-PAP-017】【TP_ADMIT_PAP_049】编辑取消不保存 + +- 前置条件:存在可编辑行 +- 操作步骤: + 1. 打开编辑,把产品名称改为 `${uniqProductName}` 后返回不保存 + 2. 再次打开,断言名称仍为原值 +- 自动化断言:规划与 CSV `ADMIT-PAP-017` 一致 + +###### 预期结果 + +取消后数据不变。 + +--- + +##### 【P0】【TC-UI-ADMIT-PAP-018】【TP_ADMIT_PAP_041】作废确认后取消 + +- 前置条件:待申请至少 1 条可勾选 +- 操作步骤: + 1. 勾选 1 行并点击「作 废」 + 2. 断言确认框出现 + 3. 点击「取 消」 + 4. 断言该行仍在,状态未变为已作废 +- 自动化断言:规划与 CSV `ADMIT-PAP-018` 一致 + +###### 预期结果 + +取消作废不改变状态。本条不点击确定。 + +--- + +##### 【P1】【TC-UI-ADMIT-PAP-019】【TP_ADMIT_PAP_051】已通过行不提供编辑 + +- 前置条件:已通过 Tab 计数大于 0 +- 操作步骤: + 1. 切换「已通过」 + 2. 断言首行无「编辑」,或打开后保存被拒绝 +- 自动化断言:规划与 CSV `ADMIT-PAP-019` 一致 + +###### 预期结果 + +已通过行不能改写产品名称。计数为 0 则 skip。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **19** 条(P0=11 / P1=8) | +| 来源 | 从 56 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 未登录/权限、合法新增落库、Excel 真实导入、确认生成申请单、一键生成、确认作废、跨单列表追溯 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..6733376 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,305 @@ +# 基础管理-准入管理-供货目录申请 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-准入管理-供货目录申请-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-准入管理-供货目录申请-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/dataSync/dataSync/supplyApply/list` +**定位器 YAML**:`locators/spd/admit-supply.locator.yaml` +**元素扫描**:`data/scan/admit-supply-elements.json`(117 项) +**版本**:2026-09-22 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/admit-supply.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 准入管理 → 供货目录申请」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**菜单导航**:展开「基础设置 → 准入管理」后,点击 `.ant-menu-item` 文案精确为「供货目录申请」。 +**写操作**:不提交新申请、不导入 Excel、不确认批量提交。新增页只做打开、取消和空提交拦截。 + +**动态取数约定**: +1. 默认查询后从首行读取 `${sampleOrderNo}`、`${sampleHospitalSupplier}`、`${sampleSecondSupplier}`、`${sampleBizMode}`、`${sampleDefaultFlag}`。 +2. **禁止写死**供应商名称与申请单号。 +3. 状态 Tab:全部、待提交、审批中、已通过、已驳回。无待提交行时,编辑与批量勾选用例 skip。 + +--- + +## 基础设置 · 准入管理 + +### 供货目录申请 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-SUP-001】【TP_ADMIT_SUP_001】菜单进入供货目录申请页 + +- 前置条件:已登录且具备准入管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 准入管理 → 供货目录申请」进入 + 2. 断言 URL 含 `/spd/basic/dataSync/dataSync/supplyApply/list` + 3. 断言可见「查 询」、「批量提交」、「新 增」及列表表头 +- 自动化断言:规划与 CSV `ADMIT-SUP-001` 及 `tests/spd/admit-supply.spec.ts` 一致 + +###### 预期结果 + +进入供货目录申请页;不误入准入任务分配。 + +--- + +### 供货目录申请 · 筛选与状态 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-SUP-002】【TP_ADMIT_SUP_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入供货目录申请页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-SUP-002` 一致 + +###### 预期结果 + +查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-ADMIT-SUP-003】【TP_ADMIT_SUP_010】按进院供应商筛选 + +- 前置条件:默认查询至少 1 条且进院供应商非空 +- 操作步骤: + 1. 从首行读取 `${sampleHospitalSupplier}` + 2. 在进院供应商选择该值,制单日期保持默认,点击「查 询」 + 3. 断言结果行「进院供应商」与所选值一致 +- 自动化断言:规划与 CSV `ADMIT-SUP-003` 一致 + +###### 预期结果 + +进院供应商列与条件一致。 + +--- + +##### 【P1】【TC-UI-ADMIT-SUP-004】【TP_ADMIT_SUP_012】重置恢复默认 + +- 前置条件:已选择进院供应商 +- 操作步骤: + 1. 点击「重 置」 + 2. 断言进院供应商等筛选恢复默认 +- 自动化断言:规划与 CSV `ADMIT-SUP-004` 一致 + +###### 预期结果 + +筛选恢复默认;列表按默认规则刷新。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-ADMIT-SUP-005】【TP_ADMIT_SUP_019】变更单号无匹配空态(mock反例) + +- 前置条件:已进入供货目录申请页 +- 操作步骤: + 1. 变更单号填入 `__NOT_EXIST_ORDER__99999`;若筛选区无此框,则在产品名称填同一 mock + 2. 点击「查 询」 + 3. 断言空态且分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-SUP-005` 一致 + +###### 预期结果 + +无匹配时不报错;总数为 0。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-SUP-006】【TP_ADMIT_SUP_014】列表核心列完整 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 断言表头含申请单号、申请人、申请时间、进院供应商、二级供应商、业务模式、是否默认、备注、操作 +- 自动化断言:规划与 CSV `ADMIT-SUP-006` 一致 + +###### 预期结果 + +核心列可见;操作含「编辑」或「详情」(随状态变化)。 + +--- + +##### 【P0】【TC-UI-ADMIT-SUP-007】【TP_ADMIT_SUP_013】切换待提交 Tab 且计数一致 + +- 前置条件:已进入供货目录申请页 +- 操作步骤: + 1. 点击「待提交」 + 2. 读取括号数量 + 3. 断言分页「共 N 条」等于该数量;有数据时状态为待提交 +- 自动化断言:规划与 CSV `ADMIT-SUP-007` 一致 + +###### 预期结果 + +待提交 Tab 与分页总数一致。计数为 0 时为空态。 + +--- + +### 供货目录申请 · 新增与编辑 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-SUP-008】【TP_ADMIT_SUP_021】打开新增供货目录 + +- 前置条件:已进入供货目录申请页 +- 操作步骤: + 1. 点击「新 增」 + 2. 断言可见进院供应商、二级供应商、业务模式、是否默认、计划生效时间、添加产品、提交 + 3. 返回不提交 +- 自动化断言:规划与 CSV `ADMIT-SUP-008` 一致 + +###### 预期结果 + +新增页字段齐全;返回后列表不新增申请单。 + +--- + +##### 【P0】【TC-UI-ADMIT-SUP-009】【TP_ADMIT_SUP_026】新增放弃不落库 + +- 前置条件:已进入供货目录申请页 +- 操作步骤: + 1. 记录总数 `${sampleTotal}` + 2. 打开新增,备注填 `${uniqRemark}`(建议 `AUTO-SUP-${timestamp}`)后返回不提交 + 3. 断言总数不变,列表无该备注 +- 自动化断言:规划与 CSV `ADMIT-SUP-009` 一致 + +###### 预期结果 + +未提交不产生申请单。 + +--- + +##### 【P1】【TC-UI-ADMIT-SUP-010】【TP_ADMIT_SUP_048】业务模式与是否默认只切换不提交 + +- 前置条件:已打开新增页 +- 操作步骤: + 1. 展开业务模式、是否默认,断言存在可选项 + 2. 各选一项后返回,不提交 + 3. 断言列表总数不变 +- 自动化断言:规划与 CSV `ADMIT-SUP-010` 一致 + +###### 预期结果 + +选项可见;不提交则无新单。 + +--- + +##### 【P0】【TC-UI-ADMIT-SUP-011】【TP_ADMIT_SUP_025】待提交编辑回填 + +- 前置条件:待提交至少 1 行含「编辑」 +- 操作步骤: + 1. 读取该行 `${sampleHospitalSupplier}`、`${sampleBizMode}`、`${sampleDefaultFlag}` + 2. 点击「编辑」 + 3. 断言进院供应商、业务模式、是否默认与该行一致 + 4. 返回不提交 +- 自动化断言:规划与 CSV `ADMIT-SUP-011` 一致 + +###### 预期结果 + +编辑回填与列表行一致。无待提交或无编辑则 skip。 + +--- + +##### 【P1】【TC-UI-ADMIT-SUP-012】【TP_ADMIT_SUP_026】编辑取消不保存 + +- 前置条件:存在可编辑的待提交行 +- 操作步骤: + 1. 打开编辑,把备注改为 `${uniqRemark}` 后返回不提交 + 2. 再次打开,断言备注仍为原值 +- 自动化断言:规划与 CSV `ADMIT-SUP-012` 一致 + +###### 预期结果 + +取消后备注不变。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-ADMIT-SUP-013】【TP_ADMIT_SUP_030】进院供应商为空不可提交(mock反例) + +- 前置条件:已打开新增页,进院供应商未选 +- 操作步骤: + 1. 不选进院供应商,点击「提 交」 + 2. 断言必填提示,页面不关闭 + 3. 返回列表,断言总数不变 +- 自动化断言:规划与 CSV `ADMIT-SUP-013` 一致 + +###### 预期结果 + +提交被拦截;不产生申请单。 + +--- + +##### 【P0】【TC-UI-ADMIT-SUP-014】【TP_ADMIT_SUP_031】未添加产品不可提交 + +- 前置条件:已打开新增页,产品明细为 0 +- 操作步骤: + 1. 若进院供应商、二级供应商、业务模式可从样本选择则先选上,但不添加产品 + 2. 点击「提 交」 + 3. 断言拦截提示,明细仍为 0,列表总数不变 +- 自动化断言:规划与 CSV `ADMIT-SUP-014` 一致 + +###### 预期结果 + +没有产品明细时不能提交。 + +--- + +### 供货目录申请 · 批量提交与详情 + +#### 功能验证点 + +##### 【P1】【TC-UI-ADMIT-SUP-015】【TP_ADMIT_SUP_042】未勾选时批量提交不可用 + +- 前置条件:已进入供货目录申请页,无勾选行 +- 操作步骤: + 1. 断言「批量提交」计数为 0,或按钮不可用 + 2. 若仍可点击,断言出现请选择提示,且无单据离开待提交 +- 自动化断言:规划与 CSV `ADMIT-SUP-015` 一致 + +###### 预期结果 + +未勾选不提交任何申请单。 + +--- + +##### 【P0】【TC-UI-ADMIT-SUP-016】【TP_ADMIT_SUP_038】勾选后批量提交取消 + +- 前置条件:待提交至少 1 条 +- 操作步骤: + 1. 勾选 1 行,记录申请单号与状态 + 2. 点击「批量提交」 + 3. 若出现确认框,点「取 消」 + 4. 断言该单仍为待提交 +- 自动化断言:规划与 CSV `ADMIT-SUP-016` 一致 + +###### 预期结果 + +取消批量提交不改变状态。本条不点击确定。无待提交则 skip。 + +--- + +##### 【P0】【TC-UI-ADMIT-SUP-017】【TP_ADMIT_SUP_039】打开详情后返回 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 点击首行「详情」 + 2. 断言可见「审批进程」「单据日志」「返 回」 + 3. 返回后 URL 仍含 `supplyApply/list` +- 自动化断言:规划与 CSV `ADMIT-SUP-017` 一致 + +###### 预期结果 + +详情可打开;返回后仍在供货目录申请列表。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **17** 条(P0=11 / P1=6) | +| 来源 | 从 48 条测试点选取查询、新增拦截、编辑回填、批量提交取消与详情 | +| 未纳入自动化 | 未登录/权限、真实提交、Excel 导入落库、确认批量提交、是否默认冲突后的供货关系回写 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..6c70355 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,261 @@ +# 基础管理-准入管理-准入任务分配 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-准入管理-准入任务分配-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-准入管理-准入任务分配-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/dataSync/dataSync/accessKind/list` +**定位器 YAML**:`locators/spd/admit-task.locator.yaml` +**元素扫描**:`data/scan/admit-task-elements.json`(158 项) +**版本**:2026-09-22 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/admit-task.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 准入管理 → 准入任务分配」权限。 +**页面约定**:整页渲染,**无报表 iframe**。分类 Tab 为「待分类」「已分类」,分页含「跳至」。 +**菜单导航**:展开「基础设置 → 准入管理」后,点击 `.ant-menu-item` 文案精确为「准入任务分配」。 +**写操作**:不确认分配类型、不确认批量分配,避免改生产分类。 + +**动态取数约定**: +1. 在待分类默认查询后,从首行读取 `${sampleProductName}`、`${sampleProductCode}`、`${sampleSpec}`。 +2. **禁止写死**产品名称与产品编号。 +3. 总条数不足两页时,翻页与跳至用例 skip。已分类计数为 0 时,已分类用例只断言空态。 + +--- + +## 基础设置 · 准入管理 + +### 准入任务分配 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-TSK-001】【TP_ADMIT_TSK_001】菜单进入准入任务分配页 + +- 前置条件:已登录且具备准入管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 准入管理 → 准入任务分配」进入 + 2. 断言 URL 含 `/spd/basic/dataSync/dataSync/accessKind/list` + 3. 断言可见「查 询」、「待分类」「已分类」、「批量分配」及列表表头 +- 自动化断言:规划与 CSV `ADMIT-TSK-001` 及 `tests/spd/admit-task.spec.ts` 一致 + +###### 预期结果 + +进入准入任务分配页;不误入供货目录申请。 + +--- + +### 准入任务分配 · 筛选与分类 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-TSK-002】【TP_ADMIT_TSK_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入准入任务分配页,当前为待分类 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-TSK-002` 一致 + +###### 预期结果 + +查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-ADMIT-TSK-003】【TP_ADMIT_TSK_011】产品名称筛选 + +- 前置条件:待分类至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleProductName}` + 2. 填入产品名称,新增日期与操作日期保持默认,点击「查 询」 + 3. 断言结果行「产品名称」均包含该关键字 +- 自动化断言:规划与 CSV `ADMIT-TSK-003` 一致 + +###### 预期结果 + +产品名称与已填关键字匹配。 + +--- + +##### 【P0】【TC-UI-ADMIT-TSK-004】【TP_ADMIT_TSK_015】待分类与已分类切换 + +- 前置条件:已进入准入任务分配页 +- 操作步骤: + 1. 记录「待分类」括号数量 `${pendingCount}` + 2. 点击「已分类」,断言分页总数等于已分类括号数量;为 0 时为空态 + 3. 再点「待分类」,断言分页总数等于 `${pendingCount}` +- 自动化断言:规划与 CSV `ADMIT-TSK-004` 一致 + +###### 预期结果 + +两个 Tab 的列表范围与各自计数一致,不串数据。 + +--- + +##### 【P1】【TC-UI-ADMIT-TSK-005】【TP_ADMIT_TSK_014】重置恢复默认 + +- 前置条件:已填入 `${sampleProductName}` +- 操作步骤: + 1. 点击「重 置」 + 2. 断言产品名称等筛选恢复默认 +- 自动化断言:规划与 CSV `ADMIT-TSK-005` 一致 + +###### 预期结果 + +筛选恢复默认;列表按默认规则刷新。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-ADMIT-TSK-006】【TP_ADMIT_TSK_019】无匹配结果空态(mock反例) + +- 前置条件:当前在待分类 +- 操作步骤: + 1. 产品名称填入 `__NOT_EXIST_GOODS__99999` + 2. 点击「查 询」 + 3. 断言空态且分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-TSK-006` 一致 + +###### 预期结果 + +无匹配时不报错;总数为 0。 + +--- + +### 准入任务分配 · 列表分页 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-TSK-007】【TP_ADMIT_TSK_021】列表核心列完整 + +- 前置条件:待分类至少 1 条数据 +- 操作步骤: + 1. 断言表头含准入类型、产品编号、产品名称、规格型号、规格、型号、器械注册人、操作 + 2. 断言待分类行操作含「分配类型」 +- 自动化断言:规划与 CSV `ADMIT-TSK-007` 一致 + +###### 预期结果 + +核心列可见;待分类行可发起分配类型。 + +--- + +##### 【P1】【TC-UI-ADMIT-TSK-008】【TP_ADMIT_TSK_022】翻至第 2 页 + +- 前置条件:待分类总数大于每页条数 +- 操作步骤: + 1. 记录第 1 页首行产品名称或产品编号 + 2. 点击页码「2」或「下一页」 + 3. 断言页码为 2,首行与第 1 页不同 +- 自动化断言:规划与 CSV `ADMIT-TSK-008` 一致 + +###### 预期结果 + +翻页成功且数据更换。不足两页则 skip。 + +--- + +##### 【P1】【TC-UI-ADMIT-TSK-009】【TP_ADMIT_TSK_036】跳至指定页 + +- 前置条件:总页数不少于 2,且存在「跳至」 +- 操作步骤: + 1. 跳至输入 `2` 并回车 + 2. 断言当前页为 2 +- 自动化断言:规划与 CSV `ADMIT-TSK-009` 一致 + +###### 预期结果 + +跳转到第 2 页。无跳至或仅 1 页则 skip。 + +--- + +#### 边界验证点 + +##### 【P1】【TC-UI-ADMIT-TSK-010】【TP_ADMIT_TSK_023】第 1 页上一页不可用 + +- 前置条件:当前在第 1 页 +- 操作步骤: + 1. 断言「上一页」禁用,或点击后仍停留在第 1 页 +- 自动化断言:规划与 CSV `ADMIT-TSK-010` 一致 + +###### 预期结果 + +第 1 页不能再向前翻。 + +--- + +### 准入任务分配 · 分配操作 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-TSK-011】【TP_ADMIT_TSK_027】单行分配类型取消 + +- 前置条件:待分类至少 1 行含「分配类型」 +- 操作步骤: + 1. 记录该行产品名称,以及待分类计数 `${pendingCount}` + 2. 点击「分配类型」 + 3. 断言分配层打开 + 4. 取消或关闭,不确认 + 5. 断言该行仍在待分类,计数仍为 `${pendingCount}` +- 自动化断言:规划与 CSV `ADMIT-TSK-011` 一致 + +###### 预期结果 + +取消分配不改变分类。本条不点击确定。 + +--- + +##### 【P1】【TC-UI-ADMIT-TSK-012】【TP_ADMIT_TSK_024】未勾选时批量分配不可用 + +- 前置条件:待分类页无勾选行 +- 操作步骤: + 1. 点击「批量分配」 + 2. 断言按钮不可用,或提示先选择数据 + 3. 断言待分类计数不变 +- 自动化断言:规划与 CSV `ADMIT-TSK-012` 一致 + +###### 预期结果 + +未勾选不分配任何行。 + +--- + +##### 【P0】【TC-UI-ADMIT-TSK-013】【TP_ADMIT_TSK_028】勾选后批量分配取消 + +- 前置条件:待分类至少 1 条 +- 操作步骤: + 1. 勾选 1 行,记录待分类计数 + 2. 点击「批量分配」 + 3. 若打开分配层或确认框,点取消或关闭 + 4. 断言该行仍在待分类,计数不变 +- 自动化断言:规划与 CSV `ADMIT-TSK-013` 一致 + +###### 预期结果 + +取消批量分配不改变分类。本条不确认分配。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-ADMIT-TSK-014】【TP_ADMIT_TSK_032】已分类行不再按待分类分配 + +- 前置条件:已分类计数大于 0 +- 操作步骤: + 1. 切换到「已分类」 + 2. 断言首行无「分配类型」,或点击后被拒绝且该行仍留在已分类 + 3. 断言待分类计数不因此增加 +- 自动化断言:规划与 CSV `ADMIT-TSK-014` 一致 + +###### 预期结果 + +已分类数据不会被这次操作退回待分类。已分类为 0 则 skip。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **14** 条(P0=7 / P1=7) | +| 来源 | 从 36 条测试点选取查询、分类 Tab、翻页跳至、分配取消 | +| 未纳入自动化 | 未登录/权限、确认分配后的分类回写、未选分类时的提交拦截(依赖分配弹窗字段,扫描未打开该层) | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..bafeb2d --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,244 @@ +# 基础管理-准入管理-物料准入单列表 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-准入管理-物料准入单列表-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-准入管理-物料准入单列表-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/dataSync/dataSync/productOrder/zrlist` +**定位器 YAML**:`locators/spd/admit-material-list.locator.yaml` +**元素扫描**:`data/scan/admit-material-list-elements.json`(79 项) +**版本**:2026-09-22 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/admit-material-list.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 准入管理 → 物料准入单列表」权限。 +**页面约定**:整页渲染,**无报表 iframe**。本页是单据查询,列表行操作是「详情」。 +**菜单导航**:展开「基础设置 → 准入管理」后,点击 `.ant-menu-item` 文案精确为「物料准入单列表」。 +**写操作**:不点击详情里的「通过」确定,避免推进真实审批。 + +**动态取数约定**: +1. 默认查询后从首行读取 `${sampleOrderNo}`、`${sampleAccessType}`、`${sampleProductCount}`、`${sampleStatus}`。 +2. 单号筛选使用样本占位,**禁止写死**准入单号。 +3. 状态 Tab:全部、审批中、已通过、已驳回。 + +--- + +## 基础设置 · 准入管理 + +### 物料准入单列表 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-MLS-001】【TP_ADMIT_MLS_001】菜单进入物料准入单列表 + +- 前置条件:已登录且具备准入管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 准入管理 → 物料准入单列表」进入 + 2. 断言 URL 含 `/spd/basic/dataSync/dataSync/productOrder/zrlist`,且不是 `productOrder/list` + 3. 断言制单日期、查询、状态 Tab、列表表头可见 +- 自动化断言:规划与 CSV `ADMIT-MLS-001` 及 `tests/spd/admit-material-list.spec.ts` 一致 + +###### 预期结果 + +进入物料准入单列表;不误入产品准入单列表。 + +--- + +### 物料准入单列表 · 筛选与状态 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-MLS-002】【TP_ADMIT_MLS_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入物料准入单列表 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-MLS-002` 一致 + +###### 预期结果 + +查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-ADMIT-MLS-003】【TP_ADMIT_MLS_010】按准入单号筛选 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleOrderNo}` + 2. 若页面有准入单号条件则填入;否则用注册证号或产品名称中首行非空字段 + 3. 制单日期保持默认,点击「查 询」 + 4. 断言结果行包含该单号或该关键字 +- 自动化断言:规划与 CSV `ADMIT-MLS-003` 一致 + +###### 预期结果 + +结果与已填单号或关键字匹配。列表筛选区没有单号框时,改用首行注册证号,并在用例备注中记录实际字段。 + +--- + +##### 【P1】【TC-UI-ADMIT-MLS-004】【TP_ADMIT_MLS_011】重置恢复默认 + +- 前置条件:已进入物料准入单列表 +- 操作步骤: + 1. 填入上一条使用的关键字 + 2. 点击「重 置」 + 3. 断言筛选恢复默认 +- 自动化断言:规划与 CSV `ADMIT-MLS-004` 一致 + +###### 预期结果 + +筛选恢复默认;列表按默认规则刷新。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-ADMIT-MLS-005】【TP_ADMIT_MLS_018】无匹配结果空态(mock反例) + +- 前置条件:已进入物料准入单列表 +- 操作步骤: + 1. 注册证号填入 `__NOT_EXIST_CERT__99999` + 2. 点击「查 询」 + 3. 断言空态且分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-MLS-005` 一致 + +###### 预期结果 + +无匹配时不报错;总数为 0。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-MLS-006】【TP_ADMIT_MLS_013】列表核心列完整 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 断言表头含准入类型、准入单号、进院供应商、二级供应商、产品名称、注册证号、耗材类型、产品条数、状态、操作 +- 自动化断言:规划与 CSV `ADMIT-MLS-006` 一致 + +###### 预期结果 + +核心列可见;操作列为「详情」。 + +--- + +##### 【P0】【TC-UI-ADMIT-MLS-007】【TP_ADMIT_MLS_012】切换已通过 Tab + +- 前置条件:已进入物料准入单列表 +- 操作步骤: + 1. 点击「已通过」 + 2. 断言有数据时「状态」列为已通过;计数为 0 时为空态 + 3. 读取 Tab 括号数量,断言与分页「共 N 条」一致 +- 自动化断言:规划与 CSV `ADMIT-MLS-007` 一致 + +###### 预期结果 + +已通过 Tab 的状态列、计数与分页总数一致。 + +--- + +### 物料准入单列表 · 单据详情 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-MLS-008】【TP_ADMIT_MLS_020】打开详情后返回 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 记录首行 `${sampleOrderNo}`、`${sampleProductCount}` + 2. 点击「详情」 + 3. 断言可见「审批进程」「单据日志」「返 回」 + 4. 点击「返 回」 + 5. 断言 URL 仍含 `productOrder/zrlist` +- 自动化断言:规划与 CSV `ADMIT-MLS-008` 一致 + +###### 预期结果 + +进入单据详情并可返回列表。 + +--- + +##### 【P0】【TC-UI-ADMIT-MLS-009】【TP_ADMIT_MLS_021】详情产品条数与列表一致 + +- 前置条件:已打开首行详情,且 `${sampleProductCount}` 为正整数 +- 操作步骤: + 1. 统计详情明细数据行数(跨页则读详情分页总数) + 2. 断言等于列表「产品条数」`${sampleProductCount}` +- 自动化断言:规划与 CSV `ADMIT-MLS-009` 一致 + +###### 预期结果 + +详情明细条数与列表产品条数一致。 + +--- + +##### 【P1】【TC-UI-ADMIT-MLS-010】【TP_ADMIT_MLS_023】审批进程可打开 + +- 前置条件:已打开单据详情 +- 操作步骤: + 1. 点击「审批进程」 + 2. 断言进程层或区域可见 + 3. 关闭后仍停留在该单据 +- 自动化断言:规划与 CSV `ADMIT-MLS-010` 一致 + +###### 预期结果 + +审批进程可查看;单据不被关掉。 + +--- + +##### 【P1】【TC-UI-ADMIT-MLS-011】【TP_ADMIT_MLS_026】详情导出不白屏 + +- 前置条件:已打开单据详情 +- 操作步骤: + 1. 点击「导 出」 + 2. 断言出现下载,或出现导出任务/成功提示 + 3. 断言页面仍为该单据详情,不白屏 +- 自动化断言:规划与 CSV `ADMIT-MLS-011` 一致 + +###### 预期结果 + +导出有反馈;详情页仍在。无导出按钮则 skip。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-ADMIT-MLS-012】【TP_ADMIT_MLS_028】详情搜索无匹配(mock反例) + +- 前置条件:已打开单据详情 +- 操作步骤: + 1. 在「请输入产品编码、名称」填入 `__NOT_EXIST_GOODS__99999` + 2. 触发搜索 + 3. 断言明细空态,单据头(审批进程、返回)仍在 +- 自动化断言:规划与 CSV `ADMIT-MLS-012` 一致 + +###### 预期结果 + +无匹配时只清空明细,不销毁单据头。 + +--- + +##### 【P0】【TC-UI-ADMIT-MLS-013】【TP_ADMIT_MLS_030】已通过单据不确认通过 + +- 前置条件:已通过 Tab 至少 1 条,并打开其详情 +- 操作步骤: + 1. 若「通过」不可见或禁用,断言该状态保持已通过后返回 + 2. 若「通过」可点,点击后出现确认框则点「取 消」 + 3. 返回列表,断言该单状态仍为已通过 +- 自动化断言:规划与 CSV `ADMIT-MLS-013` 一致 + +###### 预期结果 + +本条不把已通过单据再次审批成功。已通过 Tab 无数据则 skip。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **13** 条(P0=7 / P1=6) | +| 来源 | 从 34 条测试点选取可稳定自动化的查询、详情与审批门禁 | +| 未纳入自动化 | 未登录/权限、确认点击「通过」、与物料准入管理的跨页追溯、导出文件内容逐行比对 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..4496559 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,379 @@ +# 基础管理-准入管理-物料准入管理 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-准入管理-物料准入管理-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-准入管理-物料准入管理-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/dataSync/dataSync/productApply/ZRlist` +**定位器 YAML**:`locators/spd/admit-material.locator.yaml` +**元素扫描**:`data/scan/admit-material-elements.json`(328 项) +**版本**:2026-09-22 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/admit-material.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号(凭据见 `.env`);具备「基础设置 → 准入管理 → 物料准入管理」权限。 +**页面约定**:整页渲染,**无报表 iframe**;定位均在 `page` 上。 +**菜单导航**:先点「基础设置」,展开左侧「准入管理」,再点击 `.ant-menu-item` 且文案精确为「物料准入管理」(现网子项不是 `menuitem` role)。 +**写操作**:本文件不真实执行「生成申请单」「一键生成」「确认作废」「确认关联」,避免改生产准入数据。编辑类只做回填、取消和必填拦截。 + +**动态取数约定**: +1. 默认「查询」后从首行读取 `${sampleProductName}`、`${sampleSpec}`、`${sampleConsumableType}`、`${sampleSupplier}`。 +2. 筛选关键字使用样本占位,**禁止写死**产品名称、供应商、注册证号。 +3. 状态 Tab:全部、待申请、审批中、已通过、已驳回、已作废、生效失败、已生效。无「编辑」的行则编辑类用例 skip。 + +--- + +## 基础设置 · 准入管理 + +### 物料准入管理 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-MAT-001】【TP_ADMIT_MAT_001】菜单进入物料准入管理页 + +- 前置条件:已登录且具备准入管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 准入管理 → 物料准入管理」进入 + 2. 断言 URL 含 `/spd/basic/dataSync/dataSync/productApply/ZRlist` + 3. 断言筛选区、「查 询」、状态 Tab、「关联产品」「生成申请单」「一键生成」「作 废」及列表表头可见 + 4. 断言当前高亮不是「产品准入申请」或「物料准入单列表」 +- 自动化断言:规划与 CSV `ADMIT-MAT-001` 及 `tests/spd/admit-material.spec.ts` 一致 + +###### 预期结果 + +进入物料准入管理页;不误入同组其它准入页;筛选、状态 Tab 与工具栏可见。 + +--- + +### 物料准入管理 · 筛选查询 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-MAT-002】【TP_ADMIT_MAT_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入物料准入管理页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-MAT-002` 一致 + +###### 预期结果 + +查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-ADMIT-MAT-003】【TP_ADMIT_MAT_013】产品名称筛选 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleProductName}` + 2. 填入「产品名称」,新增日期保持页面默认,点击「查 询」 + 3. 断言结果行「产品名称」均包含该关键字 +- 自动化断言:规划与 CSV `ADMIT-MAT-003` 一致 + +###### 预期结果 + +查询结果产品名称与已填关键字匹配;无未捕获异常。 + +--- + +##### 【P0】【TC-UI-ADMIT-MAT-004】【TP_ADMIT_MAT_012】耗材类型筛选 + +- 前置条件:默认查询至少 1 条且首行耗材类型非空 +- 操作步骤: + 1. 从首行读取 `${sampleConsumableType}` + 2. 在耗材类型下拉选择该值,点击「查 询」 + 3. 断言结果行「耗材类型」与所选值一致 +- 自动化断言:规划与 CSV `ADMIT-MAT-004` 一致 + +###### 预期结果 + +耗材类型列与所选条件一致。首行类型为空则 skip。 + +--- + +##### 【P0】【TC-UI-ADMIT-MAT-005】【TP_ADMIT_MAT_014】名称与耗材类型组合查询 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 读取首行 `${sampleProductName}`、`${sampleConsumableType}` + 2. 同时填入名称与耗材类型(新增日期保持默认,勿改成单日) + 3. 点击「查 询」 + 4. 断言结果同时满足两个条件 +- 自动化断言:规划与 CSV `ADMIT-MAT-005` 一致 + +###### 预期结果 + +组合查询无报错;结果同时满足产品名称与耗材类型。 + +--- + +##### 【P1】【TC-UI-ADMIT-MAT-006】【TP_ADMIT_MAT_015】重置恢复默认 + +- 前置条件:已进入物料准入管理页 +- 操作步骤: + 1. 填入 `${sampleProductName}` + 2. 点击「重 置」 + 3. 断言产品名称等筛选恢复默认 +- 自动化断言:规划与 CSV `ADMIT-MAT-006` 一致 + +###### 预期结果 + +筛选字段恢复默认;列表按默认规则刷新。 + +--- + +##### 【P1】【TC-UI-ADMIT-MAT-007】【TP_ADMIT_MAT_016】收起后再展开保留条件 + +- 前置条件:已进入物料准入管理页 +- 操作步骤: + 1. 填入 `${sampleProductName}` + 2. 点击「收起」,再展开筛选区 + 3. 断言产品名称仍为已填值 +- 自动化断言:规划与 CSV `ADMIT-MAT-007` 一致 + +###### 预期结果 + +收起不丢失已填条件;展开后值仍在。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-ADMIT-MAT-008】【TP_ADMIT_MAT_021】无匹配结果空态(mock反例) + +- 前置条件:已进入物料准入管理页 +- 操作步骤: + 1. 产品名称填入 mock 值 `__NOT_EXIST_GOODS__99999` + 2. 点击「查 询」 + 3. 断言空态或「暂无数据」,分页总数为 0 +- 自动化断言:规划与 CSV `ADMIT-MAT-008` 一致 + +###### 预期结果 + +无匹配时不报错;空态明确;总数为 0。 + +--- + +### 物料准入管理 · 状态与列表 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-MAT-009】【TP_ADMIT_MAT_028】列表核心列完整 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 点击「查 询」 + 2. 断言表头含准入类型、产品编号、产品名称、规格型号、器械注册人、单位、耗材类型、默认进院供应商、操作 +- 自动化断言:规划与 CSV `ADMIT-MAT-009` 一致 + +###### 预期结果 + +核心列可见;操作列含「详情」「日志」,待申请行另可见「编辑」。 + +--- + +##### 【P0】【TC-UI-ADMIT-MAT-010】【TP_ADMIT_MAT_023】切换待申请 Tab + +- 前置条件:已进入物料准入管理页 +- 操作步骤: + 1. 点击状态 Tab「待申请」 + 2. 断言列表刷新;若有数据,行状态属于待申请(不出现已生效、已作废) + 3. 再点「全部」,断言列表不再只限定待申请 +- 自动化断言:规划与 CSV `ADMIT-MAT-010` 一致 + +###### 预期结果 + +待申请 Tab 只展示待申请数据;「全部」不额外收窄。计数为 0 时为空态且不报错。 + +--- + +##### 【P1】【TC-UI-ADMIT-MAT-011】【TP_ADMIT_MAT_024】待申请 Tab 计数与分页总数一致 + +- 前置条件:已进入物料准入管理页 +- 操作步骤: + 1. 读取「待申请」括号内数量 `${tabCount}` + 2. 点击该 Tab 并查询稳定 + 3. 断言分页「共 N 条」的 N 等于 `${tabCount}` +- 自动化断言:规划与 CSV `ADMIT-MAT-011` 一致 + +###### 预期结果 + +Tab 计数与该状态下分页总数一致。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-ADMIT-MAT-012】【TP_ADMIT_MAT_033】未勾选时生成申请单不可用或提示 + +- 前置条件:已进入物料准入管理页,且当前无勾选行 +- 操作步骤: + 1. 确认表头复选框与行复选框均未选中 + 2. 点击「生成申请单」 + 3. 断言按钮不可用,或出现「请选择」类提示,且列表不新增申请单 +- 自动化断言:规划与 CSV `ADMIT-MAT-012` 一致 + +###### 预期结果 + +未勾选不产生申请单。 + +--- + +### 物料准入管理 · 详情与编辑 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-MAT-013】【TP_ADMIT_MAT_046】打开详情后返回 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 记录首行 `${sampleProductName}` + 2. 点击该行「详情」 + 3. 断言详情中产品名称、规格型号、注册证号可见,且名称包含 `${sampleProductName}` + 4. 点击「返回」 + 5. 断言回到列表,URL 仍含 `productApply/ZRlist` +- 自动化断言:规划与 CSV `ADMIT-MAT-013` 一致 + +###### 预期结果 + +详情展示该行产品主数据;返回后仍在物料准入管理列表。 + +--- + +##### 【P1】【TC-UI-ADMIT-MAT-014】【TP_ADMIT_MAT_051】详情关键字段不可直接改写 + +- 前置条件:已打开首行详情 +- 操作步骤: + 1. 断言产品名称等主数据无可用的保存按钮,或输入框为只读 + 2. 返回列表,断言首行名称仍为 `${sampleProductName}` +- 自动化断言:规划与 CSV `ADMIT-MAT-014` 一致 + +###### 预期结果 + +详情态不能把产品名称改写入库。 + +--- + +##### 【P0】【TC-UI-ADMIT-MAT-015】【TP_ADMIT_MAT_047】待申请行编辑回填 + +- 前置条件:待申请 Tab 至少 1 行含「编辑」 +- 操作步骤: + 1. 在待申请列表读取首行 `${sampleProductName}`、`${sampleSpec}` + 2. 点击「编辑」 + 3. 断言产品名称、规格型号与该行一致 + 4. 返回且不保存 +- 自动化断言:规划与 CSV `ADMIT-MAT-015` 一致 + +###### 预期结果 + +编辑页回填与列表行一致。无「编辑」则 skip。 + +--- + +##### 【P1】【TC-UI-ADMIT-MAT-016】【TP_ADMIT_MAT_049】编辑取消不保存 + +- 前置条件:存在可编辑行 +- 操作步骤: + 1. 打开编辑,记录原产品名称 + 2. 将产品名称改为 `${uniqProductName}`(建议 `AUTO-MAT-${timestamp}`)后返回或取消,不保存 + 3. 再次打开编辑,断言名称仍为原值 +- 自动化断言:规划与 CSV `ADMIT-MAT-016` 一致 + +###### 预期结果 + +取消后列表与回看均为修改前名称。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-ADMIT-MAT-017】【TP_ADMIT_MAT_052】编辑清空产品名称拦截(mock反例) + +- 前置条件:存在可编辑行 +- 操作步骤: + 1. 打开编辑,将产品名称清空为 mock 空值 + 2. 点击保存 + 3. 断言必填提示,页面不关闭 + 4. 取消后再次打开,断言名称仍为原值 +- 自动化断言:规划与 CSV `ADMIT-MAT-017` 一致 + +###### 预期结果 + +保存被拦截;原记录不被写成空。 + +--- + +##### 【P1】【TC-UI-ADMIT-MAT-018】【TP_ADMIT_MAT_053】已生效行不提供编辑 + +- 前置条件:已生效 Tab 计数大于 0 +- 操作步骤: + 1. 切换到「已生效」 + 2. 断言首行操作无「编辑」,或点击后保存被拒绝 +- 自动化断言:规划与 CSV `ADMIT-MAT-018` 一致 + +###### 预期结果 + +已生效行不能改写成新的产品名称。计数为 0 则 skip。 + +--- + +### 物料准入管理 · 关联与作废 + +#### 功能验证点 + +##### 【P0】【TC-UI-ADMIT-MAT-019】【TP_ADMIT_MAT_035】关联产品取消不落库 + +- 前置条件:已进入物料准入管理页 +- 操作步骤: + 1. 记录当前分页总数 `${sampleTotal}` 与首行默认进院供应商 + 2. 点击「关联产品」 + 3. 断言关联层打开 + 4. 取消或关闭,不确认 + 5. 断言总数不变,首行供应商不变 +- 自动化断言:规划与 CSV `ADMIT-MAT-019` 一致 + +###### 预期结果 + +取消关联不产生申请单,也不改默认进院供应商。 + +--- + +##### 【P0】【TC-UI-ADMIT-MAT-020】【TP_ADMIT_MAT_040】作废确认后取消 + +- 前置条件:待申请至少 1 条可勾选 +- 操作步骤: + 1. 勾选 1 行,记录其产品名称与当前状态 + 2. 点击「作 废」 + 3. 断言出现确认框,含取消与确定 + 4. 点击「取 消」 + 5. 断言该行仍在,状态未变为已作废 +- 自动化断言:规划与 CSV `ADMIT-MAT-020` 一致 + +###### 预期结果 + +作废需确认;取消后记录仍在。本条不点击确定,避免作废生产数据。 + +--- + +##### 【P1】【TC-UI-ADMIT-MAT-021】【TP_ADMIT_MAT_050】打开操作日志后关闭 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 点击首行「日志」 + 2. 断言出现操作日志层 + 3. 关闭后断言仍在列表页 +- 自动化断言:规划与 CSV `ADMIT-MAT-021` 一致 + +###### 预期结果 + +日志层可打开可关闭;列表数据不变。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **21** 条(P0=12 / P1=9) | +| 来源 | 从 54 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 未登录/权限/会话过期、查询接口失败注入、一键生成与确认生成申请单、确认作废、确认关联落库、跨列表追溯 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..6250c03 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,324 @@ +# 基础管理-医院资料-公告管理 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-医院资料-公告管理-测试点分析-01.md` +**执行 CSV**:待生成 `testcase/基础管理-医院资料-公告管理-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/hospital/paltform/notice` +**定位器 YAML**:`locators/spd/basic-notice.locator.yaml` +**元素扫描**:`data/scan/basic-notice-elements.json`(82 项) +**版本**:2026-09-16 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/basic-notice.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号(凭据见 `.env`);具备「基础设置 → 医院资料 → 公告管理」权限。 +**页面约定**:整页渲染,**无报表 iframe**;定位均在 `page` 上。 +**菜单导航**:先展开「基础设置 → 医院资料」,再 `page.getByRole('menuitem', { name: '公告管理', exact: true })`。 +**写操作用例**:标题使用 `${uniqNoticeTitle}`(建议 `AUTO-NTC-${timestamp}`),用例结束后删除或恢复,禁止写死生产公告标题。 + +**动态取数约定**: +1. 打开页面后默认「查询」,从首行读取 `${sampleNoticeTitle}`、`${samplePublishScope}`、`${samplePublishDate}`。 +2. 筛选条件使用样本占位,**禁止写死**业务标题/日期。 +3. 发布范围现网选项:全部 / 全院 / 中心库 / 进院供应商。 + +--- + +## 基础设置 · 医院资料 + +### 公告管理 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-NTC-001】【TP_BASIC_NTC_001】菜单进入公告管理页 + +- 前置条件:已登录且具备医院资料权限 +- 操作步骤: + 1. 按菜单「基础设置 → 医院资料 → 公告管理」进入 + 2. 断言 URL 含 `/spd/basic/hospital/paltform/notice` + 3. 断言筛选区、新增按钮、列表表头可见 +- 自动化断言:规划与 CSV `BASIC-NTC-001` 及 `tests/spd/basic-notice.spec.ts` 一致 + +###### 预期结果 + +进入公告管理页;不误入医院信息登记/院区管理/财务月份设置;筛选区与列表可见。 + +--- + +### 公告管理 · 筛选查询 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-NTC-002】【TP_BASIC_NTC_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入公告管理页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `BASIC-NTC-002` 一致 + +###### 预期结果 + +查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-BASIC-NTC-003】【TP_BASIC_NTC_010】公告标题模糊查询 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleNoticeTitle}` + 2. 填入筛选「公告标题」,点击「查 询」 + 3. 断言结果行「公告标题」均包含该关键字(或与产品模糊规则一致) +- 自动化断言:规划与 CSV `BASIC-NTC-003` 一致 + +###### 预期结果 + +查询结果标题与已填关键字匹配;无未捕获异常。 + +--- + +##### 【P0】【TC-UI-BASIC-NTC-004】【TP_BASIC_NTC_012】发布范围下拉查询 + +- 前置条件:已进入公告管理页 +- 操作步骤: + 1. 打开发布范围下拉,选择与首行 `${samplePublishScope}` 相同的选项(勿写死) + 2. 点击「查 询」 + 3. 断言结果行「发布范围」列与所选值一致(选「全部」时不额外收窄) +- 自动化断言:规划与 CSV `BASIC-NTC-004` 一致 + +###### 预期结果 + +下拉选项含全部/全院/中心库/进院供应商;查询结果范围列与条件一致。 + +--- + +##### 【P0】【TC-UI-BASIC-NTC-005】【TP_BASIC_NTC_013】标题与范围组合查询 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 读取首行 `${sampleNoticeTitle}`、`${samplePublishScope}` + 2. 填入标题与范围(发布日期保持页面默认,勿 fallback 单日) + 3. 点击「查 询」 + 4. 断言结果同时满足两个已填条件 +- 自动化断言:规划与 CSV `BASIC-NTC-005` 一致 + +###### 预期结果 + +组合查询无报错;结果同时满足标题与发布范围。 + +--- + +##### 【P1】【TC-UI-BASIC-NTC-006】【TP_BASIC_NTC_014】快捷区重置恢复默认 + +- 前置条件:已进入公告管理页 +- 操作步骤: + 1. 填入 `${sampleNoticeTitle}` 等筛选条件 + 2. 点击「重 置」 + 3. 断言标题/范围/日期恢复默认 +- 自动化断言:规划与 CSV `BASIC-NTC-006` 一致 + +###### 预期结果 + +筛选字段恢复默认;列表按默认规则刷新。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-BASIC-NTC-007】【TP_BASIC_NTC_020】无匹配结果空态 + +- 前置条件:已进入公告管理页 +- 操作步骤: + 1. 公告标题填入 `__NOT_EXIST_NOTICE__99999` + 2. 点击「查 询」 + 3. 断言空态或暂无数据,分页总数为 0 +- 自动化断言:规划与 CSV `BASIC-NTC-007` 一致 + +###### 预期结果 + +无匹配时不报错;空态明确;总数为 0。 + +--- + +### 公告管理 · 列表分页 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-NTC-008】【TP_BASIC_NTC_022】查询后核心列展示 + +- 前置条件:已进入公告管理页 +- 操作步骤: + 1. 点击「查 询」 + 2. 断言表头含公告编号、公告标题、发布范围、发布科室、发布人、操作 +- 自动化断言:规划与 CSV `BASIC-NTC-008` 一致 + +###### 预期结果 + +核心列完整;操作列可见「编辑」「删除」。 + +--- + +##### 【P1】【TC-UI-BASIC-NTC-009】【TP_BASIC_NTC_025】分页翻至第 2 页 + +- 前置条件:默认查询总条数大于一页 +- 操作步骤: + 1. 记录第 1 页首行公告编号 + 2. 点击页码「2」或「下一页」 + 3. 断言页码高亮为 2,首行编号与第 1 页不同 +- 自动化断言:规划与 CSV `BASIC-NTC-009` 一致;总条数不足一页则 skip + +###### 预期结果 + +翻页成功;数据更换;筛选条件保持。 + +--- + +##### 【P1】【TC-UI-BASIC-NTC-010】【TP_BASIC_NTC_027】跳至指定页 + +- 前置条件:总页数≥2 +- 操作步骤: + 1. 在分页「跳至」输入 `2` 并回车 + 2. 断言当前页为 2 +- 自动化断言:规划与 CSV `BASIC-NTC-010` 一致;无 jumper 或仅 1 页则 skip + +###### 预期结果 + +跳转至指定页;列表刷新为该页数据。 + +--- + +### 公告管理 · 新增公告 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-NTC-011】【TP_BASIC_NTC_033】打开新增公告弹窗 + +- 前置条件:已进入公告管理页 +- 操作步骤: + 1. 点击「新 增」 + 2. 断言弹窗标题为「新增公告」 + 3. 断言可见标题、发布范围、置顶、截止日期、富文本、取消、发布 +- 自动化断言:规划与 CSV `BASIC-NTC-011` 一致 + +###### 预期结果 + +新增弹窗可见;必填/选填控件齐全;富文本容器 `.ql-editor` 可见。 + +--- + +##### 【P1】【TC-UI-BASIC-NTC-012】【TP_BASIC_NTC_039】新增取消不落库 + +- 前置条件:已进入公告管理页 +- 操作步骤: + 1. 打开新增,标题填 `${uniqNoticeTitle}`,内容填简短正文 + 2. 点击「取 消」 + 3. 按标题查询,断言无该记录 +- 自动化断言:规划与 CSV `BASIC-NTC-012` 一致 + +###### 预期结果 + +弹窗关闭;列表不出现 `${uniqNoticeTitle}`。 + +--- + +##### 【P0】【TC-UI-BASIC-NTC-013】【TP_BASIC_NTC_034】合法必填发布成功 + +- 前置条件:已进入公告管理页;允许自动化写公告 +- 操作步骤: + 1. 打开新增,标题 `${uniqNoticeTitle}`,发布范围选「中心库」,内容写入 `.ql-editor` + 2. 点击「发 布」 + 3. 按标题查询,断言出现该行且发布范围为中心库 + 4. 后置:删除该测试公告 +- 自动化断言:规划与 CSV `BASIC-NTC-013` 一致;环境禁止写则 skip + +###### 预期结果 + +弹窗关闭;列表出现新记录;字段与提交一致。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-BASIC-NTC-014】【TP_BASIC_NTC_043】标题为空发布拦截 + +- 前置条件:已进入公告管理页 +- 操作步骤: + 1. 打开新增,标题留空,填写内容后点「发 布」 + 2. 断言必填提示出现,弹窗仍打开 +- 自动化断言:规划与 CSV `BASIC-NTC-014` 一致 + +###### 预期结果 + +发布被拦截;不落库;弹窗不关闭。 + +--- + +##### 【P0】【TC-UI-BASIC-NTC-015】【TP_BASIC_NTC_044】内容为空发布拦截 + +- 前置条件:已进入公告管理页 +- 操作步骤: + 1. 打开新增,填写标题,富文本留空,点「发 布」 + 2. 断言内容必填提示,弹窗仍打开 +- 自动化断言:规划与 CSV `BASIC-NTC-015` 一致 + +###### 预期结果 + +发布被拦截;不落库。 + +--- + +### 公告管理 · 编辑与删除 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-NTC-016】【TP_BASIC_NTC_050】打开编辑公告并回填 + +- 前置条件:列表至少 1 条数据 +- 操作步骤: + 1. 点击首行「编辑」 + 2. 断言弹窗标题为「编辑公告」 + 3. 断言标题输入框值与该行 `${sampleNoticeTitle}` 一致(允许规范化空白) +- 自动化断言:规划与 CSV `BASIC-NTC-016` 一致 + +###### 预期结果 + +编辑弹窗可见;标题/范围/内容回填正确。 + +--- + +##### 【P1】【TC-UI-BASIC-NTC-017】【TP_BASIC_NTC_052】编辑取消不保存 + +- 前置条件:列表至少 1 条数据 +- 操作步骤: + 1. 打开首行编辑,记录原标题 + 2. 将标题改为 `${uniqNoticeTitle}` 后点「取 消」 + 3. 再次打开编辑,断言标题仍为原值 +- 自动化断言:规划与 CSV `BASIC-NTC-017` 一致 + +###### 预期结果 + +取消后列表与回看均为修改前标题。 + +--- + +##### 【P0】【TC-UI-BASIC-NTC-018】【TP_BASIC_NTC_057】删除弹出确认框 + +- 前置条件:列表至少 1 条数据 +- 操作步骤: + 1. 点击首行「删除」 + 2. 断言出现确认框(标题含「提示」),可见「取 消」「确 定」 + 3. 点击「取 消」 + 4. 断言该行仍在 +- 自动化断言:规划与 CSV `BASIC-NTC-018` 一致 + +###### 预期结果 + +删除需二次确认;取消后记录仍在。本条不断言真实删除,避免污染数据。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **18** 条(P0=10 / P1=8) | +| 来源 | 从 63 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 未登录/权限(由 auth.setup 覆盖)、发布后跨端可见、删除真实落库、接口失败注入 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..4886377 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,202 @@ +# 基础管理-医院资料-医院信息登记 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-医院资料-医院信息登记-测试点分析-01.md` +**执行 CSV**:待生成 `testcase/基础管理-医院资料-医院信息登记-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/hospital/paltform/info` +**定位器 YAML**:`locators/spd/basic-hospital-info.locator.yaml` +**元素扫描**:`data/scan/basic-hospital-info-elements.json`(38 项) +**版本**:2026-09-16 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/basic-hospital-info.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 医院资料 → 医院信息登记」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**菜单导航**:`page.getByRole('menuitem', { name: '医院信息登记', exact: true })`。 +**写操作**:保存前读取原值 `${sampleLinkman}` / `${sampleContactWay}` / `${sampleShortName}`,用例结束必须恢复,禁止写死并留下脏数据。 + +--- + +## 基础设置 · 医院资料 + +### 医院信息登记 · 页面进入与查看 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-HOS-001】【TP_BASIC_HOS_001】菜单进入医院信息登记页 + +- 前置条件:已登录且具备医院资料权限 +- 操作步骤: + 1. 按菜单进入「医院信息登记」 + 2. 断言 URL 含 `/spd/basic/hospital/paltform/info` + 3. 断言可见「编 辑」及医院编码/名称/地址等详情标签 +- 自动化断言:规划与 CSV `BASIC-HOS-001` 及 `tests/spd/basic-hospital-info.spec.ts` 一致 + +###### 预期结果 + +进入详情页;不误入公告/院区/财务月份页;查看态「编辑」可见。 + +--- + +##### 【P0】【TC-UI-BASIC-HOS-002】【TP_BASIC_HOS_009】查看态只读字段不可改 + +- 前置条件:已进入医院信息登记且未点编辑 +- 操作步骤: + 1. 断言医院编码、医院名称、地址区域无可用输入框 + 2. 断言「保 存」「取 消」不可见 +- 自动化断言:规划与 CSV `BASIC-HOS-002` 一致 + +###### 预期结果 + +查看模式下编码/名称/地址只读;不出现保存按钮。 + +--- + +##### 【P1】【TC-UI-BASIC-HOS-003】【TP_BASIC_HOS_011】已上传图片可预览 + +- 前置条件:当前医院已有登录页背景或 logo +- 操作步骤: + 1. 点击「预览文件」(背景或 logo 任一可见项) + 2. 断言预览打开或新预览层可见 +- 自动化断言:规划与 CSV `BASIC-HOS-003` 一致;均无预览入口则 skip + +###### 预期结果 + +预览可打开;不报错。 + +--- + +### 医院信息登记 · 编辑保存 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-HOS-004】【TP_BASIC_HOS_015】进入编辑模式 + +- 前置条件:已进入医院信息登记查看态 +- 操作步骤: + 1. 点击「编 辑」 + 2. 断言「保 存」「取 消」可见 + 3. 断言 `#form_item_linkman`、`#form_item_contactWay`、`#form_item_anotherName` 可编辑 +- 自动化断言:规划与 CSV `BASIC-HOS-004` 一致 + +###### 预期结果 + +进入编辑态;三个可改文本框可输入;编码/名称/地址仍不可改。 + +--- + +##### 【P0】【TC-UI-BASIC-HOS-005】【TP_BASIC_HOS_016】编辑态只读字段仍不可改 + +- 前置条件:已进入编辑模式 +- 操作步骤: + 1. 点击「编 辑」 + 2. 断言医院编码、医院名称、地址仍无可用输入框 +- 自动化断言:规划与 CSV `BASIC-HOS-005` 一致 + +###### 预期结果 + +编辑模式下系统分配字段保持只读。 + +--- + +##### 【P0】【TC-UI-BASIC-HOS-006】【TP_BASIC_HOS_018】取消不保存 + +- 前置条件:已进入查看态 +- 操作步骤: + 1. 点击「编 辑」,读取原联系人 `${sampleLinkman}` + 2. 将联系人改为 `${uniqLinkman}` 后点「取 消」 + 3. 断言回到查看态,联系人展示仍为 `${sampleLinkman}` +- 自动化断言:规划与 CSV `BASIC-HOS-006` 一致 + +###### 预期结果 + +取消后不落库;查看态为修改前值。 + +--- + +##### 【P0】【TC-UI-BASIC-HOS-007】【TP_BASIC_HOS_017】保存后字段持久化 + +- 前置条件:已进入查看态;允许写医院资料 +- 操作步骤: + 1. 记录原联系人 `${sampleLinkman}` + 2. 编辑为 `${uniqLinkman}` 并「保 存」 + 3. 刷新或重新打开本页,断言联系人为 `${uniqLinkman}` + 4. 后置:再编辑回 `${sampleLinkman}` 并保存 +- 自动化断言:规划与 CSV `BASIC-HOS-007` 一致;环境禁止写则 skip + +###### 预期结果 + +保存后刷新仍为新值;用例结束恢复原值。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-BASIC-HOS-008】【TP_BASIC_HOS_022】联系人为空保存拦截 + +- 前置条件:已进入查看态 +- 操作步骤: + 1. 点击「编 辑」,清空联系人 + 2. 点击「保 存」 + 3. 断言必填提示,仍停留编辑态 +- 自动化断言:规划与 CSV `BASIC-HOS-008` 一致 + +###### 预期结果 + +保存被拦截;原联系人不被写成空。 + +--- + +##### 【P0】【TC-UI-BASIC-HOS-009】【TP_BASIC_HOS_023】联系方式为空保存拦截 + +- 前置条件:已进入查看态 +- 操作步骤: + 1. 编辑态清空联系方式后点「保 存」 + 2. 断言必填提示,编辑态保持 +- 自动化断言:规划与 CSV `BASIC-HOS-009` 一致 + +###### 预期结果 + +保存被拦截;联系方式不被清空落库。 + +--- + +##### 【P0】【TC-UI-BASIC-HOS-010】【TP_BASIC_HOS_024】医院简称为空保存拦截 + +- 前置条件:已进入查看态 +- 操作步骤: + 1. 编辑态清空医院简称后点「保 存」 + 2. 断言必填提示 +- 自动化断言:规划与 CSV `BASIC-HOS-010` 一致 + +###### 预期结果 + +保存被拦截;简称不被写成空。 + +--- + +### 医院信息登记 · 图片上传 + +#### 功能验证点 + +##### 【P1】【TC-UI-BASIC-HOS-011】【TP_BASIC_HOS_030】编辑态存在背景与 logo 上传控件 + +- 前置条件:已进入查看态 +- 操作步骤: + 1. 点击「编 辑」 + 2. 断言存在登录页背景、医院 logo 对应 `input[type=file]`(`loginBgUploadInput` / `logoUploadInput`) + 3. 点「取 消」,不实际上传 +- 自动化断言:规划与 CSV `BASIC-HOS-011` 一致 + +###### 预期结果 + +编辑态两个上传控件可定位;本条不强制替换线上图片。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **11** 条(P0=9 / P1=2) | +| 来源 | 从 38 条测试点选取可稳定自动化项 | +| 未纳入自动化 | 真实换登录页背景/logo(易污染环境)、登录页视觉生效、超限文件二进制、未登录 | +| 数据隔离 | 保存用例必须恢复原联系人 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..3e811d6 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,261 @@ +# 基础管理-医院资料-财务月份设置 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-医院资料-财务月份设置-测试点分析-01.md` +**执行 CSV**:待生成 `testcase/基础管理-医院资料-财务月份设置-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/hospital/balance/month` +**定位器 YAML**:`locators/spd/basic-finance-month.locator.yaml` +**元素扫描**:`data/scan/basic-finance-month-elements.json`(43 项) +**版本**:2026-09-16 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/basic-finance-month.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 医院资料 → 财务月份设置」权限。 +**页面约定**:整页渲染,**无报表 iframe**;本页分页无跳至输入。 +**菜单导航**:`page.getByRole('menuitem', { name: '财务月份设置', exact: true })`。 +**写操作**:仅允许改「当前月份」行的自动结算日期。保存前读取 `${sampleAutoSettleDate}`,用例结束必须恢复,禁止写死日期并留下脏数据。 + +**动态取数约定**: +1. 默认查询后,从「当前月份」行读取 `${sampleFinanceMonth}`、`${sampleStartDate}`、`${sampleEndDate}`、`${sampleAutoSettleDate}`。 +2. 筛选条件使用样本占位,**禁止写死**财务月份。 +3. 「修改」只出现在当前月份行;历史月份无修改入口。 + +--- + +## 基础设置 · 医院资料 + +### 财务月份设置 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-FMN-001】【TP_BASIC_FMN_001】菜单进入财务月份设置页 + +- 前置条件:已登录且具备医院资料权限 +- 操作步骤: + 1. 按菜单「基础设置 → 医院资料 → 财务月份设置」进入 + 2. 断言 URL 含 `/spd/basic/hospital/balance/month` + 3. 断言筛选区(`#form_item_settleMonthDate`)、查询/重置、列表列头可见 +- 自动化断言:规划与 CSV `BASIC-FMN-001` 及 `tests/spd/basic-finance-month.spec.ts` 一致 + +###### 预期结果 + +进入财务月份设置页;不误入公告/医院信息/院区页;筛选与列表可见。 + +--- + +### 财务月份设置 · 筛选查询 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-FMN-002】【TP_BASIC_FMN_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入财务月份设置页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `BASIC-FMN-002` 一致 + +###### 预期结果 + +查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-BASIC-FMN-003】【TP_BASIC_FMN_010】按财务月份筛选 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 从首行或当前月份行读取 `${sampleFinanceMonth}` + 2. 在 `#form_item_settleMonthDate` 选择该月份,点击「查 询」 + 3. 断言结果行「财务月份」均等于该月份(或与产品筛选规则一致) +- 自动化断言:规划与 CSV `BASIC-FMN-003` 一致 + +###### 预期结果 + +结果仅含所选财务月份;无未捕获异常。 + +--- + +##### 【P1】【TC-UI-BASIC-FMN-004】【TP_BASIC_FMN_011】重置恢复默认 + +- 前置条件:已按财务月份筛选 +- 操作步骤: + 1. 选择 `${sampleFinanceMonth}` 并查询 + 2. 点击「重 置」 + 3. 断言月份筛选恢复默认,列表按默认规则刷新 +- 自动化断言:规划与 CSV `BASIC-FMN-004` 一致 + +###### 预期结果 + +重置后筛选清空或回到默认;列表可再查询。 + +--- + +### 财务月份设置 · 列表与状态 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-FMN-005】【TP_BASIC_FMN_017】列表核心列完整 + +- 前置条件:默认查询至少 1 条数据 +- 操作步骤: + 1. 断言表头包含财务月份、开始日期、结束日期、自动结算日期、状态、操作 +- 自动化断言:规划与 CSV `BASIC-FMN-005` 一致 + +###### 预期结果 + +核心列均可见,不缺「状态」「操作」。 + +--- + +##### 【P0】【TC-UI-BASIC-FMN-006】【TP_BASIC_FMN_018】仅当前月份行可修改 + +- 前置条件:列表同时存在当前月份与历史月份(若仅一行当前月份则只断言该行) +- 操作步骤: + 1. 定位状态为「当前月份」的行,断言操作列可见「修 改」(`modifyBtn`) + 2. 对「历史月份」行断言无「修 改」或操作为「--」 +- 自动化断言:规划与 CSV `BASIC-FMN-006` 一致;无历史月份则只校验当前月份入口 + +###### 预期结果 + +可修改入口只出现在当前月份行。 + +--- + +##### 【P1】【TC-UI-BASIC-FMN-007】【TP_BASIC_FMN_019】当前月份至多一行 + +- 前置条件:默认查询已出列表 +- 操作步骤: + 1. 统计状态为「当前月份」的行数 + 2. 断言行数 ≤ 1 +- 自动化断言:规划与 CSV `BASIC-FMN-007` 一致 + +###### 预期结果 + +同一时点至多一行「当前月份」。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-BASIC-FMN-008】【TP_BASIC_FMN_022】历史月份无法进入编辑 + +- 前置条件:列表至少 1 条历史月份 +- 操作步骤: + 1. 对历史月份行尝试点击操作区 + 2. 断言不出现行内「保 存」「取 消」,自动结算日期仍为文本 +- 自动化断言:规划与 CSV `BASIC-FMN-008` 一致;无历史月份则 skip + +###### 预期结果 + +历史月份不能改开始/结束/自动结算日期。 + +--- + +### 财务月份设置 · 当前月份修改 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-FMN-009】【TP_BASIC_FMN_024】修改进入行内编辑 + +- 前置条件:存在状态为「当前月份」的行 +- 操作步骤: + 1. 点击该行「修 改」 + 2. 断言出现「保 存」「取 消」 + 3. 断言自动结算日期变为日期框(`autoSettleDateInput`) +- 自动化断言:规划与 CSV `BASIC-FMN-009` 一致 + +###### 预期结果 + +进入行内编辑;仅自动结算日可改。 + +--- + +##### 【P0】【TC-UI-BASIC-FMN-010】【TP_BASIC_FMN_025】编辑态开始结束日期只读 + +- 前置条件:已进入当前月份行内编辑 +- 操作步骤: + 1. 点击「修 改」 + 2. 断言开始日期、结束日期无可用输入框 + 3. 断言仅 `autoSettleDateInput` 可编辑 + 4. 点击「取 消」退出 +- 自动化断言:规划与 CSV `BASIC-FMN-010` 一致 + +###### 预期结果 + +开始/结束日期保持只读;账期区间不被改写。 + +--- + +##### 【P0】【TC-UI-BASIC-FMN-011】【TP_BASIC_FMN_027】取消不保存自动结算日 + +- 前置条件:存在当前月份行 +- 操作步骤: + 1. 读取 `${sampleAutoSettleDate}` + 2. 点「修 改」,将自动结算日改为当月区间内另一天 `${uniqAutoSettleDate}` + 3. 点击「取 消」 + 4. 断言该行自动结算日仍为 `${sampleAutoSettleDate}` +- 自动化断言:规划与 CSV `BASIC-FMN-011` 一致 + +###### 预期结果 + +取消后不落库;日期恢复修改前值。 + +--- + +##### 【P0】【TC-UI-BASIC-FMN-012】【TP_BASIC_FMN_026】保存自动结算日期持久化 + +- 前置条件:存在当前月份行;允许写财务月份 +- 操作步骤: + 1. 记录 `${sampleAutoSettleDate}`、`${sampleStartDate}`、`${sampleEndDate}` + 2. 将自动结算日改为区间内另一天 `${uniqAutoSettleDate}` 并「保 存」 + 3. 刷新或重新查询,断言该行自动结算日为 `${uniqAutoSettleDate}` + 4. 后置:再改为 `${sampleAutoSettleDate}` 并保存 +- 自动化断言:规划与 CSV `BASIC-FMN-012` 一致;环境禁止写则 skip + +###### 预期结果 + +保存后刷新仍为新日期;用例结束恢复原值。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-BASIC-FMN-013】【TP_BASIC_FMN_031】自动结算日为空拦截 + +- 前置条件:存在当前月份行 +- 操作步骤: + 1. 点「修 改」,清空 `autoSettleDateInput` + 2. 点击「保 存」 + 3. 断言必填或非法提示,行上日期仍为 `${sampleAutoSettleDate}` +- 自动化断言:规划与 CSV `BASIC-FMN-013` 一致 + +###### 预期结果 + +保存被拦截;原日期不被写成空。 + +--- + +##### 【P1】【TC-UI-BASIC-FMN-014】【TP_BASIC_FMN_032】自动结算日超出当月区间拦截 + +- 前置条件:存在当前月份行且能读到开始/结束日 +- 操作步骤: + 1. 点「修 改」,将日期改为早于 `${sampleStartDate}` 或晚于 `${sampleEndDate}` + 2. 点击「保 存」 + 3. 断言拒绝保存并提示,原日期不变 + 4. 点「取 消」退出 +- 自动化断言:规划与 CSV `BASIC-FMN-014` 一致;日期控件无法选出区间外日期则 skip 并记录产品限制 + +###### 预期结果 + +超出账期区间时拒绝保存;不脏写。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **14** 条(P0=10 / P1=4) | +| 来源 | 从 38 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 未登录/权限、自动结算任务调度联调、查询接口失败注入、最早/最晚月份边界 | +| 数据隔离 | 保存用例必须恢复原自动结算日 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..13c85e9 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,323 @@ +# 基础管理-医院资料-院区管理 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-医院资料-院区管理-测试点分析-01.md` +**执行 CSV**:待生成 `testcase/基础管理-医院资料-院区管理-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/hospital/paltform/area` +**定位器 YAML**:`locators/spd/basic-branch.locator.yaml` +**元素扫描**:`data/scan/basic-branch-elements.json`(60 项) +**版本**:2026-09-16 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/basic-branch.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 医院资料 → 院区管理」权限。 +**页面约定**:整页渲染,**无报表 iframe**;院区名称框即时或回车过滤,**无独立「查询」「重置」按钮**。 +**菜单导航**:`page.getByRole('menuitem', { name: '院区管理', exact: true })`。 +**写操作**:院区名称使用 `${uniqCampusName}`(建议 `AUTO-BRN-${timestamp}`),用例结束后停用/恢复或按环境约定清理,禁止写死生产院区名称。 + +**动态取数约定**: +1. 打开页面后从首行读取 `${sampleCampusName}`、`${sampleCampusId}`、`${sampleCampusAddress}`、`${sampleCampusStatus}`。 +2. 搜索关键字使用样本占位,**禁止写死**院区名称。 +3. 新增弹窗无院区编号、无启用云仓;编辑弹窗额外有 `#form_item_id` 与启用云仓;新增状态文案为停用/启用,编辑为禁用/启用。 + +--- + +## 基础设置 · 医院资料 + +### 院区管理 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-BRN-001】【TP_BASIC_BRN_001】菜单进入院区管理页 + +- 前置条件:已登录且具备医院资料权限 +- 操作步骤: + 1. 按菜单「基础设置 → 医院资料 → 院区管理」进入 + 2. 断言 URL 含 `/spd/basic/hospital/paltform/area` + 3. 断言「新 增」、院区名称搜索框(`campusNameInput`)、列表列头可见 + 4. 断言页面无独立「查 询」「重 置」按钮 +- 自动化断言:规划与 CSV `BASIC-BRN-001` 及 `tests/spd/basic-branch.spec.ts` 一致 + +###### 预期结果 + +进入院区管理页;不误入公告/医院信息/财务月份页;搜索框与新增可见,无查询/重置。 + +--- + +### 院区管理 · 列表搜索 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-BRN-002】【TP_BASIC_BRN_009】默认列出院区或空态明确 + +- 前置条件:已进入院区管理页 +- 操作步骤: + 1. 等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `BASIC-BRN-002` 一致 + +###### 预期结果 + +加载无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-BASIC-BRN-003】【TP_BASIC_BRN_010】院区名称搜索过滤 + +- 前置条件:默认列表至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleCampusName}` + 2. 在 `campusNameInput` 填入该关键字(即时过滤或回车,以现网为准) + 3. 断言结果行「院区名称」均包含该关键字 +- 自动化断言:规划与 CSV `BASIC-BRN-003` 一致 + +###### 预期结果 + +过滤结果名称与已填关键字匹配;无未捕获异常。 + +--- + +##### 【P0】【TC-UI-BASIC-BRN-004】【TP_BASIC_BRN_011】列表核心列完整 + +- 前置条件:默认列表至少 1 条数据 +- 操作步骤: + 1. 断言表头包含序号、院区编号、院区名称、院区地址、状态、启用云仓、操作 +- 自动化断言:规划与 CSV `BASIC-BRN-004` 一致 + +###### 预期结果 + +核心列均可见,操作列含「编 辑」。 + +--- + +##### 【P1】【TC-UI-BASIC-BRN-005】【TP_BASIC_BRN_015】清空搜索恢复全量 + +- 前置条件:已用 `${sampleCampusName}` 过滤 +- 操作步骤: + 1. 记录过滤后条数 + 2. 清空 `campusNameInput` 并等待列表刷新 + 3. 断言条数回到未过滤的默认集合(或不少于过滤后条数) +- 自动化断言:规划与 CSV `BASIC-BRN-005` 一致 + +###### 预期结果 + +清空后列表恢复默认集合。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-BASIC-BRN-006】【TP_BASIC_BRN_018】搜索无匹配空态 + +- 前置条件:已进入院区管理页 +- 操作步骤: + 1. 在院区名称框输入不存在关键字 `${uniqNoMatchCampus}`(建议 `NO-MATCH-BRN-${timestamp}`) + 2. 断言「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `BASIC-BRN-006` 一致 + +###### 预期结果 + +无匹配时空态明确,不白屏。 + +--- + +### 院区管理 · 新增院区 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-BRN-007】【TP_BASIC_BRN_020】打开新增院区弹窗 + +- 前置条件:已进入院区管理页 +- 操作步骤: + 1. 点击「新 增」 + 2. 断言弹窗标题为「新增院区」 + 3. 断言可见院区名称、所在地区、详细地址、主院区标志、状态、取消/确定 + 4. 断言无 `#form_item_id`、无启用云仓 + 5. 点击「取 消」关闭 +- 自动化断言:规划与 CSV `BASIC-BRN-007` 一致 + +###### 预期结果 + +新增弹窗字段完整;不含院区编号与启用云仓。 + +--- + +##### 【P0】【TC-UI-BASIC-BRN-008】【TP_BASIC_BRN_025】新增取消不落库 + +- 前置条件:已进入院区管理页 +- 操作步骤: + 1. 记录当前分页总数 `${sampleTotal}` + 2. 打开新增,填写名称 `${uniqCampusName}` 后点「取 消」 + 3. 断言列表总数不变,且无该名称行 +- 自动化断言:规划与 CSV `BASIC-BRN-008` 一致 + +###### 预期结果 + +取消后不新增院区。 + +--- + +##### 【P0】【TC-UI-BASIC-BRN-009】【TP_BASIC_BRN_021】合法必填新增成功 + +- 前置条件:允许写院区;名称使用 `${uniqCampusName}` +- 操作步骤: + 1. 打开新增,填写名称、所在地区、详细地址,主院区选「否」,状态选「启用」 + 2. 点击「确 定」 + 3. 断言弹窗关闭,列表出现该名称行且字段一致 + 4. 后置:按环境约定停用或清理该院区,避免污染主数据 +- 自动化断言:规划与 CSV `BASIC-BRN-009` 一致;环境禁止写则 skip + +###### 预期结果 + +新增成功且列表可见;用例结束清理或停用,不留生产脏数据。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-BASIC-BRN-010】【TP_BASIC_BRN_029】院区名称为空拦截 + +- 前置条件:已打开新增弹窗 +- 操作步骤: + 1. 名称留空,补齐地区与地址后点「确 定」 + 2. 断言必填提示,弹窗仍在,列表无新行 +- 自动化断言:规划与 CSV `BASIC-BRN-010` 一致 + +###### 预期结果 + +确定被拦截;不落库。 + +--- + +##### 【P0】【TC-UI-BASIC-BRN-011】【TP_BASIC_BRN_030】所在地区为空拦截 + +- 前置条件:已打开新增弹窗 +- 操作步骤: + 1. 填写名称与地址,不选所在地区,点「确 定」 + 2. 断言必填提示,弹窗仍在 +- 自动化断言:规划与 CSV `BASIC-BRN-011` 一致 + +###### 预期结果 + +确定被拦截;不落库。 + +--- + +##### 【P0】【TC-UI-BASIC-BRN-012】【TP_BASIC_BRN_031】详细地址为空拦截 + +- 前置条件:已打开新增弹窗 +- 操作步骤: + 1. 填写名称并选择地区,地址留空,点「确 定」 + 2. 断言必填提示,弹窗仍在 +- 自动化断言:规划与 CSV `BASIC-BRN-012` 一致 + +###### 预期结果 + +确定被拦截;不落库。 + +--- + +### 院区管理 · 编辑院区 + +#### 功能验证点 + +##### 【P0】【TC-UI-BASIC-BRN-013】【TP_BASIC_BRN_037】打开编辑弹窗并回填 + +- 前置条件:列表至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleCampusId}`、`${sampleCampusName}`、`${sampleCampusAddress}` + 2. 点击「编 辑」 + 3. 断言弹窗标题为「编辑院区」 + 4. 断言 `#form_item_id` 等于 `${sampleCampusId}`,名称/地址回填正确 + 5. 断言额外可见「启用云仓」 + 6. 点击「取 消」 +- 自动化断言:规划与 CSV `BASIC-BRN-013` 一致 + +###### 预期结果 + +编辑弹窗回填正确;相对新增多出院区编号与启用云仓。 + +--- + +##### 【P0】【TC-UI-BASIC-BRN-014】【TP_BASIC_BRN_038】编辑态院区编号不可换号 + +- 前置条件:已打开首行编辑 +- 操作步骤: + 1. 断言 `#form_item_id` 可见且值等于列表「院区编号」 + 2. 若编号只读则直接断言 disabled/readonly;若可输入则改写后点确定,刷新后编号仍为原值 + 3. 未改其它字段时点「取 消」 +- 自动化断言:规划与 CSV `BASIC-BRN-014` 一致 + +###### 预期结果 + +院区编号与列表一致;保存后编号不变。 + +--- + +##### 【P1】【TC-UI-BASIC-BRN-015】【TP_BASIC_BRN_042】编辑取消不保存 + +- 前置条件:列表至少 1 条数据 +- 操作步骤: + 1. 打开首行编辑,记录原名称 + 2. 将名称改为 `${uniqCampusName}` 后点「取 消」 + 3. 再次打开编辑,断言名称仍为原值 +- 自动化断言:规划与 CSV `BASIC-BRN-015` 一致 + +###### 预期结果 + +取消后列表与回看均为修改前名称。 + +--- + +##### 【P1】【TC-UI-BASIC-BRN-016】【TP_BASIC_BRN_040】编辑状态为禁用启用 + +- 前置条件:已打开编辑弹窗 +- 操作步骤: + 1. 断言状态选项文案为「禁用 / 启用」(不是新增弹窗的「停用 / 启用」) + 2. 点「取 消」,不改状态 +- 自动化断言:规划与 CSV `BASIC-BRN-016` 一致 + +###### 预期结果 + +编辑态状态文案与新增不同;本条不真实切换生产院区状态。 + +--- + +##### 【P1】【TC-UI-BASIC-BRN-017】【TP_BASIC_BRN_041】编辑可见启用云仓 + +- 前置条件:已打开编辑弹窗 +- 操作步骤: + 1. 断言「启用云仓」单选「是 / 否」可见 + 2. 点「取 消」,不改云仓标志 +- 自动化断言:规划与 CSV `BASIC-BRN-017` 一致 + +###### 预期结果 + +编辑可看到启用云仓;本条不真实改生产院区云仓开关。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-BASIC-BRN-018】【TP_BASIC_BRN_045】编辑清空必填拦截 + +- 前置条件:列表至少 1 条数据 +- 操作步骤: + 1. 打开首行编辑,清空院区名称后点「确 定」 + 2. 断言必填提示,弹窗仍在 + 3. 点「取 消」后再打开,断言名称仍为原值 +- 自动化断言:规划与 CSV `BASIC-BRN-018` 一致 + +###### 预期结果 + +确定被拦截;原记录不被写成空。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **18** 条(P0=13 / P1=5) | +| 来源 | 从 50 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 未登录/权限、主院区全局唯一、禁用后下游不可选、云仓能力联调、真实改生产院区状态 | +| 数据隔离 | 新增必须用唯一名称并清理/停用;编辑不改生产主数据 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..451d649 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,159 @@ +# 基础管理-厂商管理-供应商节假日管理 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-厂商管理-供应商节假日管理-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-厂商管理-供应商节假日管理-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/mfrs/basic/prov/holiday` +**定位器 YAML**:`locators/spd/vendor-holiday.locator.yaml` +**元素扫描**:`data/scan/vendor-holiday-elements.json`(80 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-010.md` +**版本**:2026-09-30 +**自动化框架**:Playwright + TypeScript(`tests/spd/vendor-holiday.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 厂商管理 → 供应商节假日管理」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**写操作**:左侧新增只打开后取消;不批量删除真实数据。 +**动态取数**:左侧首行读取 `${sampleHolidayName}`/年度;mock:`__NOT_EXIST_ORDER__99999`。 + +--- + +## 基础设置 · 厂商管理 + +### 供应商节假日管理 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-HLD-001】【TP_VENDOR_HLD_001】菜单进入供应商节假日管理页 + +- 前置条件:已登录且具备厂商管理权限 +- 操作步骤: + 1. 按菜单进入供应商节假日管理 + 2. 断言 URL 含 `prov/holiday` + 3. 断言可见节假日主区与新增 +- 自动化断言:与 CSV `VENDOR-HLD-001` 及 `tests/spd/vendor-holiday.spec.ts` 一致 + +###### 预期结果 + +进入供应商节假日管理页。 + +--- + +### 供应商节假日管理 · 左右分栏 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-HLD-002】【TP_VENDOR_HLD_009】选中左侧节假日展示详情 + +- 前置条件:左侧有数据 +- 操作步骤: + 1. 点击左侧首行 + 2. 断言默认假日详情或自定义列表区可见 +- 自动化断言:与 CSV `VENDOR-HLD-002` 及 `tests/spd/vendor-holiday.spec.ts` 一致 + +###### 预期结果 + +详情联动成功。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-VENDOR-HLD-003】【TP_VENDOR_HLD_015】左侧搜索无匹配空态(mock反例) + +- 前置条件:已进入供应商节假日管理页 +- 操作步骤: + 1. 左侧搜索框填 mock 并回车 + 2. 断言左侧列表空态 +- 自动化断言:与 CSV `VENDOR-HLD-003` 及 `tests/spd/vendor-holiday.spec.ts` 一致 + +###### 预期结果 + +无匹配空态明确。 + +--- + +### 供应商节假日管理 · 维护 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-HLD-004】【TP_VENDOR_HLD_011】左侧新增取消 + +- 前置条件:已进入供应商节假日管理页 +- 操作步骤: + 1. 点击新增 + 2. 取消/关闭不保存 + 3. 断言仍停留本页且无异常 +- 自动化断言:与 CSV `VENDOR-HLD-004` 及 `tests/spd/vendor-holiday.spec.ts` 一致 + +###### 预期结果 + +取消不落库。 + +--- + +### 供应商节假日管理 · 左右分栏 + +#### 功能验证点 + +##### 【P1】【TC-UI-VENDOR-HLD-005】【TP_VENDOR_HLD_012】自定义节假日列表区可见 + +- 前置条件:已进入供应商节假日管理页 +- 操作步骤: + 1. 选中左侧行 + 2. 断言「供应商自定义节假日列表」区域可见(可为空态) +- 自动化断言:与 CSV `VENDOR-HLD-005` 及 `tests/spd/vendor-holiday.spec.ts` 一致 + +###### 预期结果 + +自定义区可见。 + +--- + +### 供应商节假日管理 · 维护 + +#### 异常验证点 + +##### 【P0】【TC-UI-VENDOR-HLD-006】【TP_VENDOR_HLD_016】结束早于开始日期拦截 + +- 前置条件:已进入供应商节假日管理页 +- 操作步骤: + 1. 打开新增/编辑,结束日期早于开始日期尝试保存 + 2. 断言拒绝保存并提示 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +非法日期被拦截。 + +--- + +#### 边界验证点 + +##### 【P2】【TC-UI-VENDOR-HLD-007】【TP_VENDOR_HLD_015】开始结束为同一天 + +- 前置条件:已进入供应商节假日管理页 +- 操作步骤: + 1. 打开新增/编辑,开始与结束日期选同一天尝试保存(或仅校验控件) + 2. 断言允许保存或明确提示规则(按产品规则),不报错白屏 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +同日区间规则明确。 + +--- + +### 供应商节假日管理 · 左右分栏 + +#### 集成验证点 + +##### 【P1】【TC-UI-VENDOR-HLD-008】【TP_VENDOR_HLD_018】进院供应商主数据一致 + +- 前置条件:已进入供应商节假日管理页 +- 操作步骤: + 1. 在自定义列表抽样进院供应商(若有数据) + 2. 断言可在厂商/供应商主数据中找到对应记录 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +自定义节假日供应商与主数据一致。 diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..4b489ad --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,210 @@ +# 基础管理-厂商管理-供应商资质管理 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-厂商管理-供应商资质管理-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-厂商管理-供应商资质管理-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/mfrs/basic/qcManage` +**定位器 YAML**:`locators/spd/vendor-qual.locator.yaml` +**元素扫描**:`data/scan/vendor-qual-elements.json`(99 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-002.md` +**版本**:2026-09-30 +**自动化框架**:Playwright + TypeScript(`tests/spd/vendor-qual.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 厂商管理 → 供应商资质管理」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**写操作**:本批只读:查看打开后关闭;不确认导出下载、不改证件。 +**动态取数**:证件效期提醒 Tab 首行读取 `${sampleHospitalSupplier}`;mock:`__NOT_EXIST_SUPPLIER__99999`。 + +--- + +## 基础设置 · 厂商管理 + +### 供应商资质管理 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-QUL-001】【TP_VENDOR_QUL_001】菜单进入供应商资质管理页 + +- 前置条件:已登录且具备厂商管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 厂商管理 → 供应商资质管理」进入 + 2. 断言 URL 含 `/spd/basic/mfrs/basic/qcManage` + 3. 断言可见「查 询」「重 置」及证件效期提醒相关入口 +- 自动化断言:与 CSV `VENDOR-QUL-001` 及 `tests/spd/vendor-qual.spec.ts` 一致 + +###### 预期结果 + +进入供应商资质管理;不误入厂商查询。 + +--- + +### 供应商资质管理 · 筛选与 Tab + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-QUL-002】【TP_VENDOR_QUL_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入供应商资质管理页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行或空态明确 +- 自动化断言:与 CSV `VENDOR-QUL-002` 及 `tests/spd/vendor-qual.spec.ts` 一致 + +###### 预期结果 + +查询无报错;有数据时分页 N≥1。 + +--- + +##### 【P0】【TC-UI-VENDOR-QUL-003】【TP_VENDOR_QUL_009】切换授权链路效期预警 Tab + +- 前置条件:已进入供应商资质管理页 +- 操作步骤: + 1. 点击「授权链路效期预警」 + 2. 断言列表有数据或空态明确 +- 自动化断言:与 CSV `VENDOR-QUL-003` 及 `tests/spd/vendor-qual.spec.ts` 一致 + +###### 预期结果 + +Tab 切换成功,列表字段可随 Tab 变化。 + +--- + +##### 【P0】【TC-UI-VENDOR-QUL-004】【TP_VENDOR_QUL_010】近三个月到期计数一致 + +- 前置条件:已进入供应商资质管理页 +- 操作步骤: + 1. 切换「近三个月到期」(或等价文案) + 2. 若可读到括号/冒号计数,断言等于分页「共 N 条」 +- 自动化断言:与 CSV `VENDOR-QUL-004` 及 `tests/spd/vendor-qual.spec.ts` 一致 + +###### 预期结果 + +计数与分页一致(可读时)。 + +--- + +##### 【P0】【TC-UI-VENDOR-QUL-005】【TP_VENDOR_QUL_011】按进院供应商筛选 + +- 前置条件:证件效期提醒至少 1 条且进院供应商非空 +- 操作步骤: + 1. 从首行读取 `${sampleHospitalSupplier}` 取关键字 + 2. 在进院供应商名称填入关键字并「查 询」 + 3. 断言结果行「进院供应商」包含关键字 +- 自动化断言:与 CSV `VENDOR-QUL-005` 及 `tests/spd/vendor-qual.spec.ts` 一致 + +###### 预期结果 + +进院供应商列与条件一致。 + +--- + +##### 【P1】【TC-UI-VENDOR-QUL-006】【TP_VENDOR_QUL_011】重置恢复默认 + +- 前置条件:已进入供应商资质管理页 +- 操作步骤: + 1. 填写筛选条件后点击「重 置」 + 2. 断言列表按默认规则刷新 +- 自动化断言:与 CSV `VENDOR-QUL-006` 及 `tests/spd/vendor-qual.spec.ts` 一致 + +###### 预期结果 + +筛选恢复默认。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-VENDOR-QUL-007】【TP_VENDOR_QUL_016】无匹配空态(mock反例) + +- 前置条件:已进入供应商资质管理页 +- 操作步骤: + 1. 进院供应商名称填入 `__NOT_EXIST_SUPPLIER__99999` + 2. 点击「查 询」 + 3. 断言空态且分页总数为 0 +- 自动化断言:与 CSV `VENDOR-QUL-007` 及 `tests/spd/vendor-qual.spec.ts` 一致 + +###### 预期结果 + +无匹配时不报错;总数为 0。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-QUL-008】【TP_VENDOR_QUL_012】证件效期提醒核心列完整 + +- 前置条件:已进入供应商资质管理页 +- 操作步骤: + 1. 处于证件效期提醒 Tab + 2. 断言表头含序号、数据来源、进院供应商、有效期至、证件名称、证件类型、效期提醒、操作 +- 自动化断言:与 CSV `VENDOR-QUL-008` 及 `tests/spd/vendor-qual.spec.ts` 一致 + +###### 预期结果 + +核心列可见。 + +--- + +### 供应商资质管理 · 查看与导出 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-QUL-009】【TP_VENDOR_QUL_014】打开查看后关闭 + +- 前置条件:列表至少 1 行含「查看」 +- 操作步骤: + 1. 点击首行「查 看」 + 2. 断言详情层可见 + 3. 关闭/返回,断言回到列表 +- 自动化断言:与 CSV `VENDOR-QUL-009` 及 `tests/spd/vendor-qual.spec.ts` 一致 + +###### 预期结果 + +详情可打开;关闭后回到列表。 + +--- + +##### 【P1】【TC-UI-VENDOR-QUL-010】【TP_VENDOR_QUL_015】导出按钮可见 + +- 前置条件:已进入供应商资质管理页 +- 操作步骤: + 1. 断言「导出」或「导出明细」按钮可见(本批不确认下载) +- 自动化断言:与 CSV `VENDOR-QUL-010` 及 `tests/spd/vendor-qual.spec.ts` 一致 + +###### 预期结果 + +导出入口可见。 + +--- + +### 供应商资质管理 · 筛选与 Tab + +#### 边界验证点 + +##### 【P2】【TC-UI-VENDOR-QUL-011】【TP_VENDOR_QUL_015】制单/效期日期起止同一天 + +- 前置条件:已进入供应商资质管理页 +- 操作步骤: + 1. 若存在日期区间筛选,起止选同一天并查询 + 2. 断言仅返回该日数据或空态明确,不报错 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +同日区间行为明确。 + +--- + +#### 集成验证点 + +##### 【P1】【TC-UI-VENDOR-QUL-012】【TP_VENDOR_QUL_018】效期提醒与有效期至抽样一致 + +- 前置条件:已进入供应商资质管理页 +- 操作步骤: + 1. 在近三个月到期列表抽样一行 + 2. 核对「效期提醒」文案与「有效期至」日期逻辑一致 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +提醒文案与有效期至不矛盾。 diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..f74d1f7 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,195 @@ +# 基础管理-厂商管理-厂商查询 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-厂商管理-厂商查询-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-厂商管理-厂商查询-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/mfrs/paltform/mfrsName/factory` +**定位器 YAML**:`locators/spd/vendor-query.locator.yaml` +**元素扫描**:`data/scan/vendor-query-elements.json`(47 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-001.md` +**版本**:2026-09-30 +**自动化框架**:Playwright + TypeScript(`tests/spd/vendor-query.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 厂商管理 → 厂商查询」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**写操作**:本页只读查询,无新增/编辑提交。 +**动态取数**:从器械注册人 Tab 首行读取 `${sampleMfrsName}`;禁止写死厂商名称;mock:`__NOT_EXIST_MFRS__99999`。 + +--- + +## 基础设置 · 厂商管理 + +### 厂商查询 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-QRY-001】【TP_VENDOR_QRY_001】菜单进入厂商查询页 + +- 前置条件:已登录且具备厂商管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 厂商管理 → 厂商查询」进入 + 2. 断言 URL 含 `/spd/basic/mfrs/paltform/mfrsName/factory` + 3. 断言可见「器械注册人」「经销商」Tab、厂商名称搜索框及列表表头 +- 自动化断言:与 CSV `VENDOR-QRY-001` 及 `tests/spd/vendor-query.spec.ts` 一致 + +###### 预期结果 + +进入厂商查询页;不误入供应商资质管理。 + +--- + +### 厂商查询 · Tab 与搜索 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-QRY-002】【TP_VENDOR_QRY_009】默认器械注册人列表有数据或空态明确 + +- 前置条件:已进入厂商查询页 +- 操作步骤: + 1. 切换或停留「器械注册人」 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:与 CSV `VENDOR-QRY-002` 及 `tests/spd/vendor-query.spec.ts` 一致 + +###### 预期结果 + +器械注册人列表稳定;有数据时分页「共 N 条」N≥1。 + +--- + +##### 【P0】【TC-UI-VENDOR-QRY-003】【TP_VENDOR_QRY_010】切换经销商 Tab + +- 前置条件:已进入厂商查询页 +- 操作步骤: + 1. 点击「经销商」 + 2. 断言 Tab 激活,列表有数据或空态明确 +- 自动化断言:与 CSV `VENDOR-QRY-003` 及 `tests/spd/vendor-query.spec.ts` 一致 + +###### 预期结果 + +经销商列表加载成功;总数可与器械注册人不同。 + +--- + +##### 【P0】【TC-UI-VENDOR-QRY-004】【TP_VENDOR_QRY_011】按厂商名称搜索 + +- 前置条件:器械注册人至少 1 条且厂商名称非空 +- 操作步骤: + 1. 从首行读取 `${sampleMfrsName}` 取关键字 + 2. 在厂商名称搜索框填入关键字并回车 + 3. 断言结果行「厂商名称」包含关键字 +- 自动化断言:与 CSV `VENDOR-QRY-004` 及 `tests/spd/vendor-query.spec.ts` 一致 + +###### 预期结果 + +厂商名称列与关键字匹配。 + +--- + +##### 【P1】【TC-UI-VENDOR-QRY-005】【TP_VENDOR_QRY_013】清空搜索恢复 + +- 前置条件:已按厂商名称过滤 +- 操作步骤: + 1. 在已过滤状态下清空搜索框并回车 + 2. 断言分页总数不少于过滤后 +- 自动化断言:与 CSV `VENDOR-QRY-005` 及 `tests/spd/vendor-query.spec.ts` 一致 + +###### 预期结果 + +恢复更宽结果集。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-VENDOR-QRY-006】【TP_VENDOR_QRY_017】无匹配空态(mock反例) + +- 前置条件:已进入厂商查询页 +- 操作步骤: + 1. 厂商名称填入 `__NOT_EXIST_MFRS__99999` 并回车 + 2. 断言空态且分页总数为 0 +- 自动化断言:与 CSV `VENDOR-QRY-006` 及 `tests/spd/vendor-query.spec.ts` 一致 + +###### 预期结果 + +无匹配时不报错;总数为 0。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-QRY-007】【TP_VENDOR_QRY_012】列表核心列完整 + +- 前置条件:已进入厂商查询页 +- 操作步骤: + 1. 断言表头含序号、厂商编号、厂商名称、产地、详细地址、简称、开户银行、银行账号 +- 自动化断言:与 CSV `VENDOR-QRY-007` 及 `tests/spd/vendor-query.spec.ts` 一致 + +###### 预期结果 + +核心列可见。 + +--- + +##### 【P1】【TC-UI-VENDOR-QRY-008】【TP_VENDOR_QRY_013】分页翻至第 2 页 + +- 前置条件:器械注册人总数足够翻页 +- 操作步骤: + 1. 总数足够时翻到第 2 页 + 2. 断言列表可加载;不足两页则 skip +- 自动化断言:与 CSV `VENDOR-QRY-008` 及 `tests/spd/vendor-query.spec.ts` 一致 + +###### 预期结果 + +第 2 页可加载。 + +--- + +#### 边界验证点 + +##### 【P2】【TC-UI-VENDOR-QRY-009】【TP_VENDOR_QRY_015】仅空格关键字搜索 + +- 前置条件:已进入厂商查询页 +- 操作步骤: + 1. 厂商名称仅填空格并回车 + 2. 断言按空查询处理或提示,不白屏 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +不报错;行为明确。 + +--- + +### 厂商查询 · 页面进入 + +#### 异常验证点 + +##### 【P0】【TC-UI-VENDOR-QRY-010】【TP_VENDOR_QRY_005】未登录访问拦截 + +- 前置条件:已进入厂商查询页 +- 操作步骤: + 1. 清除登录态后直接打开厂商查询 URL + 2. 断言跳转登录或不展示业务列表 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +未登录不可见厂商数据。 + +--- + +### 厂商查询 · Tab 与搜索 + +#### 集成验证点 + +##### 【P1】【TC-UI-VENDOR-QRY-011】【TP_VENDOR_QRY_018】厂商名称与资质管理进院供应商可对照 + +- 前置条件:已进入厂商查询页 +- 操作步骤: + 1. 记录厂商查询中某厂商名称(若有) + 2. 打开供应商资质管理,用同源关键字检索或目视对照 + 3. 断言不出现明显主数据串名(允许一方无记录) +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +厂商主数据跨菜单可对照,不串名。 diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..29bb742 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,184 @@ +# 基础管理-厂商管理-授权变更初审 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-厂商管理-授权变更初审-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-厂商管理-授权变更初审-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/mfrs/paltform/mfrsName/authAudit` +**定位器 YAML**:`locators/spd/vendor-auth-first.locator.yaml` +**元素扫描**:`data/scan/vendor-auth-first-elements.json`(40 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-003.md` +**版本**:2026-09-30 +**自动化框架**:Playwright + TypeScript(`tests/spd/vendor-audit.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 厂商管理 → 授权变更初审」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**写操作**:审批写操作只打开详情后返回,不确认通过/驳回/提交。 +**动态取数**:默认查询后从首行读取 `${sampleOrderNo}`;禁止写死单号;mock:`__NOT_EXIST_ORDER__99999`。 + +--- + +## 基础设置 · 厂商管理 + +### 授权变更初审 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-AF1-001】【TP_VENDOR_AF1_001】菜单进入授权变更初审页 + +- 前置条件:已登录且具备厂商管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 厂商管理 → 授权变更初审」进入 + 2. 断言 URL 含 `/spd/basic/mfrs/paltform/mfrsName/authAudit` + 3. 断言可见「查 询」「重 置」及制单日期筛选 +- 自动化断言:与 CSV `VENDOR-AF1-001` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +进入授权变更初审页;不误入其它变更审批菜单。 + +--- + +### 授权变更初审 · 筛选与列表 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-AF1-002】【TP_VENDOR_AF1_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入授权变更初审页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行或空态明确 +- 自动化断言:与 CSV `VENDOR-AF1-002` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +查询无报错;有数据时分页 N≥1。 + +--- + +##### 【P0】【TC-UI-VENDOR-AF1-003】【TP_VENDOR_AF1_010】按单号筛选 + +- 前置条件:列表至少 1 条且单号非空 +- 操作步骤: + 1. 从首行读取 `${sampleOrderNo}` 取关键字 + 2. 单号填入关键字并「查 询」 + 3. 断言结果行「单号」包含关键字 +- 自动化断言:与 CSV `VENDOR-AF1-003` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +单号列与条件一致。 + +--- + +##### 【P0】【TC-UI-VENDOR-AF1-004】【TP_VENDOR_AF1_011】按进院供应商筛选 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 按样本填写或选择「进院供应商」并查询 + 2. 断言结果列与条件匹配 +- 自动化断言:与 CSV `VENDOR-AF1-004` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +筛选条件生效。 + +--- + +##### 【P1】【TC-UI-VENDOR-AF1-005】【TP_VENDOR_AF1_012】重置恢复默认 + +- 前置条件:已进入授权变更初审页 +- 操作步骤: + 1. 填写筛选项后点击「重 置」 + 2. 断言文本条件清空、列表按默认规则刷新 +- 自动化断言:与 CSV `VENDOR-AF1-005` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +重置成功。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-VENDOR-AF1-006】【TP_VENDOR_AF1_016】无匹配空态(mock反例) + +- 前置条件:已进入授权变更初审页 +- 操作步骤: + 1. 单号填入 `__NOT_EXIST_ORDER__99999` 并查询 + 2. 断言空态且分页总数为 0 +- 自动化断言:与 CSV `VENDOR-AF1-006` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +无匹配时不报错;总数为 0。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-AF1-007】【TP_VENDOR_AF1_013】列表核心列完整 + +- 前置条件:已进入授权变更初审页 +- 操作步骤: + 1. 断言表头含 进院供应商名称、单号、状态、制单时间、制单人、变更类型、原供应商、预转供应商、操作 + 2. 断言存在「详情」操作 +- 自动化断言:与 CSV `VENDOR-AF1-007` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +核心列与详情入口可见。 + +--- + +### 授权变更初审 · 详情 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-AF1-008】【TP_VENDOR_AF1_014】打开详情后返回 + +- 前置条件:存在含「详情」的行 +- 操作步骤: + 1. 点击行内「详 情」 + 2. 断言详情层可见且含单号片段 + 3. 返回/关闭,不提交审批 +- 自动化断言:与 CSV `VENDOR-AF1-008` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +详情只读可返回;列表状态不变。 + +--- + +### 授权变更初审 · 筛选与列表 + +#### 边界验证点 + +##### 【P2】【TC-UI-VENDOR-AF1-009】【TP_VENDOR_AF1_015】制单日期起止同一天 + +- 前置条件:已进入授权变更初审页 +- 操作步骤: + 1. 制单日期起止选同一天并查询 + 2. 断言仅返回该日单据或空态明确 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +同日区间行为明确。 + +--- + +### 授权变更初审 · 详情 + +#### 集成验证点 + +##### 【P0】【TC-UI-VENDOR-AF1-010】【TP_VENDOR_AF1_018】与终审单据衔接不串单 + +- 前置条件:已进入授权变更初审页 +- 操作步骤: + 1. 记录本页某单号后切换到对应终审菜单 + 2. 断言同业务单据状态流转合理、不串到非本业务菜单 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +初审/终审衔接正确。 diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..1b9767d --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,184 @@ +# 基础管理-厂商管理-授权变更终审 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-厂商管理-授权变更终审-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-厂商管理-授权变更终审-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/mfrs/paltform/mfrsName/authAuditFinal` +**定位器 YAML**:`locators/spd/vendor-auth-final.locator.yaml` +**元素扫描**:`data/scan/vendor-auth-final-elements.json`(40 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-004.md` +**版本**:2026-09-30 +**自动化框架**:Playwright + TypeScript(`tests/spd/vendor-audit.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 厂商管理 → 授权变更终审」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**写操作**:审批写操作只打开详情后返回,不确认通过/驳回/提交。 +**动态取数**:默认查询后从首行读取 `${sampleOrderNo}`;禁止写死单号;mock:`__NOT_EXIST_ORDER__99999`。 + +--- + +## 基础设置 · 厂商管理 + +### 授权变更终审 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-AF2-001】【TP_VENDOR_AF2_001】菜单进入授权变更终审页 + +- 前置条件:已登录且具备厂商管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 厂商管理 → 授权变更终审」进入 + 2. 断言 URL 含 `/spd/basic/mfrs/paltform/mfrsName/authAuditFinal` + 3. 断言可见「查 询」「重 置」及制单日期筛选 +- 自动化断言:与 CSV `VENDOR-AF2-001` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +进入授权变更终审页;不误入其它变更审批菜单。 + +--- + +### 授权变更终审 · 筛选与列表 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-AF2-002】【TP_VENDOR_AF2_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入授权变更终审页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行或空态明确 +- 自动化断言:与 CSV `VENDOR-AF2-002` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +查询无报错;有数据时分页 N≥1。 + +--- + +##### 【P0】【TC-UI-VENDOR-AF2-003】【TP_VENDOR_AF2_010】按单号筛选 + +- 前置条件:列表至少 1 条且单号非空 +- 操作步骤: + 1. 从首行读取 `${sampleOrderNo}` 取关键字 + 2. 单号填入关键字并「查 询」 + 3. 断言结果行「单号」包含关键字 +- 自动化断言:与 CSV `VENDOR-AF2-003` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +单号列与条件一致。 + +--- + +##### 【P0】【TC-UI-VENDOR-AF2-004】【TP_VENDOR_AF2_011】按进院供应商筛选 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 按样本填写或选择「进院供应商」并查询 + 2. 断言结果列与条件匹配 +- 自动化断言:与 CSV `VENDOR-AF2-004` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +筛选条件生效。 + +--- + +##### 【P1】【TC-UI-VENDOR-AF2-005】【TP_VENDOR_AF2_012】重置恢复默认 + +- 前置条件:已进入授权变更终审页 +- 操作步骤: + 1. 填写筛选项后点击「重 置」 + 2. 断言文本条件清空、列表按默认规则刷新 +- 自动化断言:与 CSV `VENDOR-AF2-005` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +重置成功。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-VENDOR-AF2-006】【TP_VENDOR_AF2_016】无匹配空态(mock反例) + +- 前置条件:已进入授权变更终审页 +- 操作步骤: + 1. 单号填入 `__NOT_EXIST_ORDER__99999` 并查询 + 2. 断言空态且分页总数为 0 +- 自动化断言:与 CSV `VENDOR-AF2-006` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +无匹配时不报错;总数为 0。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-AF2-007】【TP_VENDOR_AF2_013】列表核心列完整 + +- 前置条件:已进入授权变更终审页 +- 操作步骤: + 1. 断言表头含 进院供应商名称、单号、状态、制单时间、制单人、变更类型、原供应商、预转供应商、操作 + 2. 断言存在「详情」操作 +- 自动化断言:与 CSV `VENDOR-AF2-007` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +核心列与详情入口可见。 + +--- + +### 授权变更终审 · 详情 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-AF2-008】【TP_VENDOR_AF2_014】打开详情后返回 + +- 前置条件:存在含「详情」的行 +- 操作步骤: + 1. 点击行内「详 情」 + 2. 断言详情层可见且含单号片段 + 3. 返回/关闭,不提交审批 +- 自动化断言:与 CSV `VENDOR-AF2-008` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +详情只读可返回;列表状态不变。 + +--- + +### 授权变更终审 · 筛选与列表 + +#### 边界验证点 + +##### 【P2】【TC-UI-VENDOR-AF2-009】【TP_VENDOR_AF2_015】制单日期起止同一天 + +- 前置条件:已进入授权变更终审页 +- 操作步骤: + 1. 制单日期起止选同一天并查询 + 2. 断言仅返回该日单据或空态明确 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +同日区间行为明确。 + +--- + +### 授权变更终审 · 详情 + +#### 集成验证点 + +##### 【P0】【TC-UI-VENDOR-AF2-010】【TP_VENDOR_AF2_018】与初审单据衔接不串单 + +- 前置条件:已进入授权变更终审页 +- 操作步骤: + 1. 记录本页某单号后切换到对应初审菜单 + 2. 断言同业务单据状态流转合理、不串到非本业务菜单 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +初审/终审衔接正确。 diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..5e14f0f --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,184 @@ +# 基础管理-厂商管理-注册证变更初审 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-厂商管理-注册证变更初审-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-厂商管理-注册证变更初审-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/mfrs/paltform/mfrsName/regCertAudit` +**定位器 YAML**:`locators/spd/vendor-cert-first.locator.yaml` +**元素扫描**:`data/scan/vendor-cert-first-elements.json`(40 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-005.md` +**版本**:2026-09-30 +**自动化框架**:Playwright + TypeScript(`tests/spd/vendor-audit.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 厂商管理 → 注册证变更初审」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**写操作**:审批写操作只打开详情后返回,不确认通过/驳回/提交。 +**动态取数**:默认查询后从首行读取 `${sampleOrderNo}`;禁止写死单号;mock:`__NOT_EXIST_ORDER__99999`。 + +--- + +## 基础设置 · 厂商管理 + +### 注册证变更初审 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-CF1-001】【TP_VENDOR_CF1_001】菜单进入注册证变更初审页 + +- 前置条件:已登录且具备厂商管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 厂商管理 → 注册证变更初审」进入 + 2. 断言 URL 含 `/spd/basic/mfrs/paltform/mfrsName/regCertAudit` + 3. 断言可见「查 询」「重 置」及制单日期筛选 +- 自动化断言:与 CSV `VENDOR-CF1-001` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +进入注册证变更初审页;不误入其它变更审批菜单。 + +--- + +### 注册证变更初审 · 筛选与列表 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-CF1-002】【TP_VENDOR_CF1_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入注册证变更初审页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行或空态明确 +- 自动化断言:与 CSV `VENDOR-CF1-002` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +查询无报错;有数据时分页 N≥1。 + +--- + +##### 【P0】【TC-UI-VENDOR-CF1-003】【TP_VENDOR_CF1_010】按单号筛选 + +- 前置条件:列表至少 1 条且单号非空 +- 操作步骤: + 1. 从首行读取 `${sampleOrderNo}` 取关键字 + 2. 单号填入关键字并「查 询」 + 3. 断言结果行「单号」包含关键字 +- 自动化断言:与 CSV `VENDOR-CF1-003` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +单号列与条件一致。 + +--- + +##### 【P0】【TC-UI-VENDOR-CF1-004】【TP_VENDOR_CF1_011】按注册证号(新)筛选 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 按样本填写或选择「注册证号(新)」并查询 + 2. 断言结果列与条件匹配 +- 自动化断言:与 CSV `VENDOR-CF1-004` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +筛选条件生效。 + +--- + +##### 【P1】【TC-UI-VENDOR-CF1-005】【TP_VENDOR_CF1_012】重置恢复默认 + +- 前置条件:已进入注册证变更初审页 +- 操作步骤: + 1. 填写筛选项后点击「重 置」 + 2. 断言文本条件清空、列表按默认规则刷新 +- 自动化断言:与 CSV `VENDOR-CF1-005` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +重置成功。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-VENDOR-CF1-006】【TP_VENDOR_CF1_016】无匹配空态(mock反例) + +- 前置条件:已进入注册证变更初审页 +- 操作步骤: + 1. 单号填入 `__NOT_EXIST_ORDER__99999` 并查询 + 2. 断言空态且分页总数为 0 +- 自动化断言:与 CSV `VENDOR-CF1-006` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +无匹配时不报错;总数为 0。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-CF1-007】【TP_VENDOR_CF1_013】列表核心列完整 + +- 前置条件:已进入注册证变更初审页 +- 操作步骤: + 1. 断言表头含 进院供应商名称、单号、状态、制单时间、制单人、变更类型、注册证号(新)、操作 + 2. 断言存在「详情」操作 +- 自动化断言:与 CSV `VENDOR-CF1-007` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +核心列与详情入口可见。 + +--- + +### 注册证变更初审 · 详情 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-CF1-008】【TP_VENDOR_CF1_014】打开详情后返回 + +- 前置条件:存在含「详情」的行 +- 操作步骤: + 1. 点击行内「详 情」 + 2. 断言详情层可见且含单号片段 + 3. 返回/关闭,不提交审批 +- 自动化断言:与 CSV `VENDOR-CF1-008` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +详情只读可返回;列表状态不变。 + +--- + +### 注册证变更初审 · 筛选与列表 + +#### 边界验证点 + +##### 【P2】【TC-UI-VENDOR-CF1-009】【TP_VENDOR_CF1_015】制单日期起止同一天 + +- 前置条件:已进入注册证变更初审页 +- 操作步骤: + 1. 制单日期起止选同一天并查询 + 2. 断言仅返回该日单据或空态明确 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +同日区间行为明确。 + +--- + +### 注册证变更初审 · 详情 + +#### 集成验证点 + +##### 【P0】【TC-UI-VENDOR-CF1-010】【TP_VENDOR_CF1_018】与终审单据衔接不串单 + +- 前置条件:已进入注册证变更初审页 +- 操作步骤: + 1. 记录本页某单号后切换到对应终审菜单 + 2. 断言同业务单据状态流转合理、不串到非本业务菜单 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +初审/终审衔接正确。 diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..b15ff7d --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,184 @@ +# 基础管理-厂商管理-注册证变更终审 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-厂商管理-注册证变更终审-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-厂商管理-注册证变更终审-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal` +**定位器 YAML**:`locators/spd/vendor-cert-final.locator.yaml` +**元素扫描**:`data/scan/vendor-cert-final-elements.json`(40 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-006.md` +**版本**:2026-09-30 +**自动化框架**:Playwright + TypeScript(`tests/spd/vendor-audit.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 厂商管理 → 注册证变更终审」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**写操作**:审批写操作只打开详情后返回,不确认通过/驳回/提交。 +**动态取数**:默认查询后从首行读取 `${sampleOrderNo}`;禁止写死单号;mock:`__NOT_EXIST_ORDER__99999`。 + +--- + +## 基础设置 · 厂商管理 + +### 注册证变更终审 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-CF2-001】【TP_VENDOR_CF2_001】菜单进入注册证变更终审页 + +- 前置条件:已登录且具备厂商管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 厂商管理 → 注册证变更终审」进入 + 2. 断言 URL 含 `/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal` + 3. 断言可见「查 询」「重 置」及制单日期筛选 +- 自动化断言:与 CSV `VENDOR-CF2-001` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +进入注册证变更终审页;不误入其它变更审批菜单。 + +--- + +### 注册证变更终审 · 筛选与列表 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-CF2-002】【TP_VENDOR_CF2_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入注册证变更终审页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行或空态明确 +- 自动化断言:与 CSV `VENDOR-CF2-002` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +查询无报错;有数据时分页 N≥1。 + +--- + +##### 【P0】【TC-UI-VENDOR-CF2-003】【TP_VENDOR_CF2_010】按单号筛选 + +- 前置条件:列表至少 1 条且单号非空 +- 操作步骤: + 1. 从首行读取 `${sampleOrderNo}` 取关键字 + 2. 单号填入关键字并「查 询」 + 3. 断言结果行「单号」包含关键字 +- 自动化断言:与 CSV `VENDOR-CF2-003` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +单号列与条件一致。 + +--- + +##### 【P0】【TC-UI-VENDOR-CF2-004】【TP_VENDOR_CF2_011】按注册证号(新)筛选 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 按样本填写或选择「注册证号(新)」并查询 + 2. 断言结果列与条件匹配 +- 自动化断言:与 CSV `VENDOR-CF2-004` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +筛选条件生效。 + +--- + +##### 【P1】【TC-UI-VENDOR-CF2-005】【TP_VENDOR_CF2_012】重置恢复默认 + +- 前置条件:已进入注册证变更终审页 +- 操作步骤: + 1. 填写筛选项后点击「重 置」 + 2. 断言文本条件清空、列表按默认规则刷新 +- 自动化断言:与 CSV `VENDOR-CF2-005` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +重置成功。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-VENDOR-CF2-006】【TP_VENDOR_CF2_016】无匹配空态(mock反例) + +- 前置条件:已进入注册证变更终审页 +- 操作步骤: + 1. 单号填入 `__NOT_EXIST_ORDER__99999` 并查询 + 2. 断言空态且分页总数为 0 +- 自动化断言:与 CSV `VENDOR-CF2-006` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +无匹配时不报错;总数为 0。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-CF2-007】【TP_VENDOR_CF2_013】列表核心列完整 + +- 前置条件:已进入注册证变更终审页 +- 操作步骤: + 1. 断言表头含 进院供应商名称、单号、状态、制单时间、制单人、变更类型、注册证号(新)、操作 + 2. 断言存在「详情」操作 +- 自动化断言:与 CSV `VENDOR-CF2-007` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +核心列与详情入口可见。 + +--- + +### 注册证变更终审 · 详情 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-CF2-008】【TP_VENDOR_CF2_014】打开详情后返回 + +- 前置条件:存在含「详情」的行 +- 操作步骤: + 1. 点击行内「详 情」 + 2. 断言详情层可见且含单号片段 + 3. 返回/关闭,不提交审批 +- 自动化断言:与 CSV `VENDOR-CF2-008` 及 `tests/spd/vendor-audit.spec.ts` 一致 + +###### 预期结果 + +详情只读可返回;列表状态不变。 + +--- + +### 注册证变更终审 · 筛选与列表 + +#### 边界验证点 + +##### 【P2】【TC-UI-VENDOR-CF2-009】【TP_VENDOR_CF2_015】制单日期起止同一天 + +- 前置条件:已进入注册证变更终审页 +- 操作步骤: + 1. 制单日期起止选同一天并查询 + 2. 断言仅返回该日单据或空态明确 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +同日区间行为明确。 + +--- + +### 注册证变更终审 · 详情 + +#### 集成验证点 + +##### 【P0】【TC-UI-VENDOR-CF2-010】【TP_VENDOR_CF2_018】与初审单据衔接不串单 + +- 前置条件:已进入注册证变更终审页 +- 操作步骤: + 1. 记录本页某单号后切换到对应初审菜单 + 2. 断言同业务单据状态流转合理、不串到非本业务菜单 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +初审/终审衔接正确。 diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..65aabf3 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,193 @@ +# 基础管理-厂商管理-注册证差异对比 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-厂商管理-注册证差异对比-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-厂商管理-注册证差异对比-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/mfrs/scjs/yc/certCompare` +**定位器 YAML**:`locators/spd/vendor-cert-diff.locator.yaml` +**元素扫描**:`data/scan/vendor-cert-diff-elements.json`(54 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-007.md` +**版本**:2026-09-30 +**自动化框架**:Playwright + TypeScript(`tests/spd/vendor-cert-diff.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 厂商管理 → 注册证差异对比」权限。 +**页面约定**:主体在 scjs **iframe**。 +**写操作**:主体在 scjs iframe;导出本批只断言按钮可见,不确认下载。 +**动态取数**:iframe 内首行读取 `${sampleMaterialName}`;mock:`__NOT_EXIST_MFRS__99999`。 + +--- + +## 基础设置 · 厂商管理 + +### 注册证差异对比 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-CDF-001】【TP_VENDOR_CDF_001】菜单进入注册证差异对比页 + +- 前置条件:已登录且具备厂商管理权限 +- 操作步骤: + 1. 按菜单「基础设置 → 厂商管理 → 注册证差异对比」进入 + 2. 断言 URL 含 `certCompare` + 3. 断言外壳或 iframe 内可见物料名称/全部/一致/不一致等主区 +- 自动化断言:与 CSV `VENDOR-CDF-001` 及 `tests/spd/vendor-cert-diff.spec.ts` 一致 + +###### 预期结果 + +进入差异对比页;iframe 或主区加载成功。 + +--- + +### 注册证差异对比 · 筛选与对比 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-CDF-002】【TP_VENDOR_CDF_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入注册证差异对比页 +- 操作步骤: + 1. 在对比页执行查询(若有查询按钮) + 2. 断言列表有数据或空态明确 +- 自动化断言:与 CSV `VENDOR-CDF-002` 及 `tests/spd/vendor-cert-diff.spec.ts` 一致 + +###### 预期结果 + +查询无报错。 + +--- + +##### 【P0】【TC-UI-VENDOR-CDF-003】【TP_VENDOR_CDF_013】切换不一致 Tab + +- 前置条件:已进入注册证差异对比页 +- 操作步骤: + 1. 点击「不一致」 + 2. 断言列表稳定 +- 自动化断言:与 CSV `VENDOR-CDF-003` 及 `tests/spd/vendor-cert-diff.spec.ts` 一致 + +###### 预期结果 + +不一致 Tab 切换成功。 + +--- + +##### 【P1】【TC-UI-VENDOR-CDF-004】【TP_VENDOR_CDF_013】切换一致 Tab + +- 前置条件:已进入注册证差异对比页 +- 操作步骤: + 1. 点击「一致」 + 2. 断言列表稳定 +- 自动化断言:与 CSV `VENDOR-CDF-004` 及 `tests/spd/vendor-cert-diff.spec.ts` 一致 + +###### 预期结果 + +一致 Tab 切换成功。 + +--- + +##### 【P0】【TC-UI-VENDOR-CDF-005】【TP_VENDOR_CDF_010】按物料名称筛选 + +- 前置条件:对比列表至少 1 条 +- 操作步骤: + 1. 从首行读取 `${sampleMaterialName}` 取关键字 + 2. 物料名称填入并查询 + 3. 断言结果行物料名称包含关键字 +- 自动化断言:与 CSV `VENDOR-CDF-005` 及 `tests/spd/vendor-cert-diff.spec.ts` 一致 + +###### 预期结果 + +物料名称匹配。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-VENDOR-CDF-006】【TP_VENDOR_CDF_016】无匹配空态(mock反例) + +- 前置条件:已进入注册证差异对比页 +- 操作步骤: + 1. 物料名称填 mock 关键字并查询 + 2. 断言空态或总数为 0 +- 自动化断言:与 CSV `VENDOR-CDF-006` 及 `tests/spd/vendor-cert-diff.spec.ts` 一致 + +###### 预期结果 + +无匹配时不报错。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-CDF-007】【TP_VENDOR_CDF_014】列表核心列完整 + +- 前置条件:已进入注册证差异对比页 +- 操作步骤: + 1. 断言表头含物料编号/物料名称/规格型号/生产厂家等关键列 +- 自动化断言:与 CSV `VENDOR-CDF-007` 及 `tests/spd/vendor-cert-diff.spec.ts` 一致 + +###### 预期结果 + +核心对比列可见。 + +--- + +### 注册证差异对比 · 导出 + +#### 功能验证点 + +##### 【P1】【TC-UI-VENDOR-CDF-008】【TP_VENDOR_CDF_015】导出按钮可见 + +- 前置条件:已进入注册证差异对比页 +- 操作步骤: + 1. 断言「导出」按钮可见(不确认下载) +- 自动化断言:与 CSV `VENDOR-CDF-008` 及 `tests/spd/vendor-cert-diff.spec.ts` 一致 + +###### 预期结果 + +导出入口可见。 + +--- + +### 注册证差异对比 · 筛选与对比 + +#### 边界验证点 + +##### 【P2】【TC-UI-VENDOR-CDF-009】【TP_VENDOR_CDF_015】仅空格物料名称搜索 + +- 前置条件:已进入注册证差异对比页 +- 操作步骤: + 1. 物料名称仅填空格并查询 + 2. 断言按空查询处理或提示,不白屏 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +空格关键字不导致页面异常。 + +--- + +#### 集成验证点 + +##### 【P0】【TC-UI-VENDOR-CDF-010】【TP_VENDOR_CDF_017】一致行两侧字段抽样一致 + +- 前置条件:已进入注册证差异对比页 +- 操作步骤: + 1. 在「一致」Tab 抽样一行 + 2. 核对望海与 FLI+ 注册证号/效期关键字段一致 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +标记一致的行两侧关键字段抽样相同。 + +--- + +##### 【P0】【TC-UI-VENDOR-CDF-011】【TP_VENDOR_CDF_018】不一致行至少一侧差异 + +- 前置条件:已进入注册证差异对比页 +- 操作步骤: + 1. 在「不一致」Tab 抽样一行 + 2. 断言望海与 FLI+ 至少一处字段不同 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +不一致结论与字段差异相符。 diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..ab5229b --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,166 @@ +# 基础管理-厂商管理-评价指标维护 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-厂商管理-评价指标维护-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-厂商管理-评价指标维护-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/mfrs/paltform/mfrsName/indicators` +**定位器 YAML**:`locators/spd/vendor-eval-indicator.locator.yaml` +**元素扫描**:`data/scan/vendor-eval-indicator-elements.json`(151 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-009.md` +**版本**:2026-09-30 +**自动化框架**:Playwright + TypeScript(`tests/spd/vendor-eval-indicator.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 厂商管理 → 评价指标维护」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**写操作**:详情只读返回;不落库新增/编辑提交。 +**动态取数**:右表首行读取 `${sampleIndicatorCode}`;列名可能为指标编码或指标编号;mock:`__NOT_EXIST_ORDER__99999`。 + +--- + +## 基础设置 · 厂商管理 + +### 评价指标维护 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-EVI-001】【TP_VENDOR_EVI_001】菜单进入评价指标维护页 + +- 前置条件:已登录且具备厂商管理权限 +- 操作步骤: + 1. 按菜单进入评价指标维护 + 2. 断言 URL 含 `indicators` + 3. 断言可见「指标分类」与查询 +- 自动化断言:与 CSV `VENDOR-EVI-001` 及 `tests/spd/vendor-eval-indicator.spec.ts` 一致 + +###### 预期结果 + +进入评价指标维护页。 + +--- + +### 评价指标维护 · 树与列表 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-EVI-002】【TP_VENDOR_EVI_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入评价指标维护页 +- 操作步骤: + 1. 点击查询 + 2. 断言右表有数据或空态 +- 自动化断言:与 CSV `VENDOR-EVI-002` 及 `tests/spd/vendor-eval-indicator.spec.ts` 一致 + +###### 预期结果 + +查询无报错。 + +--- + +##### 【P0】【TC-UI-VENDOR-EVI-003】【TP_VENDOR_EVI_010】点击左侧分类树 + +- 前置条件:已进入评价指标维护页 +- 操作步骤: + 1. 点击左侧首个分类节点 + 2. 断言右侧列表刷新 +- 自动化断言:与 CSV `VENDOR-EVI-003` 及 `tests/spd/vendor-eval-indicator.spec.ts` 一致 + +###### 预期结果 + +分类切换成功。 + +--- + +##### 【P0】【TC-UI-VENDOR-EVI-004】【TP_VENDOR_EVI_011】按指标编码筛选 + +- 前置条件:右表有数据 +- 操作步骤: + 1. 取样本指标编码/编号筛选并查询 + 2. 断言编码列匹配 +- 自动化断言:与 CSV `VENDOR-EVI-004` 及 `tests/spd/vendor-eval-indicator.spec.ts` 一致 + +###### 预期结果 + +指标编码匹配。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-VENDOR-EVI-005】【TP_VENDOR_EVI_015】无匹配空态(mock反例) + +- 前置条件:已进入评价指标维护页 +- 操作步骤: + 1. 指标编码填 mock 后查询 + 2. 断言空态 +- 自动化断言:与 CSV `VENDOR-EVI-005` 及 `tests/spd/vendor-eval-indicator.spec.ts` 一致 + +###### 预期结果 + +无匹配不报错。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-EVI-006】【TP_VENDOR_EVI_012】列表核心列完整 + +- 前置条件:已进入评价指标维护页 +- 操作步骤: + 1. 断言表头含评价类型、指标编码或指标编号、指标名称、状态、操作 +- 自动化断言:与 CSV `VENDOR-EVI-006` 及 `tests/spd/vendor-eval-indicator.spec.ts` 一致 + +###### 预期结果 + +核心列可见。 + +--- + +### 评价指标维护 · 详情 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-EVI-007】【TP_VENDOR_EVI_014】打开详情后返回 + +- 前置条件:存在详情操作行 +- 操作步骤: + 1. 点击行内详情 + 2. 断言详情层可见 + 3. 返回不保存 +- 自动化断言:与 CSV `VENDOR-EVI-007` 及 `tests/spd/vendor-eval-indicator.spec.ts` 一致 + +###### 预期结果 + +详情只读可返回。 + +--- + +### 评价指标维护 · 树与列表 + +#### 边界验证点 + +##### 【P2】【TC-UI-VENDOR-EVI-008】【TP_VENDOR_EVI_015】分类下无指标空态 + +- 前置条件:已进入评价指标维护页 +- 操作步骤: + 1. 选择无指标的分类(若有) + 2. 断言右表空态明确 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +空分类下列表空态正确。 + +--- + +#### 集成验证点 + +##### 【P1】【TC-UI-VENDOR-EVI-009】【TP_VENDOR_EVI_018】启用指标可供方案使用 + +- 前置条件:已进入评价指标维护页 +- 操作步骤: + 1. 确认状态为启用的指标 + 2. 在评价方案维护中核对其可选(只读核对或抽样) +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +启用指标与方案引用衔接正确。 diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..e405ec0 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,183 @@ +# 基础管理-厂商管理-评价方案维护 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-厂商管理-评价方案维护-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-厂商管理-评价方案维护-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/mfrs/paltform/mfrsName/evaluationPlan` +**定位器 YAML**:`locators/spd/vendor-eval-scheme.locator.yaml` +**元素扫描**:`data/scan/vendor-eval-scheme-elements.json`(57 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-008.md` +**版本**:2026-09-30 +**自动化框架**:Playwright + TypeScript(`tests/spd/vendor-eval-scheme.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效;测试账号具备「基础设置 → 厂商管理 → 评价方案维护」权限。 +**页面约定**:整页渲染,**无报表 iframe**。 +**写操作**:新增只打开后取消,不提交保存。 +**动态取数**:有数据时从首行读取 `${sampleSchemeCode}`;mock:`__NOT_EXIST_ORDER__99999`。 + +--- + +## 基础设置 · 厂商管理 + +### 评价方案维护 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-EVS-001】【TP_VENDOR_EVS_001】菜单进入评价方案维护页 + +- 前置条件:已登录且具备厂商管理权限 +- 操作步骤: + 1. 按菜单进入评价方案维护 + 2. 断言 URL 含 `evaluationPlan` + 3. 断言可见查询与新增 +- 自动化断言:与 CSV `VENDOR-EVS-001` 及 `tests/spd/vendor-eval-scheme.spec.ts` 一致 + +###### 预期结果 + +进入评价方案维护页。 + +--- + +### 评价方案维护 · 筛选与维护 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-EVS-002】【TP_VENDOR_EVS_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入评价方案维护页 +- 操作步骤: + 1. 点击查询 + 2. 断言列表有数据或「暂无数据」空态 +- 自动化断言:与 CSV `VENDOR-EVS-002` 及 `tests/spd/vendor-eval-scheme.spec.ts` 一致 + +###### 预期结果 + +查询无报错。 + +--- + +##### 【P0】【TC-UI-VENDOR-EVS-003】【TP_VENDOR_EVS_010】按方案编码筛选 + +- 前置条件:列表有数据 +- 操作步骤: + 1. 取样本方案编码筛选并查询 + 2. 断言方案编码列匹配 +- 自动化断言:与 CSV `VENDOR-EVS-003` 及 `tests/spd/vendor-eval-scheme.spec.ts` 一致 + +###### 预期结果 + +方案编码匹配。 + +--- + +##### 【P1】【TC-UI-VENDOR-EVS-004】【TP_VENDOR_EVS_011】重置恢复默认 + +- 前置条件:已进入评价方案维护页 +- 操作步骤: + 1. 填写筛选后重置 + 2. 断言条件清空、列表刷新 +- 自动化断言:与 CSV `VENDOR-EVS-004` 及 `tests/spd/vendor-eval-scheme.spec.ts` 一致 + +###### 预期结果 + +重置成功。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-VENDOR-EVS-005】【TP_VENDOR_EVS_015】无匹配空态(mock反例) + +- 前置条件:已进入评价方案维护页 +- 操作步骤: + 1. 方案编码填 mock 后查询 + 2. 断言空态总数 0 +- 自动化断言:与 CSV `VENDOR-EVS-005` 及 `tests/spd/vendor-eval-scheme.spec.ts` 一致 + +###### 预期结果 + +无匹配不报错。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-EVS-006】【TP_VENDOR_EVS_012】列表核心列完整 + +- 前置条件:已进入评价方案维护页 +- 操作步骤: + 1. 断言表头含方案编码、方案名称、创建人、创建时间、状态、操作 +- 自动化断言:与 CSV `VENDOR-EVS-006` 及 `tests/spd/vendor-eval-scheme.spec.ts` 一致 + +###### 预期结果 + +核心列可见。 + +--- + +### 评价方案维护 · 新增 + +#### 功能验证点 + +##### 【P0】【TC-UI-VENDOR-EVS-007】【TP_VENDOR_EVS_013】打开新增后取消 + +- 前置条件:已进入评价方案维护页 +- 操作步骤: + 1. 记录总数 + 2. 点击新增打开编辑层 + 3. 取消/返回不提交 + 4. 断言总数不变 +- 自动化断言:与 CSV `VENDOR-EVS-007` 及 `tests/spd/vendor-eval-scheme.spec.ts` 一致 + +###### 预期结果 + +取消不落库。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-VENDOR-EVS-008】【TP_VENDOR_EVS_016】必填为空提交拦截 + +- 前置条件:已进入评价方案维护页 +- 操作步骤: + 1. 打开新增不填必填项尝试提交(若可点) + 2. 断言拦截提示,页面不关闭或总数不变 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +空提交被拦截。 + +--- + +### 评价方案维护 · 筛选与维护 + +#### 边界验证点 + +##### 【P2】【TC-UI-VENDOR-EVS-009】【TP_VENDOR_EVS_015】方案名称超长输入 + +- 前置条件:已进入评价方案维护页 +- 操作步骤: + 1. 打开新增,方案名称输入超长字符串(如 200+ 字符) + 2. 断言前端截断/校验提示或后端拒绝,不白屏 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +超长输入被约束且页面可用。 + +--- + +#### 集成验证点 + +##### 【P1】【TC-UI-VENDOR-EVS-010】【TP_VENDOR_EVS_018】方案与评价指标可关联 + +- 前置条件:已进入评价方案维护页 +- 操作步骤: + 1. 在方案配置中查看指标选择(只读打开) + 2. 断言可选指标来源于评价指标维护启用项或规则允许集合 +- 自动化断言:本条以手工/后续扩展为主;当前冒烟未覆盖 + +###### 预期结果 + +方案与指标主数据衔接正确。 diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..3cdccd5 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,367 @@ +# 基础管理-库房管理-库区设置 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-库房管理-库区设置-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-库房管理-库区设置-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/storehouse/platform/storehouse/area` +**定位器 YAML**:`locators/spd/warehouse-zone.locator.yaml` +**元素扫描**:`data/scan/warehouse-zone-elements.json`(106 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-WH-001.md` +**版本**:2026-10-20 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/warehouse-zone.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:正例账号 `test_1` 经 `auth.setup` 写入 `storageState`(密码仅存 `.env` 的 `TEST_PASS`);具备「基础设置 → 库房管理 → 库区设置」权限。反例筛选用 mock。 +**页面约定**:整页渲染,**无报表 iframe**;定位均在 `page` 上。 +**菜单导航**:菜单「基础设置 → 库房管理 → 库区设置」;建议 `openPath('/spd/basic/storehouse/platform/storehouse/area')` 并断言 URL,勿误入探针绑定。 +**写操作**:新增/编辑只打开后取消或点确定做必填拦截,不提交成功保存;不停用/启用落库;人员管理/货位管理只打开后关闭;同步仅点一次并断言树仍在。 +**动态取数**:从左侧树读取 `${sampleWhName}`,从右侧首行读取 `${sampleZoneNo}`、`${sampleZoneName}`;mock:`__NOT_EXIST_ZONE__99999`。 + +--- + +## 基础设置 · 库房管理 + +### 库区设置 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-ZON-001】【TP_WH_ZON_001】菜单进入库区设置页 + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 按菜单「基础设置 → 库房管理 → 库区设置」进入,或 `openPath` 打开 `/spd/basic/storehouse/platform/storehouse/area` + 2. 断言 URL 含 `/spd/basic/storehouse/platform/storehouse/area` + 3. 断言左侧「库房信息」树、右侧库区列表、「新 增」与分页可见 +- 自动化断言:规划与 CSV `WH-ZON-001` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +进入库区设置页;不误入探针绑定;无报错遮罩。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-WH-ZON-002】【TP_WH_ZON_005】未登录访问拦截 + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 清除登录态后直接打开 `/spd/basic/storehouse/platform/storehouse/area` + 2. 断言跳转登录或不展示库房/库区业务数据 +- 自动化断言:规划与 CSV `WH-ZON-002` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +未登录不可见库区数据。 + +--- + +### 库区设置 · 左侧库房树 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-ZON-003】【TP_WH_ZON_009】库房树默认有选中项 + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 断言左侧「库房信息」至少 1 个节点且有选中项 + 2. 读取选中节点 `${sampleWhName}` + 3. 断言右侧列表为该库房库区,或空态且「共 0 条」 +- 自动化断言:规划与 CSV `WH-ZON-003` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +树有选中;右侧对应当前库房。 + +--- + +##### 【P0】【TC-UI-WH-ZON-004】【TP_WH_ZON_010】按库房名称过滤树 + +- 前置条件:库房树至少 1 个节点 +- 操作步骤: + 1. 读取当前选中 `${sampleWhName}` 取关键字(可截取前若干字) + 2. 在左侧「库房名称」输入关键字 + 3. 断言可见节点名称均包含该关键字 +- 自动化断言:规划与 CSV `WH-ZON-004` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +树过滤结果与关键字匹配。 + +--- + +##### 【P1】【TC-UI-WH-ZON-005】【TP_WH_ZON_011】清空库房名称恢复全量树 + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 记录过滤前节点数 + 2. 输入关键字过滤后再清空 + 3. 断言节点数恢复为过滤前 +- 自动化断言:规划与 CSV `WH-ZON-005` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +清空后树恢复全量。 + +--- + +##### 【P0】【TC-UI-WH-ZON-006】【TP_WH_ZON_012】切换库房刷新右侧列表 + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 记录当前库房 `${sampleWhName}` 与右侧首行库区编号(若有) + 2. 点选另一库房节点(仅 1 个则 skip) + 3. 断言「共 N 条」可变化;若有行则库区编号/名称属于新库房 +- 自动化断言:规划与 CSV `WH-ZON-006` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +切换库房后右侧列表刷新,不残留上一库房库区。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-WH-ZON-007】【TP_WH_ZON_015】无匹配库房空树(mock反例) + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 库房名称填入 `__NOT_EXIST_ZONE__99999` + 2. 断言树为空或明确无数据,右侧不展示上一库房库区为成功态 +- 自动化断言:规划与 CSV `WH-ZON-007` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +无匹配不报错;不残留上一库房列表当成功。 + +--- + +### 库区设置 · 右侧库区列表与分页 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-ZON-008】【TP_WH_ZON_018】列表核心列完整 + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 断言表头含库区编号、库区名称、库区地址、是否专项库区、操作 + 2. 若有数据行,断言操作列可见「编辑」「人员管理」「货位管理」及「停用」或「启用」 +- 自动化断言:规划与 CSV `WH-ZON-008` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +核心列完整;行内操入口可见。 + +--- + +##### 【P0】【TC-UI-WH-ZON-009】【TP_WH_ZON_019】默认库区以标签展示 + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 在当前库房列表中查找「默认库区」标签 + 2. 断言该标签出现在库区名称旁,不是独立列 + 3. 断言当前库房至多 1 个默认库区标签(无默认则本条只断言不是独立列) +- 自动化断言:规划与 CSV `WH-ZON-009` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +默认库区为名称旁标签;同一库房至多一个。 + +--- + +### 库区设置 · 库区搜索 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-ZON-010】【TP_WH_ZON_029】按库区编号搜索 + +- 前置条件:当前库房至少 1 条库区且编号非空 +- 操作步骤: + 1. 从首行读取 `${sampleZoneNo}` + 2. 在「库区编号/名称」填入该编号并触发搜索 + 3. 断言结果包含该编号行,且均属于当前选中库房 +- 自动化断言:规划与 CSV `WH-ZON-010` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +搜索命中该编号;不串其它库房。 + +--- + +##### 【P0】【TC-UI-WH-ZON-011】【TP_WH_ZON_030】按库区名称搜索 + +- 前置条件:当前库房至少 1 条且名称非空 +- 操作步骤: + 1. 从首行读取 `${sampleZoneName}` 取关键字 + 2. 在「库区编号/名称」填入关键字并搜索 + 3. 断言结果行名称匹配 +- 自动化断言:规划与 CSV `WH-ZON-011` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +名称关键字命中;无未捕获异常。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-WH-ZON-012】【TP_WH_ZON_034】无匹配库区空态(mock反例) + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 「库区编号/名称」填入 `__NOT_EXIST_ZONE__99999` 并搜索 + 2. 断言列表空且分页为 0 +- 自动化断言:规划与 CSV `WH-ZON-012` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +无匹配不报错;总数为 0。 + +--- + +### 库区设置 · 新增库区 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-ZON-013】【TP_WH_ZON_036】打开新增库区弹窗 + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 点击「新 增」 + 2. 断言弹出「新增库区」,可见库区编号、库区名称、库区地址、联系人、联系方式及「取 消」「确 定」 +- 自动化断言:规划与 CSV `WH-ZON-013` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +新增弹窗可见;关键字段与按钮齐全。 + +--- + +##### 【P1】【TC-UI-WH-ZON-014】【TP_WH_ZON_038】新增取消不落库 + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 打开新增,编号填 `AUTO${timestamp}`,名称填任意合法值 + 2. 点击「取 消」 + 3. 按该编号搜索,断言无该记录 +- 自动化断言:规划与 CSV `WH-ZON-014` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +弹窗关闭;列表不出现该编号。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-WH-ZON-015】【TP_WH_ZON_041】必填为空拦截 + +- 前置条件:已进入库区设置页 +- 操作步骤: + 1. 打开新增,库区编号或名称留空后点击「确 定」 + 2. 断言出现必填提示,弹窗仍打开 + 3. 点「取 消」 +- 自动化断言:规划与 CSV `WH-ZON-015` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +确定被拦截;不落库;弹窗不关闭。 + +--- + +### 库区设置 · 编辑库区 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-ZON-016】【TP_WH_ZON_046】打开编辑并回填 + +- 前置条件:当前库房至少 1 条库区 +- 操作步骤: + 1. 点击首行「编辑」 + 2. 断言弹窗标题含「编辑库区信息」 + 3. 断言库区编号、名称与该行 `${sampleZoneNo}`、`${sampleZoneName}` 一致(允许规范化空白) + 4. 点「取 消」 +- 自动化断言:规划与 CSV `WH-ZON-016` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +编辑弹窗可见;回填与列表行一致。 + +--- + +##### 【P1】【TC-UI-WH-ZON-017】【TP_WH_ZON_048】编辑取消不保存 + +- 前置条件:当前库房至少 1 条库区 +- 操作步骤: + 1. 打开首行编辑,记录原名称 + 2. 将名称改为临时值后点「取 消」 + 3. 再次打开编辑,断言名称仍为原值 +- 自动化断言:规划与 CSV `WH-ZON-017` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +取消后列表与回看均为修改前名称。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-WH-ZON-018】【TP_WH_ZON_052】编辑清空必填拦截 + +- 前置条件:当前库房至少 1 条库区 +- 操作步骤: + 1. 打开首行编辑,清空库区名称后点「确 定」 + 2. 断言被拦截并提示,弹窗仍开 + 3. 点「取 消」,断言列表名称未被写成空 +- 自动化断言:规划与 CSV `WH-ZON-018` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +清空必填不能保存;原记录名称保持。 + +--- + +### 库区设置 · 人员管理 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-ZON-019】【TP_WH_ZON_061】打开人员管理后关闭 + +- 前置条件:当前库房至少 1 条库区 +- 操作步骤: + 1. 点击首行「人员管理」 + 2. 断言打开弹层或子页,标题含人员管理或对应当前库区 + 3. 关闭后断言仍在库区设置,当前库房选中保持 +- 自动化断言:规划与 CSV `WH-ZON-019` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +人员管理可打开;关闭后回到当前库房列表。本条不改人员。 + +--- + +### 库区设置 · 货位管理 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-ZON-020】【TP_WH_ZON_066】打开货位管理后关闭 + +- 前置条件:当前库房至少 1 条库区 +- 操作步骤: + 1. 点击首行「货位管理」 + 2. 断言打开弹层或子页,标题含货位且对应当前库区 + 3. 关闭后断言库房选中与库区列表保持 +- 自动化断言:规划与 CSV `WH-ZON-020` 及 `tests/spd/warehouse-zone.spec.ts` 一致 + +###### 预期结果 + +货位管理可打开;关闭后列表状态保持。本条不改货位。 + + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **20** 条(P0=15 / P1=5) | +| 来源 | 从 74 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 真实保存新增/编辑、停用启用持久化、接口失败注入、会话过期、无菜单权限、人员/货位子页字段细节 | +| 单一职责 | 每条只断言一个核心业务结果 | +| 独立性 | 每条独立 `open()`,不复用 BrowserContext;不依赖上一条写结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..d46786c --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,313 @@ +# 基础管理-库房管理-探针绑定 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-库房管理-探针绑定-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-库房管理-探针绑定-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/storehouse/storehouse/probe` +**定位器 YAML**:`locators/spd/warehouse-probe.locator.yaml` +**元素扫描**:`data/scan/warehouse-probe-elements.json`(78 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-WH-002.md` +**版本**:2026-10-20 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/warehouse-probe.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:正例账号 `test_1` 经 `auth.setup` 写入 `storageState`(密码仅存 `.env` 的 `TEST_PASS`);具备「基础设置 → 库房管理 → 探针绑定」权限。反例筛选用 mock。 +**页面约定**:整页渲染,**无报表 iframe**;定位均在 `page` 上。 +**菜单导航**:菜单「基础设置 → 库房管理 → 探针绑定」;建议 `openPath('/spd/basic/storehouse/storehouse/probe')` 并断言 URL。 +**写操作**:绑定货位只打开弹窗后取消,不点确定保存;不改绑定关系。 +**动态取数**:查询后从首行读取 `${sampleProbeId}`、`${sampleHostCode}`、`${sampleBindFlag}`、`${sampleBindCount}`;mock:`__NOT_EXIST_PROBE__99999`。 + +--- + +## 基础设置 · 库房管理 + +### 探针绑定 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-PRB-001】【TP_WH_PRB_001】菜单进入探针绑定页 + +- 前置条件:已进入探针绑定页 +- 操作步骤: + 1. 按菜单「基础设置 → 库房管理 → 探针绑定」进入,或 `openPath` 打开 `/spd/basic/storehouse/storehouse/probe` + 2. 断言 URL 含 `/spd/basic/storehouse/storehouse/probe` + 3. 断言探针ID、主机编码、是否绑定货位、查询、重置、列表与分页可见 +- 自动化断言:规划与 CSV `WH-PRB-001` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +进入探针绑定页;不误入库区设置。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-WH-PRB-002】【TP_WH_PRB_005】未登录访问拦截 + +- 前置条件:已进入探针绑定页 +- 操作步骤: + 1. 清除登录态后直接打开 `/spd/basic/storehouse/storehouse/probe` + 2. 断言跳转登录或不展示探针业务列表 +- 自动化断言:规划与 CSV `WH-PRB-002` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +未登录不可见探针数据。 + +--- + +### 探针绑定 · 筛选与查询 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-PRB-003】【TP_WH_PRB_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入探针绑定页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `WH-PRB-003` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +查询无报错;有数据时「共 N 条」N≥1。 + +--- + +##### 【P0】【TC-UI-WH-PRB-004】【TP_WH_PRB_010】按探针ID查询 + +- 前置条件:默认查询至少 1 条且探针ID非空 +- 操作步骤: + 1. 从首行读取 `${sampleProbeId}` + 2. 探针ID填入该值,点击「查 询」 + 3. 断言结果探针ID匹配,精确命中时行数合理 +- 自动化断言:规划与 CSV `WH-PRB-004` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +探针ID条件生效。 + +--- + +##### 【P0】【TC-UI-WH-PRB-005】【TP_WH_PRB_011】按主机编码查询 + +- 前置条件:默认查询至少 1 条且主机编码非空 +- 操作步骤: + 1. 从首行读取 `${sampleHostCode}` + 2. 主机编码填入该值,点击「查 询」 + 3. 断言结果行「主机编码」与条件一致 +- 自动化断言:规划与 CSV `WH-PRB-005` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +主机编码条件生效;允许多探针共用同一主机。 + +--- + +##### 【P0】【TC-UI-WH-PRB-006】【TP_WH_PRB_012】按是否绑定货位查询 + +- 前置条件:已进入探针绑定页 +- 操作步骤: + 1. 是否绑定货位选择「已绑定」,点击「查 询」 + 2. 断言结果「是否绑定货位」均为已绑定,且「绑定的货位」>0 + 3. 再选「未绑定」查询,断言均为未绑定且数量为 0(无数据则空态) +- 自动化断言:规划与 CSV `WH-PRB-006` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +已绑定数量大于 0;未绑定数量为 0。 + +--- + +##### 【P0】【TC-UI-WH-PRB-007】【TP_WH_PRB_013】探针ID+主机编码组合查询 + +- 前置条件:默认查询至少 1 条 +- 操作步骤: + 1. 填写 `${sampleProbeId}` 与 `${sampleHostCode}` 后点击「查 询」 + 2. 断言结果同时满足已填条件 +- 自动化断言:规划与 CSV `WH-PRB-007` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +组合条件同时生效。 + +--- + +##### 【P1】【TC-UI-WH-PRB-008】【TP_WH_PRB_014】重置恢复默认 + +- 前置条件:已进入探针绑定页 +- 操作步骤: + 1. 填写探针ID后点击「重 置」 + 2. 断言条件恢复默认,列表按默认规则刷新 +- 自动化断言:规划与 CSV `WH-PRB-008` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +条件恢复默认;列表刷新。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-WH-PRB-009】【TP_WH_PRB_018】无匹配空态(mock反例) + +- 前置条件:已进入探针绑定页 +- 操作步骤: + 1. 探针ID填入 `__NOT_EXIST_PROBE__99999`,点击「查 询」 + 2. 断言空态且分页总数为 0 +- 自动化断言:规划与 CSV `WH-PRB-009` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +无匹配不报错;总数为 0。 + +--- + +### 探针绑定 · 列表与分页 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-PRB-010】【TP_WH_PRB_020】列表核心列与数量规则 + +- 前置条件:已进入探针绑定页 +- 操作步骤: + 1. 断言表头含序号、探针ID、主机编码、是否绑定货位、绑定的货位、操作 + 2. 抽样已绑定行:绑定数量为数字且 >0;抽样未绑定行:数量为 0 + 3. 断言「绑定的货位」列不是货位号格式 +- 自动化断言:规划与 CSV `WH-PRB-010` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +核心列完整;数量列是数字不是货位号。 + +--- + +### 探针绑定 · 打开绑定货位弹窗 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-PRB-011】【TP_WH_PRB_030】行内打开货位绑定探针 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 点击首行「绑定货位」 + 2. 断言弹出「货位绑定探针」,可见当前库房/库区/货位查询及货位列表 +- 自动化断言:规划与 CSV `WH-PRB-011` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +弹窗标题正确;筛选与列表可见。 + +--- + +##### 【P1】【TC-UI-WH-PRB-012】【TP_WH_PRB_031】取消关闭不改主列表 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 记录主列表首行绑定数量与当前筛选 + 2. 打开绑定弹窗后点「取 消」 + 3. 断言主列表绑定数量与筛选条件保持 +- 自动化断言:规划与 CSV `WH-PRB-012` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +取消不改绑定关系。 + +--- + +#### 边界验证点 + +##### 【P1】【TC-UI-WH-PRB-013】【TP_WH_PRB_032】未选货位时确定计数为 0 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 打开绑定弹窗,不勾选货位 + 2. 断言底部按钮文案为「确定(0)」或等价 + 3. 点「取 消」,不点确定保存 +- 自动化断言:规划与 CSV `WH-PRB-013` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +未选时计数为 0;本条不保存绑定。 + +--- + +#### 集成验证点 + +##### 【P1】【TC-UI-WH-PRB-014】【TP_WH_PRB_034】弹窗分页独立于主列表 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 记录主列表「共 N 条」 + 2. 打开绑定弹窗,记录弹窗分页「共 M 条」 + 3. 断言不得用 M 断言主列表 N(货位量级可远大于探针条数) + 4. 点「取 消」 +- 自动化断言:规划与 CSV `WH-PRB-014` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +弹窗总条数与主列表探针条数相互独立。 + +--- + +### 探针绑定 · 弹窗筛选货位 + +#### 功能验证点 + +##### 【P0】【TC-UI-WH-PRB-015】【TP_WH_PRB_035】按当前库房筛选货位 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 打开绑定弹窗,记录默认 `${sampleModalWh}` + 2. 点「查 询」 + 3. 断言可见行「当前库房」与所选库房一致(有数据时) + 4. 点「取 消」 +- 自动化断言:规划与 CSV `WH-PRB-015` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +弹窗库房条件生效。 + +--- + +##### 【P1】【TC-UI-WH-PRB-016】【TP_WH_PRB_038】弹窗内重置 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 打开绑定弹窗,填写货位查询关键字后点「重 置」 + 2. 断言货位查询条件恢复,列表按弹窗默认规则刷新 + 3. 点「取 消」 +- 自动化断言:规划与 CSV `WH-PRB-016` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +弹窗重置不影响主列表筛选。 + +--- + +### 探针绑定 · 勾选货位并保存绑定 + +#### 功能验证点 + +##### 【P1】【TC-UI-WH-PRB-017】【TP_WH_PRB_043】勾选后确定计数增加再取消勾选 + +- 前置条件:弹窗货位列表至少 1 条 +- 操作步骤: + 1. 打开绑定弹窗,勾选 1 个货位 + 2. 断言确定按钮计数为 1 + 3. 取消该勾选,断言计数回到 0 + 4. 点「取 消」,不保存 +- 自动化断言:规划与 CSV `WH-PRB-017` 及 `tests/spd/warehouse-probe.spec.ts` 一致 + +###### 预期结果 + +计数随勾选变化;取消勾选不落库。 + + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **17** 条(P0=10 / P1=7) | +| 来源 | 从 49 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 真实勾选确定绑定、换绑/占用规则落库、接口失败、会话过期、无菜单权限 | +| 单一职责 | 每条只断言一个核心业务结果 | +| 独立性 | 每条独立 `open()`,不复用 BrowserContext;不依赖上一条写结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..6f551c9 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,387 @@ +# 基础管理-手术套包设置-基础包维护 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-手术套包设置-基础包维护-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-手术套包设置-基础包维护-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/operationSetting/baseMaintenance` +**定位器 YAML**:`locators/spd/surgery-base-pkg.locator.yaml` +**元素扫描**:`data/scan/surgery-base-pkg-elements.json`(87 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-SURG-001.md` +**版本**:2026-10-13 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/surgery-base-pkg.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效(已通过 `auth.setup` 写入,用例内不重复登录);测试账号具备「基础设置 → 手术套包设置 → 基础包维护」权限。 +**测试数据**:正例复用 `auth.setup` 写入的登录态;反例使用 mock 数据(无匹配关键字 `__NOT_EXIST_BASE__99999`、空必填项),不依赖真实业务数据。 +**页面约定**:整页渲染,**无报表 iframe**;左右分栏(左分类树 + 右列表)。 +**菜单导航**:`page.getByRole('menuitem', { name: '基础包维护', exact: true })`(注意与「术种维护」「标准套包维护」区分)。 +**写操作**:基础包名称使用 `${uniqPkgName}`(建议 `AUTO-BASE-${timestamp}`),包编码使用 `${uniqPkgCode}`;用例结束按环境约定清理,禁止写死生产基础包。 + +**动态取数约定**: +1. 打开页面后从左树读取 `${sampleCategoryName}`,从右侧首行读取 `${sampleProductCode}`、`${sampleProductName}`、`${sampleSpecModel}`、`${sampleMfrsName}`。 +2. 筛选关键字使用样本占位,**禁止写死**产品/注册人名称;mock 无匹配值:`__NOT_EXIST_BASE__99999`。 +3. 左侧分类搜索框 `categorySearchInput`、分类弹窗名称框 `basePkgCategoryName`(`#form_item_kindName`)。 + +--- + +## 基础设置 · 手术套包设置 + +### 基础包维护 · 左侧分类树 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-BASE-001】【TP_SURG_BASE_009】分类名称搜索过滤 + +- 前置条件:左侧树至少 1 个非空分类节点 +- 操作步骤: + 1. 从树读取 `${sampleCategoryName}` + 2. 在 `categorySearchInput` 填入该关键字(即时过滤或回车,以现网为准) + 3. 断言树节点文本均包含该关键字 +- 自动化断言:规划与 CSV `SURG-BASE-001` 一致 + +###### 预期结果 + +过滤后树节点名称与关键字匹配;无未捕获异常。 + +--- + +##### 【P0】【TC-UI-SURG-BASE-002】【TP_SURG_BASE_010】选择分类联动右侧列表 + +- 前置条件:左侧树至少 1 个分类节点,右侧有数据 +- 操作步骤: + 1. 点击 `${sampleCategoryName}` 节点 + 2. 等待右侧列表刷新稳定 + 3. 断言列表首屏加载无报错,分页「共 N 条」与当前分类规模一致 +- 自动化断言:规划与 CSV `SURG-BASE-002` 一致 + +###### 预期结果 + +右侧仅展示该分类下基础包;总数与筛选一致。 + +--- + +##### 【P1】【TC-UI-SURG-BASE-003】【TP_SURG_BASE_012】打开新增分类弹窗 + +- 前置条件:已进入基础包维护页 +- 操作步骤: + 1. 点击「新增分类」 + 2. 断言弹窗可见,含分类名称输入(`basePkgCategoryName`)与「取 消」「确 认」 + 3. 点击「取 消」关闭 +- 自动化断言:规划与 CSV `SURG-BASE-003` 一致 + +###### 预期结果 + +新增分类弹窗字段完整;取消可关闭。 + +--- + +#### 边界验证点 + +##### 【P2】【TC-UI-SURG-BASE-004】【TP_SURG_BASE_013】分类搜索清空恢复全量 + +- 前置条件:已用 `${sampleCategoryName}` 过滤 +- 操作步骤: + 1. 记录过滤后节点数 + 2. 清空 `categorySearchInput` + 3. 断言节点数回到未过滤的默认集合(或不少于过滤后节点数) +- 自动化断言:规划与 CSV `SURG-BASE-004` 一致 + +###### 预期结果 + +清空后分类树恢复默认集合。 + +--- + +### 基础包维护 · 右侧筛选与列表 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-BASE-005】【TP_SURG_BASE_017】默认列出基础包或空态明确 + +- 前置条件:已进入基础包维护页 +- 操作步骤: + 1. 等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `SURG-BASE-005` 一致 + +###### 预期结果 + +加载无报错;有数据时「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-SURG-BASE-006】【TP_SURG_BASE_018】产品名称筛选过滤 + +- 前置条件:默认列表至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleProductName}` + 2. 在 `productName`(`#table_search_form_keyword`)填入该关键字 + 3. 点击「查 询」,断言结果行产品名称均包含该关键字 +- 自动化断言:规划与 CSV `SURG-BASE-006` 一致 + +###### 预期结果 + +过滤结果与关键字匹配;「共 N 条」随之更新。 + +--- + +##### 【P0】【TC-UI-SURG-BASE-007】【TP_SURG_BASE_019】规格型号筛选过滤 + +- 前置条件:默认列表至少 1 条数据且含规格型号 +- 操作步骤: + 1. 从首行读取 `${sampleSpecModel}` + 2. 在 `specModel`(`#table_search_form_goodsSpec`)填入该关键字 + 3. 点击「查 询」,断言结果行规格型号均包含该关键字 +- 自动化断言:规划与 CSV `SURG-BASE-007` 一致 + +###### 预期结果 + +按规格型号过滤生效。 + +--- + +##### 【P0】【TC-UI-SURG-BASE-008】【TP_SURG_BASE_020】器械注册人筛选过滤 + +- 前置条件:默认列表至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleMfrsName}` + 2. 在 `manufacturer`(`#table_search_form_goodsMfrsName`)填入该关键字 + 3. 点击「查 询」,断言结果行器械注册人均包含该关键字 +- 自动化断言:规划与 CSV `SURG-BASE-008` 一致 + +###### 预期结果 + +按器械注册人过滤生效。 + +--- + +##### 【P0】【TC-UI-SURG-BASE-009】【TP_SURG_BASE_022】列表核心列完整 + +- 前置条件:默认列表至少 1 条数据 +- 操作步骤: + 1. 断言表头包含序号、产品编号、产品名称、规格型号、器械注册人、单位、产地、耗材类型、操作 +- 自动化断言:规划与 CSV `SURG-BASE-009` 一致 + +###### 预期结果 + +核心列均可见,操作列含「编 辑」「移 除」。 + +--- + +##### 【P1】【TC-UI-SURG-BASE-010】【TP_SURG_BASE_023】查询与重置 + +- 前置条件:已用关键字过滤 +- 操作步骤: + 1. 点击「重 置」 + 2. 断言筛选输入框被清空、列表恢复默认集合 +- 自动化断言:规划与 CSV `SURG-BASE-010` 一致 + +###### 预期结果 + +重置清空筛选并恢复默认列表。 + +--- + +##### 【P1】【TC-UI-SURG-BASE-011】【TP_SURG_BASE_024】分页与页大小切换 + +- 前置条件:默认列表总数大于单页行数上限 +- 操作步骤: + 1. 切换每页条数(如 50 → 100) + 2. 断言单页行数不超过所选值,分页总数不变 +- 自动化断言:规划与 CSV `SURG-BASE-011` 一致 + +###### 预期结果 + +页大小切换生效;总数保持稳定。 + +--- + +#### 边界验证点 + +##### 【P2】【TC-UI-SURG-BASE-012】【TP_SURG_BASE_026】首页上一页不可用 + +- 前置条件:已进入第 1 页 +- 操作步骤: + 1. 断言「上一页」置灰或点击无效果 +- 自动化断言:规划与 CSV `SURG-BASE-012` 一致 + +###### 预期结果 + +第 1 页无法回到上一页。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-SURG-BASE-013】【TP_SURG_BASE_028】搜索无匹配空态 + +- 前置条件:已进入基础包维护页 +- 操作步骤: + 1. 在 `productName` 输入不存在关键字 `__NOT_EXIST_BASE__99999` + 2. 点击「查 询」 + 3. 断言「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `SURG-BASE-013` 一致 + +###### 预期结果 + +无匹配时空态明确,不白屏。 + +--- + +### 基础包维护 · 新增基础包 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-BASE-014】【TP_SURG_BASE_030】打开新增基础包弹窗 + +- 前置条件:已进入基础包维护页 +- 操作步骤: + 1. 点击「新增基础包」 + 2. 断言弹窗可见,含基础包名称(`basePkgName`)、所属分类(`basePkgCategory`)、包编码(`basePkgCode`)、备注(`remark`) + 3. 断言含「取 消」「确 认」 +- 自动化断言:规划与 CSV `SURG-BASE-014` 一致 + +###### 预期结果 + +新增基础包弹窗字段完整(名称/所属分类/包编码/备注)。 + +--- + +##### 【P0】【TC-UI-SURG-BASE-015】【TP_SURG_BASE_034】新增取消不落库 + +- 前置条件:已进入基础包维护页 +- 操作步骤: + 1. 记录当前分页总数 `${sampleTotal}` + 2. 打开新增,填写名称 `${uniqPkgName}` 后点「取 消」 + 3. 断言列表总数不变,且无该名称行 +- 自动化断言:规划与 CSV `SURG-BASE-015` 一致 + +###### 预期结果 + +取消后不新增基础包。 + +--- + +##### 【P0】【TC-UI-SURG-BASE-016】【TP_SURG_BASE_031】合法必填新增成功 + +- 前置条件:允许写基础包;名称 `${uniqPkgName}`、编码 `${uniqPkgCode}` +- 操作步骤: + 1. 打开新增,填写名称、所属分类、包编码 + 2. 点击「确 认」 + 3. 断言弹窗关闭,列表出现该名称行且字段一致 + 4. 后置:按环境约定清理该基础包,避免污染主数据 +- 自动化断言:规划与 CSV `SURG-BASE-016` 一致;环境禁止写则 skip + +###### 预期结果 + +新增成功且列表可见;用例结束清理,不留生产脏数据。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-SURG-BASE-017】【TP_SURG_BASE_037】基础包名称为空拦截 + +- 前置条件:已打开新增基础包弹窗 +- 操作步骤: + 1. 名称留空,补齐所属分类与包编码后点「确 认」 + 2. 断言必填提示,弹窗仍在,列表无新行 +- 自动化断言:规划与 CSV `SURG-BASE-017` 一致 + +###### 预期结果 + +确定被拦截;不落库。 + +--- + +##### 【P0】【TC-UI-SURG-BASE-018】【TP_SURG_BASE_038】所属分类为空拦截 + +- 前置条件:已打开新增基础包弹窗 +- 操作步骤: + 1. 填写名称与包编码,不选所属分类,点「确 认」 + 2. 断言必填提示,弹窗仍在 +- 自动化断言:规划与 CSV `SURG-BASE-018` 一致 + +###### 预期结果 + +确定被拦截;不落库。 + +--- + +##### 【P1】【TC-UI-SURG-BASE-019】【TP_SURG_BASE_040】重复点击确定防重 + +- 前置条件:已打开新增弹窗并填写合法数据 +- 操作步骤: + 1. 快速连点「确 认」两次 + 2. 断言只产生一条基础包(或无重复名称/编码行) +- 自动化断言:规划与 CSV `SURG-BASE-019` 一致;环境禁止写则 skip + +###### 预期结果 + +连点只落一条;无重复脏数据。 + +--- + +### 基础包维护 · 行内操作(编辑 / 移除) + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-BASE-020】【TP_SURG_BASE_043】行内编辑回填 + +- 前置条件:列表至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleProductCode}` + 2. 点击行内「编 辑」 + 3. 断言编辑弹窗可见且回填当前行数据 + 4. 点击「取 消」关闭 +- 自动化断言:规划与 CSV `SURG-BASE-020` 一致 + +###### 预期结果 + +编辑弹窗回填正确;取消不影响数据。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-SURG-BASE-021】【TP_SURG_BASE_046】移除二次确认防误删 + +- 前置条件:列表至少 1 条数据 +- 操作步骤: + 1. 记录当前总数为 `${sampleTotal}` + 2. 点击首行「移 除」 + 3. 断言出现确认框,点击「取 消」 + 4. 断言总数仍为 `${sampleTotal}` +- 自动化断言:规划与 CSV `SURG-BASE-021` 一致 + +###### 预期结果 + +移除有二次确认;取消不删除。 + +--- + +#### 集成验证点 + +##### 【P1】【TC-UI-SURG-BASE-022】【TP_SURG_BASE_042】新增基础包可在标准套包被引用 + +- 前置条件:已新增 `${uniqPkgName}` 且未清理 +- 操作步骤: + 1. 进入「标准套包维护」,点击「添加基础包」 + 2. 在弹窗搜索框输入 `${uniqPkgName}` + 3. 断言可检索到该基础包并可勾选 + 4. 后置:清理该基础包 +- 自动化断言:规划与 CSV `SURG-BASE-022` 一致;环境禁止写则 skip + +###### 预期结果 + +新增基础包在标准套包可选;跨页数据一致。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **22** 条(P0=14 / P1=6 / P2=2) | +| 来源 | 从 49 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 未登录权限矩阵、分类增删主数据联动、编辑保存落库、被套包引用时移除策略、列表接口异常注入 | +| 数据隔离 | 新增必须用唯一名称/编码并清理;编辑/移除不改生产主数据 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..5e57054 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,438 @@ +# 基础管理-手术套包设置-术种维护 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-手术套包设置-术种维护-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-手术套包设置-术种维护-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/operationSetting/typeMaintenance` +**定位器 YAML**:`locators/spd/surgery-oper-type.locator.yaml` +**元素扫描**:`data/scan/surgery-oper-type-elements.json`(69 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-SURG-002.md` +**版本**:2026-10-13 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/surgery-oper-type.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效(已通过 `auth.setup` 写入,用例内不重复登录);测试账号具备「基础设置 → 手术套包设置 → 术种维护」权限。 +**测试数据**:正例复用 `auth.setup` 写入的登录态;反例使用 mock 数据(无匹配关键字 `__NOT_EXIST_OPER__99999`、空必填项),不依赖真实业务数据。 +**页面约定**:整页渲染,**无报表 iframe**;左右分栏(左术种分类树 + 右筛选/状态 Tab/列表)。 +**菜单导航**:`page.getByRole('menuitem', { name: '术种维护', exact: true })`(注意与「基础包维护」「标准套包维护」区分)。 +**写操作**:术种名称使用 `${uniqOperName}`(建议 `AUTO-OPER-${timestamp}`);停用为写操作,用例结束按环境约定恢复/清理,禁止写死生产术种。 + +**动态取数约定**: +1. 打开页面后从左树读取 `${sampleOperCategoryName}`,从右侧首行读取 `${sampleOperName}`、`${sampleDiseaseName}`、`${sampleIcd10Code}`、`${sampleIcd9Code}`。 +2. 筛选关键字使用样本占位,**禁止写死**术种/病种名称;mock 无匹配值:`__NOT_EXIST_OPER__99999`。 +3. 状态 Tab 定位:`tab`(全部)/「停用」/「未建分类」,切换后断言括号计数与列表规模一致。 +4. 分类搜索框 `operTypeCategoryInput`;分类维护弹窗名称框 `operTypeCategoryName`(`#basic_kindName`)。 + +--- + +## 基础设置 · 手术套包设置 + +### 术种维护 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-TYPE-001】【TP_SURG_TYPE_001】菜单进入术种维护页 + +- 前置条件:已登录且具备手术套包设置权限 +- 操作步骤: + 1. 按菜单「基础设置 → 手术套包设置 → 术种维护」进入 + 2. 断言 URL 含 `/spd/basic/operationSetting/typeMaintenance` + 3. 断言左侧分类树(`operTypeCategoryInput`)、右侧筛选区、状态 Tab、列表与分页可见 +- 自动化断言:规划与 CSV `SURG-TYPE-001` 及 `tests/spd/surgery-oper-type.spec.ts` 一致 + +###### 预期结果 + +进入术种维护页;不误入基础包维护/标准套包维护;左右分栏渲染完整。 + +--- + +##### 【P1】【TC-UI-SURG-TYPE-002】【TP_SURG_TYPE_003】同组页签切换后返回 + +- 前置条件:已进入术种维护页 +- 操作步骤: + 1. 点击页内 Tab 「标准套包维护」,等待 URL 切换 + 2. 返回点击 Tab 「术种维护」 + 3. 断言 URL 回到 `typeMaintenance`,左树与状态 Tab 重新渲染 +- 自动化断言:规划与 CSV `SURG-TYPE-002` 一致 + +###### 预期结果 + +三页签互切正常;返回后本页数据恢复。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-SURG-TYPE-003】【TP_SURG_TYPE_005】未登录访问拦截 + +- 前置条件:`test.use({ storageState: { cookies: [], origins: [] } })` 无登录态 +- 操作步骤: + 1. 直接访问 `/spd/basic/operationSetting/typeMaintenance` + 2. 断言跳转登录页或不展示术种列表数据 +- 自动化断言:规划与 CSV `SURG-TYPE-003` 一致 + +###### 预期结果 + +未登录被拦截,不泄露术种数据。 + +--- + +### 术种维护 · 左侧术种分类树 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-TYPE-004】【TP_SURG_TYPE_009】术种分类名称搜索过滤 + +- 前置条件:左侧树至少 1 个非空分类节点 +- 操作步骤: + 1. 从树读取 `${sampleOperCategoryName}` + 2. 在 `operTypeCategoryInput` 填入该关键字(即时过滤或回车,以现网为准) + 3. 断言树节点文本均包含该关键字 +- 自动化断言:规划与 CSV `SURG-TYPE-004` 一致 + +###### 预期结果 + +过滤后树节点名称与关键字匹配;无未捕获异常。 + +--- + +##### 【P0】【TC-UI-SURG-TYPE-005】【TP_SURG_TYPE_010】选择分类联动右侧列表 + +- 前置条件:左侧树至少 1 个分类节点,右侧有数据 +- 操作步骤: + 1. 点击 `${sampleOperCategoryName}` 节点 + 2. 等待右侧列表刷新稳定 + 3. 断言列表加载无报错,分页「共 N 条」与当前分类规模一致 +- 自动化断言:规划与 CSV `SURG-TYPE-005` 一致 + +###### 预期结果 + +右侧仅展示该分类下术种;总数与筛选一致。 + +--- + +##### 【P1】【TC-UI-SURG-TYPE-006】【TP_SURG_TYPE_011】分类树计数与下钻 + +- 前置条件:至少 1 个带计数的分类节点 +- 操作步骤: + 1. 读取某分类节点括号内计数 `${nodeCount}` + 2. 点击该节点,断言右侧「共 N 条」与 `${nodeCount}` 一致 +- 自动化断言:规划与 CSV `SURG-TYPE-006` 一致 + +###### 预期结果 + +分类计数与该分类下术种条数一致。 + +--- + +##### 【P1】【TC-UI-SURG-TYPE-007】【TP_SURG_TYPE_013】打开分类维护弹窗 + +- 前置条件:已进入术种维护页 +- 操作步骤: + 1. 点击左侧「新增」 + 2. 断言弹窗可见,含分类名称输入(`operTypeCategoryName`)与「取 消」「保 存」 + 3. 点击「取 消」关闭 +- 自动化断言:规划与 CSV `SURG-TYPE-007` 一致 + +###### 预期结果 + +分类维护弹窗字段完整;取消可关闭。 + +--- + +#### 边界验证点 + +##### 【P2】【TC-UI-SURG-TYPE-008】【TP_SURG_TYPE_014】分类搜索清空恢复全量 + +- 前置条件:已用 `${sampleOperCategoryName}` 过滤 +- 操作步骤: + 1. 记录过滤后节点数 + 2. 清空 `operTypeCategoryInput` + 3. 断言节点数回到未过滤的默认集合 +- 自动化断言:规划与 CSV `SURG-TYPE-008` 一致 + +###### 预期结果 + +清空后分类树恢复默认集合。 + +--- + +### 术种维护 · 筛选与状态 Tab + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-TYPE-009】【TP_SURG_TYPE_018】术种名称筛选过滤 + +- 前置条件:默认列表至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleOperName}` + 2. 在 `operationName`(`#table_search_form_operationName`)填入该关键字 + 3. 点击「查 询」,断言结果行术种名称均包含该关键字 +- 自动化断言:规划与 CSV `SURG-TYPE-009` 一致 + +###### 预期结果 + +过滤结果与关键字匹配;「共 N 条」随之更新。 + +--- + +##### 【P0】【TC-UI-SURG-TYPE-010】【TP_SURG_TYPE_019】病种名称筛选过滤 + +- 前置条件:默认列表至少 1 条数据且含病种名称 +- 操作步骤: + 1. 从首行读取 `${sampleDiseaseName}` + 2. 在 `diseaseName`(`#table_search_form_disease`)填入该关键字 + 3. 点击「查 询」,断言结果行病种名称均包含该关键字 +- 自动化断言:规划与 CSV `SURG-TYPE-010` 一致 + +###### 预期结果 + +按病种名称过滤生效。 + +--- + +##### 【P0】【TC-UI-SURG-TYPE-011】【TP_SURG_TYPE_020】状态 Tab 切换与计数一致 + +- 前置条件:已进入术种维护页 +- 操作步骤: + 1. 读取「全部」Tab 括号计数 `${totalCount}` + 2. 依次点击「停用」「未建分类」「全部」 + 3. 每次断言列表条数与「共 N 条」和该 Tab 括号计数一致 +- 自动化断言:规划与 CSV `SURG-TYPE-011` 一致 + +###### 预期结果 + +Tab 切换后列表仅展示对应状态;括号计数与结果规模一致。 + +--- + +##### 【P1】【TC-UI-SURG-TYPE-012】【TP_SURG_TYPE_022】左树 + 筛选 + Tab 组合过滤 + +- 前置条件:左侧至少 1 个分类节点、右侧至少 1 条数据 +- 操作步骤: + 1. 选择 `${sampleOperCategoryName}` 节点 + 2. 填入术种名称关键字并点「查 询」 + 3. 切到「全部」Tab,断言结果为三者交集且无未捕获异常 +- 自动化断言:规划与 CSV `SURG-TYPE-012` 一致 + +###### 预期结果 + +三条件叠加后结果正确,总数一致。 + +--- + +##### 【P1】【TC-UI-SURG-TYPE-013】【TP_SURG_TYPE_021】重置清空筛选 + +- 前置条件:已用关键字过滤 +- 操作步骤: + 1. 点击「重 置」 + 2. 断言筛选输入框被清空、列表恢复默认集合 +- 自动化断言:规划与 CSV `SURG-TYPE-013` 一致 + +###### 预期结果 + +重置清空筛选并恢复默认列表。 + +--- + +#### 边界验证点 + +##### 【P2】【TC-UI-SURG-TYPE-014】【TP_SURG_TYPE_023】无匹配组合空态 + +- 前置条件:已进入术种维护页 +- 操作步骤: + 1. 在 `operationName` 输入不存在关键字 `__NOT_EXIST_OPER__99999` + 2. 点击「查 询」 + 3. 断言「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `SURG-TYPE-014` 一致 + +###### 预期结果 + +无匹配时空态明确,不白屏。 + +--- + +### 术种维护 · 列表与编码展示 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-TYPE-015】【TP_SURG_TYPE_027】列表核心列完整 + +- 前置条件:默认列表至少 1 条数据 +- 操作步骤: + 1. 断言表头包含序号、术种名称、病种名称、诊断编码(ICD-10)、诊断名称(ICD-10)、操作编码(ICD-9-CM-3)、操作名称(ICD-9-CM-3)、操作 +- 自动化断言:规划与 CSV `SURG-TYPE-015` 一致 + +###### 预期结果 + +核心列均可见,操作列含「编 辑」「停用/启用」。 + +--- + +##### 【P1】【TC-UI-SURG-TYPE-016】【TP_SURG_TYPE_028】ICD-10 诊断编码展示完整 + +- 前置条件:列表中至少 1 条含诊断编码的数据 +- 操作步骤: + 1. 读取含 ICD-10 编码的行 `${sampleIcd10Code}` + 2. 断言编码文本完整(如 `A02.000x010`)不截断、无乱码 +- 自动化断言:规划与 CSV `SURG-TYPE-016` 一致 + +###### 预期结果 + +ICD-10 编码完整展示。 + +--- + +##### 【P2】【TC-UI-SURG-TYPE-017】【TP_SURG_TYPE_029】ICD-9-CM-3 操作编码展示完整 + +- 前置条件:列表中至少 1 条含操作编码的数据 +- 操作步骤: + 1. 读取含操作编码的行 `${sampleIcd9Code}` + 2. 断言操作编码(如 `00.0200`)与操作名称(如 `心脏治疗性超声`)对应展示无乱码 +- 自动化断言:规划与 CSV `SURG-TYPE-017` 一致 + +###### 预期结果 + +ICD-9-CM-3 编码与操作名称对应正确。 + +--- + +##### 【P1】【TC-UI-SURG-TYPE-018】【TP_SURG_TYPE_030】分页与页大小切换 + +- 前置条件:默认列表总数大于单页行数上限 +- 操作步骤: + 1. 切换每页条数(如 50 → 100) + 2. 断言单页行数不超过所选值,分页总数不变 +- 自动化断言:规划与 CSV `SURG-TYPE-018` 一致 + +###### 预期结果 + +页大小切换生效;总数保持稳定。 + +--- + +#### 边界验证点 + +##### 【P2】【TC-UI-SURG-TYPE-019】【TP_SURG_TYPE_032】首页上一页不可用 + +- 前置条件:已进入第 1 页 +- 操作步骤: + 1. 断言「上一页」置灰或点击无效果 +- 自动化断言:规划与 CSV `SURG-TYPE-019` 一致 + +###### 预期结果 + +第 1 页无法回到上一页。 + +--- + +### 术种维护 · 新增术种 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-TYPE-020】【TP_SURG_TYPE_035】打开新增术种入口 + +- 前置条件:已进入术种维护页 +- 操作步骤: + 1. 点击「新增术种」 + 2. 断言新增弹窗/页面可见,含术种名称、病种名称、分类与编码相关字段 + 3. 断言含「取 消」「保 存」 +- 自动化断言:规划与 CSV `SURG-TYPE-020` 一致 + +###### 预期结果 + +新增术种入口字段完整。 + +--- + +##### 【P0】【TC-UI-SURG-TYPE-021】【TP_SURG_TYPE_038】新增取消不落库 + +- 前置条件:已进入术种维护页 +- 操作步骤: + 1. 记录当前分页总数 `${sampleTotal}` + 2. 打开新增,填写名称 `${uniqOperName}` 后点「取 消」 + 3. 断言列表总数不变,且无该名称行 +- 自动化断言:规划与 CSV `SURG-TYPE-021` 一致 + +###### 预期结果 + +取消后不新增术种。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-SURG-TYPE-022】【TP_SURG_TYPE_040】术种名称为空拦截 + +- 前置条件:已打开新增术种弹窗 +- 操作步骤: + 1. 名称留空,点「保 存」 + 2. 断言必填提示,弹窗仍在,列表无新行 +- 自动化断言:规划与 CSV `SURG-TYPE-022` 一致 + +###### 预期结果 + +保存被拦截;不落库。 + +--- + +### 术种维护 · 行内操作(编辑 / 停用) + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-TYPE-023】【TP_SURG_TYPE_045】行内编辑回填 + +- 前置条件:列表至少 1 条数据 +- 操作步骤: + 1. 点击首行「编 辑」 + 2. 断言编辑弹窗可见且回填当前术种数据 + 3. 点击「取 消」关闭 +- 自动化断言:规划与 CSV `SURG-TYPE-023` 一致 + +###### 预期结果 + +编辑弹窗回填正确;取消不影响数据。 + +--- + +##### 【P1】【TC-UI-SURG-TYPE-024】【TP_SURG_TYPE_046】停用状态流转 + +- 前置条件:列表中存在启用状态术种(优先选 `${uniqOperName}` 自建数据) +- 操作步骤: + 1. 记录「停用」Tab 当前计数 `${disabledCount}` + 2. 点击目标行「停用」,若有确认框则点「确 定」 + 3. 断言「停用」Tab 计数 +1 + 4. 后置:对该术种执行「启用」恢复 +- 自动化断言:规划与 CSV `SURG-TYPE-024` 一致;环境禁止写则 skip + +###### 预期结果 + +术种状态变为停用;「停用」计数 +1;结束恢复原状态。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-SURG-TYPE-025】【TP_SURG_TYPE_049】停用二次确认防误操作 + +- 前置条件:列表中存在启用状态术种 +- 操作步骤: + 1. 记录「停用」Tab 当前计数 + 2. 点击首行「停用」 + 3. 若出现确认框则点「取 消」 + 4. 断言计数未变化 +- 自动化断言:规划与 CSV `SURG-TYPE-025` 一致 + +###### 预期结果 + +停用有二次确认;取消不改状态。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **25** 条(P0=13 / P1=8 / P2=4) | +| 来源 | 从 53 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 未登录权限矩阵、分类新增落库、新增术种保存落库、ICD 字典一致性、停用后下游不可选联调 | +| 数据隔离 | 新增/停用使用唯一名称并恢复/清理;不改生产术种状态 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..d121bdc --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,496 @@ +# 基础管理-手术套包设置-标准套包维护 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-手术套包设置-标准套包维护-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-手术套包设置-标准套包维护-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/operationSetting/standardMaintenance` +**定位器 YAML**:`locators/spd/surgery-std-pkg.locator.yaml` +**元素扫描**:`data/scan/surgery-std-pkg-elements.json`(61 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-SURG-003.md` +**版本**:2026-10-13 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/surgery-std-pkg.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:已执行 `auth.setup`,`storageState` 有效(已通过 `auth.setup` 写入,用例内不重复登录);测试账号具备「基础设置 → 手术套包设置 → 标准套包维护」权限。 +**测试数据**:正例复用 `auth.setup` 写入的登录态;反例使用 mock 数据(无匹配关键字 `__NOT_EXIST_STD__99999`、空必填项),不依赖真实业务数据。 +**页面约定**:整页渲染,**无报表 iframe**;左右分栏(左术种分类树 + 右标准套包构成列表);含两级嵌套弹窗。 +**菜单导航**:`page.getByRole('menuitem', { name: '标准套包维护', exact: true })`(注意与「基础包维护」「术种维护」区分)。 +**写操作**:涉及新增/编辑/移除构成项,用例结束按环境约定恢复/清理,禁止写死生产套包。 + +**动态取数约定**: +1. 打开页面后从左树读取 `${sampleOperCategoryName}`,从右侧首行读取 `${samplePkgGroupName}`、`${sampleProductCode}`、`${sampleProductName}`、`${sampleSpecModel}`、`${sampleMfrsName}`。 +2. 筛选关键字使用样本占位,**禁止写死**产品/注册人名称;mock 无匹配值:`__NOT_EXIST_STD__99999`。 +3. 构成列表按「包名称」分组,操作前需先**展开目标包**(`expandFilterBtn` / 包名称展开节点);断言时避免全量断言。 +4. 弹窗:添加基础包 `addBasePkgSearchInput`(`请输入分类/基础包名称`)与数量步进器;添加散件 `stdPkgTreeSearchInput`(`请输入术种分类名称、术种名称、标准套包名称`)、应拣明细统计耗材品种数/耗材总数。 + +--- + +## 基础设置 · 手术套包设置 + +### 标准套包维护 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-STD-001】【TP_SURG_STD_001】菜单进入标准套包维护页 + +- 前置条件:已登录且具备手术套包设置权限 +- 操作步骤: + 1. 按菜单「基础设置 → 手术套包设置 → 标准套包维护」进入 + 2. 断言 URL 含 `/spd/basic/operationSetting/standardMaintenance` + 3. 断言左侧分类树、右侧筛选区、构成列表与分页可见 +- 自动化断言:规划与 CSV `SURG-STD-001` 及 `tests/spd/surgery-std-pkg.spec.ts` 一致 + +###### 预期结果 + +进入标准套包维护页;不误入基础包维护/术种维护;左右分栏渲染完整。 + +--- + +##### 【P1】【TC-UI-SURG-STD-002】【TP_SURG_STD_003】同组页签切换后返回 + +- 前置条件:已进入标准套包维护页 +- 操作步骤: + 1. 点击页内 Tab 「术种维护」,等待 URL 切换 + 2. 返回点击 Tab 「标准套包维护」 + 3. 断言 URL 回到 `standardMaintenance`,左树与构成列表重新渲染 +- 自动化断言:规划与 CSV `SURG-STD-002` 一致 + +###### 预期结果 + +三页签互切正常;返回后本页数据恢复。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-SURG-STD-003】【TP_SURG_STD_005】未登录访问拦截 + +- 前置条件:`test.use({ storageState: { cookies: [], origins: [] } })` 无登录态 +- 操作步骤: + 1. 直接访问 `/spd/basic/operationSetting/standardMaintenance` + 2. 断言跳转登录页或不展示套包列表数据 +- 自动化断言:规划与 CSV `SURG-STD-003` 一致 + +###### 预期结果 + +未登录被拦截,不泄露套包数据。 + +--- + +### 标准套包维护 · 左侧术种分类树 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-STD-004】【TP_SURG_STD_009】术种分类名称搜索过滤 + +- 前置条件:左侧树至少 1 个非空分类节点 +- 操作步骤: + 1. 从树读取 `${sampleOperCategoryName}` + 2. 在分类搜索框填入该关键字(即时过滤或回车,以现网为准) + 3. 断言树节点文本均包含该关键字 +- 自动化断言:规划与 CSV `SURG-STD-004` 一致 + +###### 预期结果 + +过滤后树节点名称与关键字匹配;无未捕获异常。 + +--- + +##### 【P0】【TC-UI-SURG-STD-005】【TP_SURG_STD_010】选择分类联动构成列表 + +- 前置条件:左侧树至少 1 个分类节点,右侧有数据 +- 操作步骤: + 1. 点击 `${sampleOperCategoryName}` 节点 + 2. 等待右侧构成列表刷新稳定 + 3. 断言列表加载无报错,分页「共 N 条」与当前分类规模一致 +- 自动化断言:规划与 CSV `SURG-STD-005` 一致 + +###### 预期结果 + +右侧仅展示该分类下标准套包;总数与筛选一致。 + +--- + +##### 【P2】【TC-UI-SURG-STD-006】【TP_SURG_STD_013】分类搜索清空恢复全量 + +- 前置条件:已用 `${sampleOperCategoryName}` 过滤 +- 操作步骤: + 1. 记录过滤后节点数 + 2. 清空分类搜索框 + 3. 断言节点数回到未过滤的默认集合 +- 自动化断言:规划与 CSV `SURG-STD-006` 一致 + +###### 预期结果 + +清空后分类树恢复默认集合。 + +--- + +### 标准套包维护 · 筛选与构成列表 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-STD-007】【TP_SURG_STD_017】默认展示标准套包构成 + +- 前置条件:已进入标准套包维护页 +- 操作步骤: + 1. 等待列表稳定 + 2. 断言按「包名称」分组出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `SURG-STD-007` 一致 + +###### 预期结果 + +加载无报错;有数据时「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-SURG-STD-008】【TP_SURG_STD_018】产品名称筛选过滤 + +- 前置条件:默认列表至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleProductName}` + 2. 在 `productName`(`#table_search_form_keyword`)填入该关键字 + 3. 点击「查 询」,断言结果行产品名称均包含该关键字 +- 自动化断言:规划与 CSV `SURG-STD-008` 一致 + +###### 预期结果 + +过滤结果与关键字匹配;「共 N 条」随之更新。 + +--- + +##### 【P1】【TC-UI-SURG-STD-009】【TP_SURG_STD_019】规格型号筛选过滤 + +- 前置条件:默认列表至少 1 条数据且含规格型号 +- 操作步骤: + 1. 从首行读取 `${sampleSpecModel}` + 2. 在 `specModel`(`#table_search_form_goodsSpec`)填入该关键字 + 3. 点击「查 询」,断言结果行规格型号均包含该关键字 +- 自动化断言:规划与 CSV `SURG-STD-009` 一致 + +###### 预期结果 + +按规格型号过滤生效。 + +--- + +##### 【P1】【TC-UI-SURG-STD-010】【TP_SURG_STD_020】器械注册人筛选过滤 + +- 前置条件:默认列表至少 1 条数据 +- 操作步骤: + 1. 从首行读取 `${sampleMfrsName}` + 2. 在 `manufacturer`(`#table_search_form_goodsMfrsName`)填入该关键字 + 3. 点击「查 询」,断言结果行器械注册人均包含该关键字 +- 自动化断言:规划与 CSV `SURG-STD-010` 一致 + +###### 预期结果 + +按器械注册人过滤生效。 + +--- + +##### 【P0】【TC-UI-SURG-STD-011】【TP_SURG_STD_022】构成列表核心列完整 + +- 前置条件:默认列表至少 1 条数据且存在分组 +- 操作步骤: + 1. 断言表头/首行包含包名称、包数量/每包含量、产品编号、产品名称、规格型号、器械注册人、单位、产地、耗材类型、操作 +- 自动化断言:规划与 CSV `SURG-STD-011` 一致 + +###### 预期结果 + +核心列均可见,操作列含「编 辑」「移 除」。 + +--- + +##### 【P0】【TC-UI-SURG-STD-012】【TP_SURG_STD_023】包名称展开/收起子项 + +- 前置条件:列表中存在可展开的包分组 +- 操作步骤: + 1. 读取首个包分组名称 `${samplePkgGroupName}` + 2. 点击展开该分组,断言出现子项行 + 3. 再次点击收起,断言子项行隐藏 +- 自动化断言:规划与 CSV `SURG-STD-012` 一致 + +###### 预期结果 + +展开显示子项、收起隐藏子项;分组计数不受影响。 + +--- + +##### 【P1】【TC-UI-SURG-STD-013】【TP_SURG_STD_024】查询与重置 + +- 前置条件:已用关键字过滤 +- 操作步骤: + 1. 点击「重 置」 + 2. 断言筛选输入框被清空、列表恢复默认集合 +- 自动化断言:规划与 CSV `SURG-STD-013` 一致 + +###### 预期结果 + +重置清空筛选并恢复默认列表。 + +--- + +##### 【P1】【TC-UI-SURG-STD-014】【TP_SURG_STD_025】分页与页大小切换 + +- 前置条件:默认列表总数大于单页行数上限 +- 操作步骤: + 1. 切换每页条数(如 50 → 100) + 2. 断言单页行数不超过所选值,分页总数不变 +- 自动化断言:规划与 CSV `SURG-STD-014` 一致 + +###### 预期结果 + +页大小切换生效;总数保持稳定。 + +--- + +#### 边界验证点 + +##### 【P2】【TC-UI-SURG-STD-015】【TP_SURG_STD_027】无匹配组合空态 + +- 前置条件:已进入标准套包维护页 +- 操作步骤: + 1. 在 `productName` 输入不存在关键字 `__NOT_EXIST_STD__99999` + 2. 点击「查 询」 + 3. 断言「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `SURG-STD-015` 一致 + +###### 预期结果 + +无匹配时空态明确,不白屏。 + +--- + +### 标准套包维护 · 添加基础包弹窗 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-STD-016】【TP_SURG_STD_032】打开添加基础包弹窗 + +- 前置条件:已进入标准套包维护页 +- 操作步骤: + 1. 点击「添加基础包」 + 2. 断言弹窗可见,含左侧基础包分类树与右侧基础包明细 + 3. 断言含「取 消」「确定(N)」 +- 自动化断言:规划与 CSV `SURG-STD-016` 一致 + +###### 预期结果 + +添加基础包弹窗字段完整;确定按钮带选择计数。 + +--- + +##### 【P0】【TC-UI-SURG-STD-017】【TP_SURG_STD_033】弹窗内筛选与选择计数联动 + +- 前置条件:已打开添加基础包弹窗 +- 操作步骤: + 1. 在 `addBasePkgSearchInput` 输入基础包关键字 + 2. 勾选 1 个基础包 + 3. 断言「确定(N)」计数由 0 变为 1 +- 自动化断言:规划与 CSV `SURG-STD-017` 一致 + +###### 预期结果 + +筛选生效;选择数与确定按钮计数联动。 + +--- + +##### 【P1】【TC-UI-SURG-STD-018】【TP_SURG_STD_034】弹窗内构成数量可编辑 + +- 前置条件:已勾选至少 1 个基础包 +- 操作步骤: + 1. 定位明细行「构成数量」步进器,点击「Increase Value」 + 2. 断言数量 +1 且合计随之更新 +- 自动化断言:规划与 CSV `SURG-STD-018` 一致 + +###### 预期结果 + +构成数量可编辑;加减生效。 + +--- + +##### 【P0】【TC-UI-SURG-STD-019】【TP_SURG_STD_036】添加基础包取消不落库 + +- 前置条件:已进入标准套包维护页 +- 操作步骤: + 1. 记录当前分页总数 `${sampleTotal}` + 2. 打开添加基础包,勾选 1 个基础包后点「取 消」 + 3. 断言构成列表总数不变 +- 自动化断言:规划与 CSV `SURG-STD-019` 一致 + +###### 预期结果 + +取消后不新增构成行。 + +--- + +#### 边界验证点 + +##### 【P2】【TC-UI-SURG-STD-020】【TP_SURG_STD_037】确定按钮零选禁用 + +- 前置条件:已打开添加基础包弹窗且未选择 +- 操作步骤: + 1. 断言「确定(0)」置灰或点击无效果 +- 自动化断言:规划与 CSV `SURG-STD-020` 一致 + +###### 预期结果 + +未选择时确定不可用。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-SURG-STD-021】【TP_SURG_STD_038】重复添加同一基础包 + +- 前置条件:标准套包已含某基础包(或先添加一个) +- 操作步骤: + 1. 再次打开添加基础包,选择同一基础包并确定 + 2. 断言按产品规则拒绝/合并/累加,不产生重复脏行 +- 自动化断言:规划与 CSV `SURG-STD-021` 一致;环境禁止写则 skip + +###### 预期结果 + +重复添加不产生重复行(策略以现网为准)。 + +--- + +### 标准套包维护 · 添加散件弹窗 + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-STD-022】【TP_SURG_STD_041】打开添加散件弹窗 + +- 前置条件:已进入标准套包维护页 +- 操作步骤: + 1. 点击「添加散件」 + 2. 断言弹窗可见并展示「产品列表」视图,含产品名称/规格型号/器械注册人/耗材类型筛选与分页 +- 自动化断言:规划与 CSV `SURG-STD-022` 一致 + +###### 预期结果 + +添加散件弹窗产品列表视图完整。 + +--- + +##### 【P0】【TC-UI-SURG-STD-023】【TP_SURG_STD_042】产品列表筛选与选择散件 + +- 前置条件:已打开添加散件弹窗 +- 操作步骤: + 1. 在产品名称筛选框输入 `${sampleProductName}` + 2. 勾选首个匹配散件 + 3. 断言散件进入应拣明细,计数同步变化 +- 自动化断言:规划与 CSV `SURG-STD-023` 一致 + +###### 预期结果 + +筛选生效;选中散件进入应拣明细。 + +--- + +##### 【P0】【TC-UI-SURG-STD-024】【TP_SURG_STD_043】散件规格/应拣明细视图 + +- 前置条件:已勾选至少 1 个散件 +- 操作步骤: + 1. 切换到规格明细/应拣明细视图 + 2. 断言展示耗材品种数、耗材总数,以及应拣数量、产品编号/名称/规格/注册人/单位/耗材类型/是否收费列 +- 自动化断言:规划与 CSV `SURG-STD-024` 一致 + +###### 预期结果 + +应拣明细视图字段完整。 + +--- + +##### 【P1】【TC-UI-SURG-STD-025】【TP_SURG_STD_044】明细统计与实际一致 + +- 前置条件:应拣明细含至少 1 个散件 +- 操作步骤: + 1. 读取「耗材品种数」`${itemKindCount}` 与「耗材总数」`${itemTotalCount}` + 2. 断言品种数 = 明细去重产品数;总数 = 应拣数量合计 +- 自动化断言:规划与 CSV `SURG-STD-025` 一致 + +###### 预期结果 + +统计口径与实际所选一致。 + +--- + +##### 【P0】【TC-UI-SURG-STD-026】【TP_SURG_STD_046】添加散件取消不落库 + +- 前置条件:已进入标准套包维护页 +- 操作步骤: + 1. 记录当前分页总数 `${sampleTotal}` + 2. 打开添加散件,勾选散件后点「取 消」 + 3. 断言构成列表总数不变 +- 自动化断言:规划与 CSV `SURG-STD-026` 一致 + +###### 预期结果 + +取消后不新增散件行。 + +--- + +#### 边界验证点 + +##### 【P2】【TC-UI-SURG-STD-027】【TP_SURG_STD_047】万级散件列表首屏可交互 + +- 前置条件:添加散件产品列表总量达万级(示例「共 16299 条」) +- 操作步骤: + 1. 打开添加散件弹窗 + 2. 断言首屏可交互(筛选/分页可用),不卡死 + 3. 避免全量断言,仅校验首行与总数格式 +- 自动化断言:规划与 CSV `SURG-STD-027` 一致 + +###### 预期结果 + +万级列表首屏可用;无超时白屏。 + +--- + +### 标准套包维护 · 行内操作(编辑 / 移除) + +#### 功能验证点 + +##### 【P0】【TC-UI-SURG-STD-028】【TP_SURG_STD_052】行内编辑构成项 + +- 前置条件:构成列表至少 1 条数据(先展开目标包) +- 操作步骤: + 1. 点击子项行「编 辑」 + 2. 断言编辑弹窗/表单可见并回填当前值 + 3. 点击「取 消」关闭 +- 自动化断言:规划与 CSV `SURG-STD-028` 一致 + +###### 预期结果 + +编辑回填正确;取消不影响数据。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-SURG-STD-029】【TP_SURG_STD_055】移除二次确认防误删整包 + +- 前置条件:构成列表至少 1 条数据(先展开目标包) +- 操作步骤: + 1. 记录当前行数 + 2. 点击子项行「移 除」 + 3. 若出现确认框则点「取 消」 + 4. 断言行数未变化 +- 自动化断言:规划与 CSV `SURG-STD-029` 一致 + +###### 预期结果 + +移除有二次确认;取消不删除。 + +--- + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **29** 条(P0=17 / P1=8 / P2=4) | +| 来源 | 从 57 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 未登录权限矩阵、添加基础包/散件保存落库、散件提交接口失败、编辑保存、跨页套包可用性联调、移除整包 | +| 数据隔离 | 新增/编辑/移除使用样本或唯一数据并恢复/清理;不改生产套包构成 | +| 单一职责 | 每条只断言一个核心业务结果 | +| 特殊说明 | 构成列表按包名称分组,操作前先展开目标包;万级散件列表避免全量断言 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..6555adf --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,297 @@ +# 基础管理-流程设置-审批流实例 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-流程设置-审批流实例-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-流程设置-审批流实例-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/flowSet/workflow/flowCase` +**定位器 YAML**:`locators/spd/flow-inst.locator.yaml` +**元素扫描**:`data/scan/flow-inst-elements.json`(62 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-FLOW-002.md` +**版本**:2026-10-20 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/flow-inst.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:正例账号 `test_1` 经 `auth.setup` 写入 `storageState`(密码仅存 `.env` 的 `TEST_PASS`);具备「基础设置 → 流程设置 → 审批流实例」权限。反例筛选用 mock。 +**页面约定**:整页渲染,**无报表 iframe**;定位均在 `page` 上。 +**菜单导航**:菜单「基础设置 → 流程设置 → 审批流实例」;建议 `openPath('/spd/basic/flowSet/workflow/flowCase')`,不要点到审批流程设置。 +**写操作**:本页只读。详情只打开后返回,不改审批人。断言「共 N 条」前先收窄日期。 +**动态取数**:查询后从首行读取 `${sampleOrderNo}`、`${sampleBizType}`、`${sampleStatus}`、`${sampleSummary}`;mock:`__NOT_EXIST_FLOW__99999`。 + +--- + +## 基础设置 · 流程设置 + +### 审批流实例 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-FLOW-INS-001】【TP_FLOW_INS_001】菜单进入审批流实例页 + +- 前置条件:已进入审批流实例页 +- 操作步骤: + 1. 按菜单「基础设置 → 流程设置 → 审批流实例」进入,或 `openPath` 打开 `/spd/basic/flowSet/workflow/flowCase` + 2. 断言 URL 含 `/spd/basic/flowSet/workflow/flowCase` + 3. 断言单据编号、审批状态、发起时间、业务类型、摘要、查询、重置、列表与分页可见 +- 自动化断言:规划与 CSV `FLOW-INS-001` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +进入审批流实例;不误入审批流程设置。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-FLOW-INS-002】【TP_FLOW_INS_005】未登录访问拦截 + +- 前置条件:已进入审批流实例页 +- 操作步骤: + 1. 清除登录态后直接打开 `/spd/basic/flowSet/workflow/flowCase` + 2. 断言跳转登录或不展示审批单据列表 +- 自动化断言:规划与 CSV `FLOW-INS-002` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +未登录不可见审批实例。 + +--- + +### 审批流实例 · 筛选与查询 + +#### 功能验证点 + +##### 【P0】【TC-UI-FLOW-INS-003】【TP_FLOW_INS_009】默认日期区间查询 + +- 前置条件:已进入审批流实例页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行或空态明确,「共 N 条」与结果逻辑一致 +- 自动化断言:规划与 CSV `FLOW-INS-003` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +默认查询无报错;有数据时 N≥1。 + +--- + +##### 【P0】【TC-UI-FLOW-INS-004】【TP_FLOW_INS_010】单据编号精确查询 + +- 前置条件:默认查询至少 1 条且单据编号非空 +- 操作步骤: + 1. 从首行读取 `${sampleOrderNo}` + 2. 单据编号填入完整单号,点击「查 询」 + 3. 断言至多 1 条,且编号、业务类型与该行一致 +- 自动化断言:规划与 CSV `FLOW-INS-004` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +精确单号命中当前行。 + +--- + +##### 【P0】【TC-UI-FLOW-INS-005】【TP_FLOW_INS_011】按审批状态筛选 + +- 前置条件:已进入审批流实例页 +- 操作步骤: + 1. 审批状态下拉选择「审批通过」(无该选项则选列表中已出现的状态) + 2. 点击「查 询」 + 3. 断言结果「审批状态」列与所选一致 +- 自动化断言:规划与 CSV `FLOW-INS-005` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +状态筛选生效。 + +--- + +##### 【P0】【TC-UI-FLOW-INS-006】【TP_FLOW_INS_012】按业务类型筛选 + +- 前置条件:默认查询至少 1 条 +- 操作步骤: + 1. 从首行读取 `${sampleBizType}` + 2. 业务类型选择该值,点击「查 询」 + 3. 断言结果「业务类型」均为该类型 +- 自动化断言:规划与 CSV `FLOW-INS-006` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +业务类型筛选生效。 + +--- + +##### 【P0】【TC-UI-FLOW-INS-007】【TP_FLOW_INS_013】按发起时间区间筛选 + +- 前置条件:已进入审批流实例页 +- 操作步骤: + 1. 记录当前默认起止日期 + 2. 点击「查 询」 + 3. 抽样结果行「发起时间」落在起止日内(含边界) +- 自动化断言:规划与 CSV `FLOW-INS-007` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +发起时间落在区间内。 + +--- + +##### 【P1】【TC-UI-FLOW-INS-008】【TP_FLOW_INS_014】按摘要关键字筛选 + +- 前置条件:已进入审批流实例页 +- 操作步骤: + 1. 找到摘要非 `--` 的行读取 `${sampleSummary}` 关键字;若全为 `--` 则填 mock 关键字 + 2. 点击「查 询」 + 3. 有关键字命中时摘要包含该关键字;mock 无命中时空态且 `--` 行不被命中 +- 自动化断言:规划与 CSV `FLOW-INS-008` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +非空摘要可检索;`--` 不被非空关键字命中。 + +--- + +##### 【P0】【TC-UI-FLOW-INS-009】【TP_FLOW_INS_015】状态+类型组合查询 + +- 前置条件:默认查询至少 1 条 +- 操作步骤: + 1. 填写 `${sampleStatus}` 与 `${sampleBizType}` 后点击「查 询」 + 2. 断言抽样行同时满足全部已填条件 +- 自动化断言:规划与 CSV `FLOW-INS-009` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +组合条件同时生效。 + +--- + +##### 【P1】【TC-UI-FLOW-INS-010】【TP_FLOW_INS_016】重置恢复默认 + +- 前置条件:已进入审批流实例页 +- 操作步骤: + 1. 填写单据编号等条件后点击「重 置」 + 2. 断言日期回到默认区间,其余条件清空,列表刷新 +- 自动化断言:规划与 CSV `FLOW-INS-010` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +重置后日期与其它条件恢复默认。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-FLOW-INS-011】【TP_FLOW_INS_021】无匹配空态(mock反例) + +- 前置条件:已进入审批流实例页 +- 操作步骤: + 1. 单据编号填入 `__NOT_EXIST_FLOW__99999`,点击「查 询」 + 2. 断言空态且分页总数为 0 +- 自动化断言:规划与 CSV `FLOW-INS-011` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +无匹配不报错;总数为 0。 + +--- + +### 审批流实例 · 列表与分页 + +#### 功能验证点 + +##### 【P0】【TC-UI-FLOW-INS-012】【TP_FLOW_INS_024】列表核心列完整 + +- 前置条件:已进入审批流实例页 +- 操作步骤: + 1. 断言表头含序号、单据编号、业务类型、发起人、发起部门、发起时间、摘要、审批状态、操作 + 2. 断言行内可见「详情」 +- 自动化断言:规划与 CSV `FLOW-INS-012` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +核心列完整;详情入口可见。 + +--- + +##### 【P1】【TC-UI-FLOW-INS-013】【TP_FLOW_INS_025】单据编号前缀与业务类型对应 + +- 前置条件:默认查询有数据 +- 操作步骤: + 1. 抽样 `PLAN`/`DB`/`GC`/`BUY` 前缀行(有则断言) + 2. 断言 PLAN→采购计划、DB→二级库调拨、GC→产品信息变更、BUY→请领单(以现网枚举为准,缺前缀 skip 该项) +- 自动化断言:规划与 CSV `FLOW-INS-013` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +前缀与类型对应。 + +--- + +##### 【P1】【TC-UI-FLOW-INS-014】【TP_FLOW_INS_028】分页翻至第 2 页 + +- 前置条件:总条数大于一页,否则 skip +- 操作步骤: + 1. 记录第 1 页首行单据编号 + 2. 翻到第 2 页 + 3. 断言数据更换;筛选条件保持 +- 自动化断言:规划与 CSV `FLOW-INS-014` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +翻页成功;筛选不丢失。 + +--- + +### 审批流实例 · 审批详情 + +#### 功能验证点 + +##### 【P0】【TC-UI-FLOW-INS-015】【TP_FLOW_INS_036】打开详情对应当前行 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 记录首行 `${sampleOrderNo}` + 2. 点击「详情」 + 3. 断言打开该单审批过程,标题或单号含 `${sampleOrderNo}` +- 自动化断言:规划与 CSV `FLOW-INS-015` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +详情单号与当前行一致。 + +--- + +##### 【P1】【TC-UI-FLOW-INS-016】【TP_FLOW_INS_038】关闭详情保留列表状态 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 填写单据编号查询后打开详情 + 2. 点返回/关闭 + 3. 断言筛选条件与列表仍是打开前的查询结果 +- 自动化断言:规划与 CSV `FLOW-INS-016` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +返回后条件与页码保持。 + +--- + +##### 【P1】【TC-UI-FLOW-INS-017】【TP_FLOW_INS_037】详情只读 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 打开首行详情 + 2. 断言无保存审批人/改条件的提交按钮,或此类控件不可用 + 3. 返回列表 +- 自动化断言:规划与 CSV `FLOW-INS-017` 及 `tests/spd/flow-inst.spec.ts` 一致 + +###### 预期结果 + +详情只读;改配置须到审批流程设置。 + + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **17** 条(P0=10 / P1=7) | +| 来源 | 从 41 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 改审批人、接口失败、会话过期、无菜单权限、详情与流程设置节点逐条对照(可另开集成专项) | +| 单一职责 | 每条只断言一个核心业务结果 | +| 独立性 | 每条独立 `open()`,不复用 BrowserContext;不依赖上一条写结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..4f0085f --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,256 @@ +# 基础管理-流程设置-审批流程设置 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-流程设置-审批流程设置-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-流程设置-审批流程设置-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/flowSet/workflow/flowSet` +**定位器 YAML**:`locators/spd/flow-def.locator.yaml` +**元素扫描**:`data/scan/flow-def-elements.json`(43 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-FLOW-001.md` +**版本**:2026-10-20 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/flow-def.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:正例账号 `test_1` 经 `auth.setup` 写入 `storageState`(密码仅存 `.env` 的 `TEST_PASS`);具备「基础设置 → 流程设置 → 审批流程设置」权限。反例筛选用 mock。 +**页面约定**:整页渲染,**无报表 iframe**;定位均在 `page` 上。 +**菜单导航**:菜单「基础设置 → 流程设置 → 审批流程设置」;建议 `openPath('/spd/basic/flowSet/workflow/flowSet')`,与「审批流实例」用 exact 区分。 +**写操作**:禁止点「保 存」提交线上审批路由。新增流程/改节点后必须「重 置」或取消条件配置;必填拦截若需点保存,仅空行拦截成功后立刻重置。 +**动态取数**:从左侧读取 `${sampleBizType}`(如采购计划);自定义行读取 `${sampleFlowName}`、`${sampleCondText}`;mock:`__NOT_EXIST_FLOW__99999`。 + +--- + +## 基础设置 · 流程设置 + +### 审批流程设置 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-FLOW-DEF-001】【TP_FLOW_DEF_001】菜单进入审批流程设置页 + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 按菜单「基础设置 → 流程设置 → 审批流程设置」进入,或 `openPath` 打开 `/spd/basic/flowSet/workflow/flowSet` + 2. 断言 URL 含 `/spd/basic/flowSet/workflow/flowSet` + 3. 断言左侧业务类型、右侧默认审批流与自定义审批流、「保 存」「重 置」可见 +- 自动化断言:规划与 CSV `FLOW-DEF-001` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +进入审批流程设置;不误入审批流实例。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-FLOW-DEF-002】【TP_FLOW_DEF_005】未登录访问拦截 + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 清除登录态后直接打开 `/spd/basic/flowSet/workflow/flowSet` + 2. 断言跳转登录或不展示审批流配置 +- 自动化断言:规划与 CSV `FLOW-DEF-002` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +未登录不可见审批流配置。 + +--- + +### 审批流程设置 · 左侧业务类型 + +#### 功能验证点 + +##### 【P0】【TC-UI-FLOW-DEF-003】【TP_FLOW_DEF_009】业务类型列表可见 + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 断言左侧可见采购计划等类型且有当前高亮项 + 2. 读取 `${sampleBizType}` +- 自动化断言:规划与 CSV `FLOW-DEF-003` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +类型列表可见;当前项高亮。 + +--- + +##### 【P0】【TC-UI-FLOW-DEF-004】【TP_FLOW_DEF_010】按流程名称过滤类型 + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 在「流程名称」输入 `${sampleBizType}` 关键字 + 2. 断言可见类型名称均匹配关键字 +- 自动化断言:规划与 CSV `FLOW-DEF-004` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +类型过滤与关键字匹配。 + +--- + +##### 【P1】【TC-UI-FLOW-DEF-005】【TP_FLOW_DEF_011】清空关键字恢复全量 + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 记录过滤前类型数量 + 2. 输入关键字后再清空 + 3. 断言数量恢复 +- 自动化断言:规划与 CSV `FLOW-DEF-005` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +清空后类型列表恢复全量。 + +--- + +##### 【P0】【TC-UI-FLOW-DEF-006】【TP_FLOW_DEF_012】切换类型刷新右侧配置 + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 记录当前类型默认流节点文案 + 2. 点选另一业务类型(仅 1 个则 skip) + 3. 断言右侧默认流/自定义流已刷新,不残留上一类型节点人员 +- 自动化断言:规划与 CSV `FLOW-DEF-006` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +切换类型后右侧与该类型已保存配置一致。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-FLOW-DEF-007】【TP_FLOW_DEF_015】无匹配类型(mock反例) + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 流程名称填入 `__NOT_EXIST_FLOW__99999` + 2. 断言左侧无匹配或空,右侧不把上一类型配置展示为当前成功配置 +- 自动化断言:规划与 CSV `FLOW-DEF-007` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +无匹配不报错。 + +--- + +### 审批流程设置 · 默认审批流 + +#### 功能验证点 + +##### 【P0】【TC-UI-FLOW-DEF-008】【TP_FLOW_DEF_017】默认审批流节点可见 + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 选中「采购计划」(或当前默认类型) + 2. 断言「默认审批流」区域可见节点卡片,含节点/账号/人员三行信息(按卡片容器取文本) +- 自动化断言:规划与 CSV `FLOW-DEF-008` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +默认流节点结构可见;不点保存。 + +--- + +### 审批流程设置 · 自定义审批流 + +#### 功能验证点 + +##### 【P0】【TC-UI-FLOW-DEF-009】【TP_FLOW_DEF_022】新增流程增加一行后重置 + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 记录当前自定义流行数 + 2. 点击「新增流程」 + 3. 断言行数 +1,新行含流程名称、条件、审批流程、删除 + 4. 点击「重 置」,断言行数回到操作前,不点「保 存」 +- 自动化断言:规划与 CSV `FLOW-DEF-009` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +可新增行;重置放弃未保存行。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-FLOW-DEF-010】【TP_FLOW_DEF_028】空行保存被拦截后重置 + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 点击「新增流程」得到空行 + 2. 点击「保 存」 + 3. 断言出现必填拦截提示,配置未按成功态关闭 + 4. 点击「重 置」,放弃该空行 +- 自动化断言:规划与 CSV `FLOW-DEF-010` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +空名称/条件/节点不能落库;拦截后重置恢复。 + +--- + +### 审批流程设置 · 条件配置 + +#### 功能验证点 + +##### 【P0】【TC-UI-FLOW-DEF-011】【TP_FLOW_DEF_031】打开条件配置后取消 + +- 前置条件:当前类型至少 1 条自定义流 +- 操作步骤: + 1. 若无自定义行则 skip + 2. 点击首条自定义流条件列「编辑」 + 3. 断言打开「条件配置」 + 4. 点「取 消」,断言行内条件文案与打开前一致 +- 自动化断言:规划与 CSV `FLOW-DEF-011` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +条件配置可打开;取消不改行内条件。 + +--- + +### 审批流程设置 · 保存与重置 + +#### 功能验证点 + +##### 【P0】【TC-UI-FLOW-DEF-012】【TP_FLOW_DEF_038】重置放弃未保存改动 + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 点击「新增流程」增加一行 + 2. 点击「重 置」 + 3. 断言自定义流行回到上次保存结果 +- 自动化断言:规划与 CSV `FLOW-DEF-012` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +重置不提交;未保存行消失。 + +--- + +### 审批流程设置 · 左侧业务类型 + +#### 集成验证点 + +##### 【P1】【TC-UI-FLOW-DEF-013】【TP_FLOW_DEF_016】类型与审批流实例业务类型同源 + +- 前置条件:已进入审批流程设置页 +- 操作步骤: + 1. 记录本页左侧类型名称集合(可见范围) + 2. 打开审批流实例,展开「业务类型」下拉 + 3. 断言选项与本页类型同源(允许按权限裁剪,本页有的关键类型如采购计划应能对应) +- 自动化断言:规划与 CSV `FLOW-DEF-013` 及 `tests/spd/flow-def.spec.ts` 一致 + +###### 预期结果 + +两页业务类型同源。 + + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **13** 条(P0=10 / P1=3) | +| 来源 | 从 41 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 真实保存审批链、自定义流持久化、条件保存落库、历史实例快照、接口失败、会话过期 | +| 单一职责 | 每条只断言一个核心业务结果 | +| 独立性 | 每条独立 `open()`,不复用 BrowserContext;不依赖上一条写结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..217c0c7 --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,286 @@ +# 基础管理-用户管理-医共体用户管理 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-用户管理-医共体用户管理-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-用户管理-医共体用户管理-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/user/platform/personCom` +**定位器 YAML**:`locators/spd/basic-user-union.locator.yaml` +**元素扫描**:`data/scan/basic-user-union-elements.json`(221 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-USER-002.md` +**版本**:2026-10-20 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/basic-user-union.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:正例账号 `test_1` 经 `auth.setup` 写入 `storageState`(密码仅存 `.env` 的 `TEST_PASS`);具备「基础设置 → 用户管理 → 医共体用户管理」权限。反例筛选用 mock(`__NOT_EXIST_USER__99999` / `__NOT_EXIST_ROLE__99999`)。 +**页面约定**:整页渲染,**无报表 iframe**;定位均在 `page` 上。 +**菜单导航**:菜单「基础设置 → 用户管理 → 医共体用户管理」;可用 `openPath('/spd/basic/user/platform/personCom')`。 +**写操作**:新增/编辑只打开后取消,不提交保存;授权与改密规则同用户管理,本页不重复真实写操作。禁止写死医院名称。 +**动态取数**:查询后从首行读取 `${sampleLoginName}`、`${sampleUserName}`、`${sampleHospital}`、`${samplePhone}`;禁止写死扫描到的医院名;mock:`__NOT_EXIST_USER__99999`。 + +--- + +## 基础设置 · 用户管理 + +### 医共体用户管理 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-UNI-001】【TP_USER_UNI_001】菜单进入医共体用户管理页 + +- 前置条件:已进入医共体用户管理页 +- 操作步骤: + 1. 按菜单「基础设置 → 用户管理 → 医共体用户管理」进入,或 `openPath` 打开 `/spd/basic/user/platform/personCom` + 2. 断言 URL 含 `/spd/basic/user/platform/personCom` + 3. 断言所属医院筛选、工具栏、列表列头与分页可见 +- 自动化断言:规划与 CSV `USER-UNI-001` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +进入医共体用户管理页;不误入用户管理或角色管理。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-USER-UNI-002】【TP_USER_UNI_005】未登录访问拦截 + +- 前置条件:已进入医共体用户管理页 +- 操作步骤: + 1. 清除登录态后直接打开 `/spd/basic/user/platform/personCom` + 2. 断言跳转登录或不展示用户业务列表 +- 自动化断言:规划与 CSV `USER-UNI-002` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +未登录不可见医共体用户数据。 + +--- + +### 医共体用户管理 · 筛选与查询 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-UNI-003】【TP_USER_UNI_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入医共体用户管理页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行,或空态且分页总数为 0 +- 自动化断言:规划与 CSV `USER-UNI-003` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +查询无报错;有数据时「共 N 条」N≥1。 + +--- + +##### 【P0】【TC-UI-USER-UNI-004】【TP_USER_UNI_010】姓名或登录账号关键字查询 + +- 前置条件:默认查询至少 1 条 +- 操作步骤: + 1. 从首行读取 `${sampleUserName}` 或 `${sampleLoginName}` + 2. 在「姓名」框填入关键字,点击「查 询」 + 3. 断言结果行姓名或登录账号匹配 +- 自动化断言:规划与 CSV `USER-UNI-004` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +查询结果与关键字匹配。 + +--- + +##### 【P0】【TC-UI-USER-UNI-005】【TP_USER_UNI_011】所属医院筛选 + +- 前置条件:已进入医共体用户管理页 +- 操作步骤: + 1. 记录当前默认 `${sampleHospital}` 与分页总数 + 2. 切换「所属医院」为另一可选项(仅 1 家则 skip) + 3. 点击「查 询」 + 4. 断言结果行「所属医院」均为所选值,「共 N 条」可与默认不同 +- 自动化断言:规划与 CSV `USER-UNI-005` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +列表仅该院用户;总数随医院变化。 + +--- + +##### 【P0】【TC-UI-USER-UNI-006】【TP_USER_UNI_013】姓名+医院+联系方式组合查询 + +- 前置条件:默认查询至少 1 条 +- 操作步骤: + 1. 读取 `${sampleUserName}`、`${sampleHospital}`、`${samplePhone}`(电话非法占位如 `1` 时改用姓名+医院) + 2. 填写后点击「查 询」 + 3. 断言结果同时满足已填条件 +- 自动化断言:规划与 CSV `USER-UNI-006` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +组合条件同时生效。 + +--- + +##### 【P1】【TC-UI-USER-UNI-007】【TP_USER_UNI_014】重置恢复默认医院 + +- 前置条件:已进入医共体用户管理页 +- 操作步骤: + 1. 更改所属医院或姓名后点击「重 置」 + 2. 断言所属医院恢复进入页时的默认值,列表刷新 +- 自动化断言:规划与 CSV `USER-UNI-007` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +医院等条件恢复默认。 + +--- + +##### 【P1】【TC-UI-USER-UNI-008】【TP_USER_UNI_015】展开高级筛选 + +- 前置条件:已进入医共体用户管理页 +- 操作步骤: + 1. 点击「高级筛选」 + 2. 断言可见所属医院、联系方式、科室、角色及「筛 选」 +- 自动化断言:规划与 CSV `USER-UNI-008` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +高级筛选含医院维度字段。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-USER-UNI-009】【TP_USER_UNI_021】无匹配空态(mock反例) + +- 前置条件:已进入医共体用户管理页 +- 操作步骤: + 1. 姓名框填入 `__NOT_EXIST_USER__99999`,点击「查 询」 + 2. 断言空态且分页总数为 0 +- 自动化断言:规划与 CSV `USER-UNI-009` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +无匹配不报错;总数为 0。 + +--- + +### 医共体用户管理 · 列表与分页 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-UNI-010】【TP_USER_UNI_024】列表列顺序含所属医院 + +- 前置条件:已进入医共体用户管理页 +- 操作步骤: + 1. 断言列顺序为登录账号、姓名、联系方式、所属医院、所属科室、医院员工编号、角色、科室任职数、操作 + 2. 勿用用户管理页列断言(用户管理无所属医院且姓名在登录账号前) +- 自动化断言:规划与 CSV `USER-UNI-010` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +列顺序与用户管理页不同;含所属医院。 + +--- + +##### 【P1】【TC-UI-USER-UNI-011】【TP_USER_UNI_026】分页翻至第 2 页 + +- 前置条件:总条数大于一页,否则 skip +- 操作步骤: + 1. 记录第 1 页首行登录账号 + 2. 翻到第 2 页 + 3. 断言数据更换且所属医院筛选仍保持 +- 自动化断言:规划与 CSV `USER-UNI-011` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +翻页成功;医院条件不丢失。 + +--- + +#### 集成验证点 + +##### 【P1】【TC-UI-USER-UNI-012】【TP_USER_UNI_033】与用户管理页同一用户字段一致 + +- 前置条件:两页都能查到同一账号,否则 skip +- 操作步骤: + 1. 记录本页首行 `${sampleLoginName}`、姓名、所属科室、角色 + 2. 打开用户管理页,按登录账号查询同一用户 + 3. 断言姓名、角色、所属科室一致;本页仅多「所属医院」 +- 自动化断言:规划与 CSV `USER-UNI-012` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +同一登录账号跨页字段一致。 + +--- + +### 医共体用户管理 · 新增与编辑 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-UNI-013】【TP_USER_UNI_036】打开新增弹窗 + +- 前置条件:已进入医共体用户管理页 +- 操作步骤: + 1. 点击「新 增」 + 2. 断言弹出用户表单,含账号、姓名、联系方式及取消/确定 +- 自动化断言:规划与 CSV `USER-UNI-013` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +新增弹窗可见。 + +--- + +##### 【P1】【TC-UI-USER-UNI-014】【TP_USER_UNI_038】新增取消不落库 + +- 前置条件:已进入医共体用户管理页 +- 操作步骤: + 1. 打开新增,账号填 `${uniqLogin}` 后点「取 消」 + 2. 当前医院下列表查询该账号,断言不存在 +- 自动化断言:规划与 CSV `USER-UNI-014` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +取消后当前医院列表不出现该账号。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-USER-UNI-015】【TP_USER_UNI_046】必填为空拦截 + +- 前置条件:已进入医共体用户管理页 +- 操作步骤: + 1. 打开新增,账号或姓名留空后点「确 定」 + 2. 断言被拦截,弹窗仍打开,不落库 +- 自动化断言:规划与 CSV `USER-UNI-015` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +必填为空不可保存。 + +--- + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-UNI-016】【TP_USER_UNI_039】打开编辑并回填 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 点击首行「编辑」 + 2. 断言弹窗回填与该行登录账号、姓名、所属医院一致 + 3. 点「取 消」 +- 自动化断言:规划与 CSV `USER-UNI-016` 及 `tests/spd/basic-user-union.spec.ts` 一致 + +###### 预期结果 + +编辑回填正确;本条不保存。 + + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **16** 条(P0=10 / P1=6) | +| 来源 | 从 58 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 授权/改密持久化(引用 TP_USER_USR)、真实跨院保存、Excel 导入文件、接口失败、会话过期 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..828563d --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,403 @@ +# 基础管理-用户管理-用户管理 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-用户管理-用户管理-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-用户管理-用户管理-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/user/platform/person` +**定位器 YAML**:`locators/spd/basic-user.locator.yaml` +**元素扫描**:`data/scan/basic-user-elements.json`(219 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-USER-001.md` +**版本**:2026-10-20 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/basic-user.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:正例账号 `test_1` 经 `auth.setup` 写入 `storageState`(密码仅存 `.env` 的 `TEST_PASS`);具备「基础设置 → 用户管理 → 用户管理」权限。反例筛选用 mock(`__NOT_EXIST_USER__99999` / `__NOT_EXIST_ROLE__99999`)。 +**页面约定**:整页渲染,**无报表 iframe**;定位均在 `page` 上。 +**菜单导航**:一级菜单与叶子菜单均名「用户管理」,须点「基础设置 → 用户管理(子菜单)→ 用户管理(叶子)」;建议 `openPath('/spd/basic/user/platform/person')` 并断言 URL,勿误入医共体/角色管理。 +**写操作**:新增/编辑只打开后取消,不提交保存;授权只打开后取消;重置密码只验证确认框后取消,不确认改密。禁止写死生产账号。 +**动态取数**:查询后从首行读取 `${sampleUserName}`、`${sampleLoginName}`、`${sampleDept}`、`${sampleRole}`;科室为 `--` 时换一行;mock:`__NOT_EXIST_USER__99999`;写操作用 `${uniqLogin}`(建议 `auto.usr.${timestamp}`),取消不落库。 + +--- + +## 基础设置 · 用户管理 + +### 用户管理 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-USR-001】【TP_USER_USR_001】菜单进入用户管理页 + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 按菜单「基础设置 → 用户管理(子菜单)→ 用户管理(叶子)」进入,或 `openPath` 打开 `/spd/basic/user/platform/person` + 2. 断言 URL 含 `/spd/basic/user/platform/person` + 3. 断言筛选区、工具栏「新 增」「Excel导入」、列表列头与分页可见 +- 自动化断言:规划与 CSV `USER-USR-001` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +进入用户管理页;不误入医共体用户管理或角色管理;无报错遮罩。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-USER-USR-002】【TP_USER_USR_005】未登录访问拦截 + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 清除登录态后直接打开 `/spd/basic/user/platform/person` + 2. 断言跳转登录或不展示用户业务列表 +- 自动化断言:规划与 CSV `USER-USR-002` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +未登录不可见用户数据。 + +--- + +### 用户管理 · 筛选与查询 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-USR-003】【TP_USER_USR_009】默认查询列表有数据或空态明确 + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 点击「查 询」并等待列表稳定 + 2. 断言出现数据行,或「暂无数据」且分页总数为 0 +- 自动化断言:规划与 CSV `USER-USR-003` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +查询无报错;有数据时分页「共 N 条」N≥1;无数据时空态明确。 + +--- + +##### 【P0】【TC-UI-USER-USR-004】【TP_USER_USR_010】姓名或登录账号关键字查询 + +- 前置条件:默认查询至少 1 条且姓名或登录账号非空 +- 操作步骤: + 1. 从首行读取 `${sampleUserName}` 或 `${sampleLoginName}` 取关键字 + 2. 在「姓名」框(占位「姓名、登录账号」)填入关键字,点击「查 询」 + 3. 断言结果行姓名或登录账号匹配该关键字 +- 自动化断言:规划与 CSV `USER-USR-004` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +查询结果与已填关键字匹配;无未捕获异常。 + +--- + +##### 【P0】【TC-UI-USER-USR-005】【TP_USER_USR_011】科室下拉筛选 + +- 前置条件:列表存在所属科室非空行,否则 skip +- 操作步骤: + 1. 取首条所属科室非 `--` 的行,读取 `${sampleDept}` + 2. 科室下拉选择该值,点击「查 询」 + 3. 断言结果行「所属科室」与所选一致,且不含未分配 `--` 行 +- 自动化断言:规划与 CSV `USER-USR-005` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +所属科室列与筛选条件一致。 + +--- + +##### 【P0】【TC-UI-USER-USR-006】【TP_USER_USR_013】姓名+科室+角色组合查询 + +- 前置条件:默认查询至少 1 条 +- 操作步骤: + 1. 读取 `${sampleUserName}`、`${sampleDept}`、`${sampleRole}`(角色可从该行截断前列名) + 2. 填写姓名、科室、角色后点击「查 询」 + 3. 断言结果同时满足已填条件 +- 自动化断言:规划与 CSV `USER-USR-006` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +组合查询无报错;结果同时满足全部已填条件。 + +--- + +##### 【P1】【TC-UI-USER-USR-007】【TP_USER_USR_014】重置恢复默认 + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 填写姓名等筛选条件后点击「重 置」 + 2. 断言筛选字段恢复默认,列表按默认规则刷新 +- 自动化断言:规划与 CSV `USER-USR-007` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +条件恢复默认;列表刷新。 + +--- + +##### 【P1】【TC-UI-USER-USR-008】【TP_USER_USR_015】展开高级筛选 + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 点击「高级筛选」 + 2. 断言展开后面板可见姓名/科室/角色/状态及「重 置」「筛 选」 +- 自动化断言:规划与 CSV `USER-USR-008` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +高级筛选面板展开;关键字段与筛选按钮可见。 + +--- + +##### 【P0】【TC-UI-USER-USR-009】【TP_USER_USR_016】高级筛选按状态过滤 + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 展开高级筛选,状态下拉选择「启用」或「禁用」 + 2. 点击「筛 选」 + 3. 断言列表仅保留对应状态用户(无匹配则空态且总数为 0) +- 自动化断言:规划与 CSV `USER-USR-009` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +按状态过滤成功;无报错。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-USER-USR-010】【TP_USER_USR_022】无匹配结果空态(mock反例) + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 姓名框填入 `__NOT_EXIST_USER__99999` + 2. 点击「查 询」 + 3. 断言空态或「暂无数据」,分页总数为 0 +- 自动化断言:规划与 CSV `USER-USR-010` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +无匹配时不报错;空态明确;总数为 0。 + +--- + +### 用户管理 · 列表与分页 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-USR-011】【TP_USER_USR_025】列表核心列完整 + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 断言表头含序号、姓名、登录账号、联系方式、所属科室、医院员工编号、角色、科室任职数、操作 + 2. 断言操作列可见「编辑」「授权」「重置密码」 +- 自动化断言:规划与 CSV `USER-USR-011` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +核心列完整;行内三个操入口可见。 + +--- + +##### 【P1】【TC-UI-USER-USR-012】【TP_USER_USR_026】列表不含所属医院列 + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 断言表头不含「所属医院」 +- 自动化断言:规划与 CSV `USER-USR-012` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +本页无所属医院列,与医共体用户管理页区分。 + +--- + +##### 【P1】【TC-UI-USER-USR-013】【TP_USER_USR_031】分页翻至第 2 页 + +- 前置条件:默认查询总条数大于一页,否则 skip +- 操作步骤: + 1. 记录第 1 页首行登录账号 + 2. 点击页码「2」或「下一页」 + 3. 断言页码高亮为 2,首行账号与第 1 页不同 +- 自动化断言:规划与 CSV `USER-USR-013` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +翻页成功;数据更换。 + +--- + +### 用户管理 · 新增用户 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-USR-014】【TP_USER_USR_041】打开新增用户弹窗 + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 点击「新 增」 + 2. 断言弹出用户表单,可见账号、姓名、联系方式、医院员工编号、启用/禁用、「取 消」「确 定」 +- 自动化断言:规划与 CSV `USER-USR-014` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +新增弹窗可见;关键字段与按钮齐全。 + +--- + +##### 【P1】【TC-UI-USER-USR-015】【TP_USER_USR_043】新增取消不落库 + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 打开新增,账号填 `${uniqLogin}`,姓名填任意合法值 + 2. 点击「取 消」 + 3. 按账号查询,断言无该记录 +- 自动化断言:规划与 CSV `USER-USR-015` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +弹窗关闭;列表不出现 `${uniqLogin}`。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-USER-USR-016】【TP_USER_USR_047】必填为空拦截 + +- 前置条件:已进入用户管理页 +- 操作步骤: + 1. 打开新增,账号或姓名留空后点击「确 定」 + 2. 断言出现必填提示,弹窗仍打开 +- 自动化断言:规划与 CSV `USER-USR-016` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +确定被拦截;不落库;弹窗不关闭。 + +--- + +##### 【P0】【TC-UI-USER-USR-017】【TP_USER_USR_048】登录账号重复拦截 + +- 前置条件:默认查询至少 1 条且登录账号非空 +- 操作步骤: + 1. 打开新增,账号填入已存在的 `${sampleLoginName}`,补齐其它必填后点「确 定」 + 2. 断言保存失败并提示,弹窗仍打开 + 3. 查询该账号,断言原用户未被覆盖 +- 自动化断言:规划与 CSV `USER-USR-017` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +重复账号被拒绝;不覆盖原用户。 + +--- + +### 用户管理 · 编辑用户 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-USR-018】【TP_USER_USR_054】打开编辑用户弹窗并回填 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 点击首行「编辑」 + 2. 断言弹窗标题含「编辑用户」 + 3. 断言账号、姓名与该行 `${sampleLoginName}`、`${sampleUserName}` 一致(允许规范化空白) +- 自动化断言:规划与 CSV `USER-USR-018` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +编辑弹窗可见;回填与列表行一致。 + +--- + +##### 【P1】【TC-UI-USER-USR-019】【TP_USER_USR_058】编辑取消不保存 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 打开首行编辑,记录原姓名 + 2. 将姓名改为临时值后点「取 消」 + 3. 再次打开编辑,断言姓名仍为原值 +- 自动化断言:规划与 CSV `USER-USR-019` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +取消后列表与回看均为修改前姓名。 + +--- + +#### 边界验证点 + +##### 【P1】【TC-UI-USER-USR-020】【TP_USER_USR_061】编辑时账号只读 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 打开首行编辑 + 2. 断言登录账号不可改(disabled/readonly 或无法输入) +- 自动化断言:规划与 CSV `USER-USR-020` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +编辑态登录账号只读。 + +--- + +#### 功能验证点 + +##### 【P1】【TC-UI-USER-USR-021】【TP_USER_USR_056】编辑弹窗可见签名图片 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 打开首行编辑 + 2. 断言可见「签名图片」区域(有图则展示,无图则上传入口可见) + 3. 点「取 消」,不保存 +- 自动化断言:规划与 CSV `USER-USR-021` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +签名图片控件可见;本条不上传、不保存。 + +--- + +### 用户管理 · 授权 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-USR-022】【TP_USER_USR_068】打开授权后取消不生效 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 点击首行「授权」 + 2. 断言打开授权层(弹窗或权限树),可见可勾选菜单/功能 + 3. 如可勾选则改一处勾选后点取消/关闭 + 4. 再次打开同一用户授权,断言勾选与操作前一致 +- 自动化断言:规划与 CSV `USER-USR-022` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +授权层可打开;取消后勾选不落库。 + +--- + +### 用户管理 · 重置密码 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-USR-023】【TP_USER_USR_075】行内重置密码确认后取消 + +- 前置条件:列表至少 1 条 +- 操作步骤: + 1. 点击首行「重置密码」 + 2. 断言出现确认框或密码表单,含取消/关闭 + 3. 点取消/关闭 + 4. 断言仍停留本页且该行仍在 +- 自动化断言:规划与 CSV `USER-USR-023` 及 `tests/spd/basic-user.spec.ts` 一致 + +###### 预期结果 + +重置需确认;取消后不改密。本条不点确定,避免污染账号。 + + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **23** 条(P0=14 / P1=9) | +| 来源 | 从 81 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 真实保存用户、禁用后登录、真实改密、Excel 导入文件、接口失败注入、会话过期、无菜单权限 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" new file mode 100644 index 0000000..d79711a --- /dev/null +++ "b/testcases/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-UI\350\207\252\345\212\250\345\214\226\346\265\213\350\257\225\347\224\250\344\276\213-01.md" @@ -0,0 +1,261 @@ +# 基础管理-用户管理-角色管理 UI 自动化测试用例 + +**来源测试点**:`testpoint/基础管理-用户管理-角色管理-测试点分析-01.md` +**执行 CSV**:`testcase/基础管理-用户管理-角色管理-UI自动化测试用例-01.csv` +**页面 URL**:`/spd/basic/user/platform/role` +**定位器 YAML**:`locators/spd/basic-role.locator.yaml` +**元素扫描**:`data/scan/basic-role-elements.json`(1170 项) +**关联规范**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-USER-003.md` +**版本**:2026-10-20 +**自动化框架**:Playwright + TypeScript(规划 `tests/spd/basic-role.spec.ts`,依赖 `auth.setup` 登录态) + +**通用前置**:正例账号 `test_1` 经 `auth.setup` 写入 `storageState`(密码仅存 `.env` 的 `TEST_PASS`);具备「基础设置 → 用户管理 → 角色管理」权限。反例筛选用 mock(`__NOT_EXIST_USER__99999` / `__NOT_EXIST_ROLE__99999`)。 +**页面约定**:整页渲染,**无报表 iframe**;定位均在 `page` 上。 +**菜单导航**:菜单「基础设置 → 用户管理 → 角色管理」;本页左右分栏、无独立分页。 +**写操作**:权限树勾选后点「刷 新」恢复,不点「保 存」;删除只验证确认框后取消;新增只打开后取消。禁止删除预置角色或真实改名。 +**动态取数**:左栏读取 `${sampleRoleName}`(优先非预置自定义角色;预置用「医院管理员」若存在);权限树按「模块名 + 菜单名」定位,根为「SPD内网系统PC端」;mock:`__NOT_EXIST_ROLE__99999`。 + +--- + +## 基础设置 · 用户管理 + +### 角色管理 · 页面进入 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-ROL-001】【TP_USER_ROL_001】菜单进入角色管理页 + +- 前置条件:已进入角色管理页 +- 操作步骤: + 1. 按菜单「基础设置 → 用户管理 → 角色管理」进入,或 `openPath` 打开 `/spd/basic/user/platform/role` + 2. 断言 URL 含 `/spd/basic/user/platform/role` + 3. 断言左侧角色列表、右侧菜单权限树、「保 存」「刷 新」可见,无独立分页 +- 自动化断言:规划与 CSV `USER-ROL-001` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +进入角色管理;左右分栏渲染完成;不误入用户管理页。 + +--- + +#### 异常验证点 + +##### 【P0】【TC-UI-USER-ROL-002】【TP_USER_ROL_005】未登录访问拦截 + +- 前置条件:已进入角色管理页 +- 操作步骤: + 1. 清除登录态后直接打开 `/spd/basic/user/platform/role` + 2. 断言跳转登录或不展示角色/权限树业务数据 +- 自动化断言:规划与 CSV `USER-ROL-002` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +未登录不可见角色配置。 + +--- + +### 角色管理 · 左侧角色列表 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-ROL-003】【TP_USER_ROL_009】角色列表加载并可点选 + +- 前置条件:已进入角色管理页 +- 操作步骤: + 1. 断言左栏列出角色名称(如医院管理员、护士等) + 2. 点击首个 `${sampleRoleName}` + 3. 断言该行处于选中态 +- 自动化断言:规划与 CSV `USER-ROL-003` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +左栏有角色且可点选。 + +--- + +##### 【P0】【TC-UI-USER-ROL-004】【TP_USER_ROL_010】角色名称搜索过滤 + +- 前置条件:左栏至少 1 个角色 +- 操作步骤: + 1. 在角色名称框输入 `${sampleRoleName}` 的部分关键字 + 2. 断言左栏收敛为匹配项(即时或回车,以现网为准) +- 自动化断言:规划与 CSV `USER-ROL-004` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +搜索结果角色名包含关键字。 + +--- + +##### 【P0】【TC-UI-USER-ROL-005】【TP_USER_ROL_011】选中角色刷新右侧权限树 + +- 前置条件:左栏至少 2 个角色 +- 操作步骤: + 1. 依次点选两个不同角色 + 2. 断言右侧勾选随当前角色变化,无上一角色残留 +- 自动化断言:规划与 CSV `USER-ROL-005` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +权限树与当前角色配置一致。 + +--- + +##### 【P1】【TC-UI-USER-ROL-006】【TP_USER_ROL_012】清空搜索恢复全量 + +- 前置条件:已进入角色管理页 +- 操作步骤: + 1. 先按关键字过滤,再清空搜索 + 2. 断言左栏恢复未过滤的完整角色列表 +- 自动化断言:规划与 CSV `USER-ROL-006` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +清空后角色列表恢复全量。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-USER-ROL-007】【TP_USER_ROL_016】搜索无匹配空态(mock反例) + +- 前置条件:已进入角色管理页 +- 操作步骤: + 1. 角色名称框填入 `__NOT_EXIST_ROLE__99999` + 2. 断言左栏空态明确 +- 自动化断言:规划与 CSV `USER-ROL-007` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +无匹配不报错;空态明确。 + +--- + +### 角色管理 · 右侧菜单权限树 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-ROL-008】【TP_USER_ROL_018】权限树按应用与模块展开 + +- 前置条件:已进入角色管理页 +- 操作步骤: + 1. 选中任一角色 + 2. 断言右侧树根为「SPD内网系统PC端」,可见基础设置/运维管理/厂商管理/产品管理等模块 + 3. 展开「基础设置」,断言可见子菜单(如医院资料) +- 自动化断言:规划与 CSV `USER-ROL-008` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +权限树层级可见;节点可展开。 + +--- + +##### 【P0】【TC-UI-USER-ROL-009】【TP_USER_ROL_019】勾选父节点联动子节点 + +- 前置条件:已进入角色管理页 +- 操作步骤: + 1. 选中一自定义或可编辑角色(预置被锁则 skip) + 2. 记录「厂商管理」父节点原勾选 + 3. 勾选「厂商管理」父节点 + 4. 断言其子菜单/功能全部勾选 + 5. 点击「刷 新」,不点「保 存」 +- 自动化断言:规划与 CSV `USER-ROL-009` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +父节点勾选联动子节点;本条不保存。 + +--- + +##### 【P1】【TC-UI-USER-ROL-010】【TP_USER_ROL_023】刷新恢复未保存勾选 + +- 前置条件:已进入角色管理页 +- 操作步骤: + 1. 改一处勾选后点「刷 新」且未保存 + 2. 断言树恢复为进入该角色时的已保存状态 +- 自动化断言:规划与 CSV `USER-ROL-010` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +刷新丢弃未保存勾选。 + +--- + +### 角色管理 · 角色增删改 + +#### 功能验证点 + +##### 【P0】【TC-UI-USER-ROL-011】【TP_USER_ROL_034】删除需确认且取消保留 + +- 前置条件:已进入角色管理页 +- 操作步骤: + 1. 点一自定义角色的删除入口(预置角色无删除则改点自定义;都没有则 skip) + 2. 断言出现确认框,含取消/确定 + 3. 点「取 消」 + 4. 断言该角色仍在左栏 +- 自动化断言:规划与 CSV `USER-ROL-011` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +删除需二次确认;取消后角色仍在。本条不点确定。 + +--- + +##### 【P1】【TC-UI-USER-ROL-012】【TP_USER_ROL_032】新增角色入口打开后取消 + +- 前置条件:已进入角色管理页 +- 操作步骤: + 1. 通过新增或「角色」工具按钮打开新增表单(扫描未打开则 skip) + 2. 不填或填写后点取消/关闭 + 3. 断言左栏角色数量不增加 +- 自动化断言:规划与 CSV `USER-ROL-012` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +新增入口可达;取消不落库。 + +--- + +#### 异常验证点 + +##### 【P1】【TC-UI-USER-ROL-013】【TP_USER_ROL_041】预置角色不可删除 + +- 前置条件:左栏存在可识别的预置角色,否则 skip +- 操作步骤: + 1. 在左栏定位「医院管理员」(或其它预置角色,以现网为准) + 2. 断言删除入口不可见;若可见则确定后被拒绝,角色仍在左栏 + 3. 若弹出确认则点取消,避免误删 +- 自动化断言:规划与 CSV `USER-ROL-013` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +预置角色不可删除。 + +--- + +### 角色管理 · 左侧角色列表 + +#### 集成验证点 + +##### 【P1】【TC-UI-USER-ROL-014】【TP_USER_ROL_017】角色名与用户管理角色下拉同源 + +- 前置条件:已进入角色管理页 +- 操作步骤: + 1. 记录本页左栏角色名称集合 + 2. 打开用户管理,展开「角色」筛选项 + 3. 断言两边名称集合一致(允许按权限裁剪,本页有的名称用户页应能对应到) +- 自动化断言:规划与 CSV `USER-ROL-014` 及 `tests/spd/basic-role.spec.ts` 一致 + +###### 预期结果 + +角色主数据跨页一致,不串名。 + + +## 质量与覆盖说明 + +| 项 | 说明 | +|----|------| +| 用例数 | **14** 条(P0=8 / P1=6) | +| 来源 | 从 45 条测试点选取可稳定自动化的主路径与关键反向 | +| 未纳入自动化 | 真实保存权限树、真实新增/改名/删除角色、未保存切角色(待评审)、角色授权后换账号登录、接口失败 | +| 单一职责 | 每条只断言一个核心业务结果 | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..eef546d --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,103 @@ +# 基础管理-准入管理-产品准入单列表 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-ADMIT-004.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productOrder/list` +**版本 / 定稿日期**:2026-09-22 +**元素扫描**:`data/scan/admit-product-list-elements.json`(77 项) +**定位器**:`locators/spd/admit-product-list.locator.yaml` + +--- + +## 基础设置 · 准入管理 · 产品准入单列表 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_ADMIT_PLS_001**(高)|菜单路径可达产品准入单列表|验证要点:登录后通过「基础设置 → 准入管理 → 产品准入单列表」进入,不误入物料准入单列表或产品准入申请|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选区、状态 Tab 与列表列头可见|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_003**(中)|同组菜单可切换|验证要点:切换同组其它准入菜单后 URL 对应,再切回本页正确|关联需求:UI-SPEC-SPD-ADMIT-004 + +#### 边界验证点 + +- **TP_ADMIT_PLS_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开产品准入单列表 URL,页面正常渲染|关联需求:UI-SPEC-SPD-ADMIT-004 + +#### 异常验证点 + +- **TP_ADMIT_PLS_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录页,不展示单据|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_006**(中)|登录态过期|验证要点:会话失效后查询或打开详情被拦截并提示重新登录|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-ADMIT-004 + +#### 集成验证点 + +- **TP_ADMIT_PLS_008**(中)|顶栏院区与单据范围一致|验证要点:列表供应商与产品范围和顶栏院区上下文不矛盾|关联需求:UI-SPEC-SPD-ADMIT-004 + +--- + +### 筛选、状态与列表 + +#### 功能验证点 + +- **TP_ADMIT_PLS_009**(高)|默认制单日期查询|验证要点:点击「查询」后列表有数据或空态明确,分页「共 N 条」与结果一致|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_010**(高)|制单日期、进院供应商、二级供应商、耗材类型、注册证号、器械注册人筛选|验证要点:分别按上述条件查询,结果与条件匹配|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_011**(高)|组合查询、重置与收起|验证要点:多条件同时生效;「重置」恢复默认;收起后再展开值保留|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_012**(高)|状态 Tab|验证要点:全部、审批中、已通过、已驳回切换后,列表「状态」与 Tab 一致,Tab 计数与分页总数一致|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_013**(高)|列表核心列|验证要点:展示序号、准入单号、进院供应商、二级供应商、产品名称、注册证号、耗材类型、产品条数、状态、备注、附件、操作「详情」|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_014**(中)|分页、列设置与翻页保持条件|验证要点:翻页和每页条数正确;列设置生效;翻页不丢失筛选与当前 Tab|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_015**(中)|附件列计数|验证要点:附件列展示的个数与详情中该单附件数量一致;为 0 时显示 0 个|关联需求:UI-SPEC-SPD-ADMIT-004 + +#### 边界验证点 + +- **TP_ADMIT_PLS_016**(中)|制单日期起止同一天|验证要点:起止同一天仅返回该日单据|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_017**(中)|零计数 Tab 与首页末页|验证要点:计数为 0 的 Tab 为空态;第 1 页上一页、末页下一页不可用|关联需求:UI-SPEC-SPD-ADMIT-004 + +#### 异常验证点 + +- **TP_ADMIT_PLS_018**(中)|日期开始晚于结束|验证要点:拒绝查询或自动纠正并提示|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_019**(中)|查询失败与无匹配空态|验证要点:接口失败有提示且不白屏;无匹配时总数为 0|关联需求:UI-SPEC-SPD-ADMIT-004 + +#### 集成验证点 + +- **TP_ADMIT_PLS_020**(高)|单据来源于产品准入申请|验证要点:在产品准入申请生成的申请单,可按准入单号在本列表查到,状态一致|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_021**(中)|与物料准入单列表数据隔离|验证要点:同一单号不会同时以错误类型出现在物料准入单列表,两列表筛选结果不串单|关联需求:UI-SPEC-SPD-ADMIT-004 + +--- + +### 单据详情 + +#### 功能验证点 + +- **TP_ADMIT_PLS_022**(高)|打开详情|验证要点:「详情」进入产品准入单详情,含审批进程、单据日志、返回|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_023**(高)|详情产品条数一致|验证要点:详情明细行数与列表「产品条数」一致|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_024**(中)|详情内产品搜索|验证要点:按产品编码或名称过滤后仅保留匹配明细|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_025**(中)|审批进程、单据日志与明细查看|验证要点:「审批进程」「单据日志」可打开;明细「查看」展示产品信息|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_026**(高)|返回保持列表条件|验证要点:「返回」回到产品准入单列表,原筛选与 Tab 保持|关联需求:UI-SPEC-SPD-ADMIT-004 + +#### 边界验证点 + +- **TP_ADMIT_PLS_027**(中)|仅 1 条明细|验证要点:产品条数为 1 时详情只展示 1 行|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_028**(低)|详情搜索无匹配|验证要点:无匹配时明细空态明确,单据头仍在|关联需求:UI-SPEC-SPD-ADMIT-004 + +#### 异常验证点 + +- **TP_ADMIT_PLS_029**(中)|详情加载失败|验证要点:详情失败有提示,不白屏|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_030**(中)|已驳回单据不可通过|验证要点:已驳回单「通过」不可用或被拒绝,状态保持已驳回|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_031**(中)|已通过单据不可再次通过|验证要点:已通过单据详情中「通过」不可用或被拒绝,状态保持已通过|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_032**(中)|取消通过不改变状态|验证要点:若「通过」有确认框,点取消后单据状态与 Tab 计数不变|关联需求:UI-SPEC-SPD-ADMIT-004 + +#### 集成验证点 + +- **TP_ADMIT_PLS_033**(高)|审批通过后列表与 Tab 计数更新|验证要点:允许审批的单据「通过」成功后,列表状态为已通过,已通过 Tab 计数增加|关联需求:UI-SPEC-SPD-ADMIT-004 +- **TP_ADMIT_PLS_034**(中)|详情产品与产品准入申请主数据一致|验证要点:抽样核对产品名称、注册证号与产品准入申请中的记录一致|关联需求:UI-SPEC-SPD-ADMIT-004 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 导航、筛选、状态 Tab、列表附件列、详情、审批、日志、通过确认与取消、已通过/已驳回不可再通过;共 **34** 条 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 与物料准入单列表共用的查询与详情规则,本页保留产品单列(备注、附件)及与物料单隔离;编号前缀 `TP_ADMIT_PLS` | +| 待评审项 | ① 列表附件个数点击是否直接打开附件 ② 审批中「通过」的角色范围 ③ 本页详情扫描未出现独立「导出」,若后续页面补充导出需追加测试点 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 JSON `data/scan/admit-product-list-elements.json` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..61ae2e5 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\272\247\345\223\201\345\207\206\345\205\245\347\224\263\350\257\267-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,173 @@ +# 基础管理-准入管理-产品准入申请 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-ADMIT-002.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productApply/list` +**版本 / 定稿日期**:2026-09-22 +**元素扫描**:`data/scan/admit-product-apply-elements.json`(467 项) +**定位器**:`locators/spd/admit-product-apply.locator.yaml` + +--- + +## 基础设置 · 准入管理 · 产品准入申请 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_ADMIT_PAP_001**(高)|菜单路径可达产品准入申请页|验证要点:登录后通过「基础设置 → 准入管理 → 产品准入申请」进入目标页,不误入物料准入管理或产品准入单列表|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选区、状态 Tab、工具栏与列表列头可见|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_003**(中)|同组准入管理菜单可切换|验证要点:切换同组其它五个菜单后 URL 与内容对应,再切回产品准入申请正确|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 边界验证点 + +- **TP_ADMIT_PAP_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开产品准入申请 URL,页面正常渲染|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 异常验证点 + +- **TP_ADMIT_PAP_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录页,不展示申请列表|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_006**(中)|登录态过期访问|验证要点:会话失效后查询或新增被拦截并提示重新登录|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_007**(中)|无产品准入申请权限|验证要点:无权限账号不可见菜单或访问 URL 被拒绝|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 集成验证点 + +- **TP_ADMIT_PAP_008**(中)|顶栏院区与列表数据范围一致|验证要点:顶栏院区与列表进院供应商、产品数据范围不矛盾|关联需求:UI-SPEC-SPD-ADMIT-002 + +--- + +### 筛选与查询 + +#### 功能验证点 + +- **TP_ADMIT_PAP_009**(高)|默认新增日期查询|验证要点:点击「查询」后列表有数据或空态明确,分页「共 N 条」与结果一致|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_010**(高)|新增日期区间查询|验证要点:结果落在所选新增日期区间内|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_011**(高)|进院供应商与耗材类型筛选|验证要点:按进院供应商、耗材类型查询,结果与条件匹配|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_012**(中)|产品名称、规格型号、注册证号、器械注册人筛选|验证要点:按上述字段查询,对应列匹配|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_013**(高)|多条件组合查询|验证要点:多个已填条件同时生效|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_014**(中)|重置与收起展开|验证要点:「重置」恢复默认;「收起」后再展开,已填值保留|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 边界验证点 + +- **TP_ADMIT_PAP_015**(中)|新增日期起止同一天|验证要点:起止同一天查询成功,仅返回该日数据|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_016**(低)|文本条件为空|验证要点:名称等留空不报错,按其余条件查询|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 异常验证点 + +- **TP_ADMIT_PAP_017**(中)|日期开始晚于结束|验证要点:拒绝查询或自动纠正,并给出提示|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_018**(中)|查询失败与空结果|验证要点:接口失败有错误提示且不白屏;无匹配时空态明确、总数为 0|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 集成验证点 + +- **TP_ADMIT_PAP_019**(中)|筛选与分页总数一致|验证要点:分页「共 N 条」等于当前筛选全量|关联需求:UI-SPEC-SPD-ADMIT-002 + +--- + +### 状态 Tab 与列表 + +#### 功能验证点 + +- **TP_ADMIT_PAP_020**(高)|状态 Tab 切换|验证要点:全部、待申请、审批中、已通过、已驳回、已作废、生效失败、已生效切换后,列表状态与 Tab 一致|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_021**(高)|Tab 计数与列表总数一致|验证要点:Tab 括号数量与该状态分页总数一致|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_022**(高)|列表核心列完整|验证要点:展示序号、准入单号、产品编号、产品名称、规格型号、注册规格、注册型号、器械注册人、单位、操作(详情/日志,待申请另有编辑)|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_023**(中)|分页、列设置与翻页保持条件|验证要点:翻页与每页条数正确;列设置生效;翻页不丢失筛选和当前 Tab|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_024**(中)|跳至指定页|验证要点:分页「跳至」输入合法页码后进入该页,列表数据与页码一致|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 边界验证点 + +- **TP_ADMIT_PAP_025**(中)|零计数 Tab 与首页末页|验证要点:计数为 0 的 Tab 为空态;第 1 页上一页、末页下一页不可用|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 异常验证点 + +- **TP_ADMIT_PAP_026**(低)|未勾选时批量操作不可用|验证要点:未勾选行时生成申请单等依赖勾选的操作不可用或提示先选择|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 集成验证点 + +- **TP_ADMIT_PAP_027**(中)|状态 Tab 与筛选同时生效|验证要点:筛选后再切 Tab,结果同时满足两者|关联需求:UI-SPEC-SPD-ADMIT-002 + +--- + +### 新增产品与 Excel 导入 + +#### 功能验证点 + +- **TP_ADMIT_PAP_028**(高)|打开新增产品|验证要点:点击「新增产品」进入新增页,含产品名称、器械注册人、规格型号、注册证号、分类、是否医疗器械等字段,以及返回|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_029**(高)|必填合法保存成功|验证要点:填写必填项后保存,返回列表出现新记录,关键字段与提交一致|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_030**(中)|新增取消不落库|验证要点:填写后返回且不保存,列表不新增该产品|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_031**(高)|Excel 导入入口|验证要点:点击「Excel导入」打开导入层,可选择模板文件或取消,取消不产生新记录|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_032**(高)|合法 Excel 导入成功|验证要点:按模板导入合法数据后,列表出现对应产品,条数与文件有效行一致|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 边界验证点 + +- **TP_ADMIT_PAP_033**(中)|名称与注册证号达到允许长度|验证要点:字段取允许最大长度可保存,超出则被拒绝或按规则截断并提示|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_034**(中)|导入仅 1 行|验证要点:模板仅 1 条合法数据时成功导入 1 条|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 异常验证点 + +- **TP_ADMIT_PAP_035**(高)|产品名称为空保存拦截|验证要点:新增页产品名称为空时保存被拦截,弹层不关闭,列表不新增|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_036**(高)|其它带必填标识的字段为空逐项拦截|验证要点:器械注册人、规格型号、注册证号等带必填标识的字段分别置空时,均被拦截且不落库;无必填标识的字段不在本条强制|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_037**(高)|Excel 模板错误或空文件|验证要点:非模板、缺列或无数据文件被拒绝,并提示原因,列表不增加脏数据|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_038**(中)|导入部分行校验失败|验证要点:文件中非法行被拒绝或标出,合法行是否入库与产品规则一致且提示明确|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_039**(中)|重复点击保存或导入|验证要点:连续提交只产生一次有效记录|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 集成验证点 + +- **TP_ADMIT_PAP_040**(高)|新增或导入的产品可被生成申请单引用|验证要点:新记录出现在待申请列表后,可勾选生成申请单,单号可在准入单列表追溯|关联需求:UI-SPEC-SPD-ADMIT-002 + +--- + +### 生成申请单、一键生成与作废 + +#### 功能验证点 + +- **TP_ADMIT_PAP_041**(高)|勾选生成申请单|验证要点:勾选允许生成的行后「生成申请单」成功,状态离开待申请|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_042**(高)|一键生成范围与当前条件一致|验证要点:一键生成只处理当前筛选下允许的记录,条数与提示一致|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_043**(高)|确认作废后进入已作废|验证要点:勾选允许作废的行并确认后,该行进入已作废|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_044**(高)|取消作废不改变状态|验证要点:作废确认框点取消后,原行状态与总数不变|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 边界验证点 + +- **TP_ADMIT_PAP_045**(中)|单条生成|验证要点:只勾选 1 条时只生成 1 张申请|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 异常验证点 + +- **TP_ADMIT_PAP_046**(高)|非法状态生成或作废被拒绝|验证要点:已生效、已作废等行执行生成或作废被拒绝,原状态不变|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_047**(中)|写接口失败|验证要点:失败有提示,不出现半成品成功态|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 集成验证点 + +- **TP_ADMIT_PAP_048**(高)|申请单进入产品准入单列表|验证要点:生成成功后,产品准入单列表可按准入单号查到该单,状态一致|关联需求:UI-SPEC-SPD-ADMIT-002 + +--- + +### 详情、编辑与日志 + +#### 功能验证点 + +- **TP_ADMIT_PAP_049**(高)|详情展示与返回|验证要点:「详情」展示产品主数据,返回后列表筛选与页码保持|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_050**(高)|编辑打开并回填|验证要点:待申请行点「编辑」,产品名称、规格型号、注册证号与该行原值一致|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_051**(高)|待申请编辑保存|验证要点:在回填正确的编辑页修改后保存,列表与再次打开为新值|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_052**(中)|操作日志可打开|验证要点:「日志」展示该行操作记录,关闭后回到列表|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 边界验证点 + +- **TP_ADMIT_PAP_053**(中)|编辑取消不保存|验证要点:修改后不保存返回,数据保持原值|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 异常验证点 + +- **TP_ADMIT_PAP_054**(高)|编辑清空必填被拦截|验证要点:清空必填后保存被拦截,原记录不被写成空|关联需求:UI-SPEC-SPD-ADMIT-002 +- **TP_ADMIT_PAP_055**(中)|非待申请行不可编辑|验证要点:已通过等行无「编辑」或保存被拒绝|关联需求:UI-SPEC-SPD-ADMIT-002 + +#### 集成验证点 + +- **TP_ADMIT_PAP_056**(中)|编辑后列表与详情一致|验证要点:保存后列表产品名称、规格、注册证号与详情一致|关联需求:UI-SPEC-SPD-ADMIT-002 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 导航、筛选、状态 Tab、列表、跳至、新增产品、分字段必填、Excel 导入、生成申请单、一键生成、作废确认与取消、详情、编辑回填与保存、日志;共 **56** 条 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 与物料准入管理共用的登录与日期规则仅保留本页 URL 与「新增产品 / Excel导入」差异;编号前缀 `TP_ADMIT_PAP` | +| 待评审项 | ① Excel 导入失败行是整单回滚还是跳过需产品确认 ② 一键生成是否包含未展示在当前页的全量待申请 ③ 准入单号点击是否等同详情 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 JSON `data/scan/admit-product-apply-elements.json` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..d512e2d --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\344\276\233\350\264\247\347\233\256\345\275\225\347\224\263\350\257\267-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,133 @@ +# 基础管理-准入管理-供货目录申请 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-ADMIT-005.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/supplyApply/list` +**版本 / 定稿日期**:2026-09-22 +**元素扫描**:`data/scan/admit-supply-elements.json`(117 项) +**定位器**:`locators/spd/admit-supply.locator.yaml` + +--- + +## 基础设置 · 准入管理 · 供货目录申请 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_ADMIT_SUP_001**(高)|菜单路径可达供货目录申请|验证要点:登录后通过「基础设置 → 准入管理 → 供货目录申请」进入,不误入准入任务分配或产品准入申请|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选区、状态 Tab、批量提交与新增按钮、列表列头可见|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_003**(中)|同组菜单可切换|验证要点:切换同组其它准入菜单后 URL 对应,再切回本页正确|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 边界验证点 + +- **TP_ADMIT_SUP_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开供货目录申请 URL,页面正常渲染|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 异常验证点 + +- **TP_ADMIT_SUP_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录页,不展示申请单|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_006**(中)|登录态过期|验证要点:会话失效后查询、新增或提交被拦截并提示重新登录|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 集成验证点 + +- **TP_ADMIT_SUP_008**(中)|顶栏院区与进院供应商范围一致|验证要点:列表进院供应商所属范围与顶栏院区不矛盾|关联需求:UI-SPEC-SPD-ADMIT-005 + +--- + +### 筛选、状态与列表 + +#### 功能验证点 + +- **TP_ADMIT_SUP_009**(高)|默认制单日期查询|验证要点:点击「查询」后列表有数据或空态明确,分页「共 N 条」与结果一致|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_010**(高)|制单日期、进院供应商、二级供应商筛选|验证要点:按制单日期区间及两级供应商查询,结果匹配|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_011**(中)|变更单号、产品名称、规格型号筛选|验证要点:按变更单号、产品名称、规格型号查询,结果匹配|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_012**(高)|组合查询、重置与收起|验证要点:多条件同时生效;重置恢复默认;收起后再展开值保留|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_013**(高)|状态 Tab|验证要点:全部、待提交、审批中、已通过、已驳回切换后列表状态匹配,Tab 计数与分页总数一致|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_014**(高)|列表核心列|验证要点:展示序号、申请单号、申请人、申请时间、进院供应商、二级供应商、业务模式、是否默认、备注、操作(编辑/详情)|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_015**(中)|分页与翻页保持条件|验证要点:翻页、每页条数正确;翻页不丢失筛选与当前 Tab|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 边界验证点 + +- **TP_ADMIT_SUP_016**(中)|制单日期起止同一天|验证要点:起止同一天仅返回该日申请|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_017**(中)|待提交计数为 0|验证要点:无待提交时 Tab 为 0,批量提交计数为 0,列表在该 Tab 下为空态|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 异常验证点 + +- **TP_ADMIT_SUP_018**(中)|日期开始晚于结束|验证要点:拒绝查询或自动纠正并提示|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_019**(中)|查询失败与无匹配空态|验证要点:接口失败有提示;无匹配时总数为 0|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 集成验证点 + +- **TP_ADMIT_SUP_020**(中)|筛选结果与分页、Tab 计数一致|验证要点:当前 Tab 加筛选后的全量条数等于分页总数,且不超过该 Tab 未筛选时的计数|关联需求:UI-SPEC-SPD-ADMIT-005 + +--- + +### 新增与编辑申请 + +#### 功能验证点 + +- **TP_ADMIT_SUP_021**(高)|打开新增|验证要点:点击「新增」进入新增供货目录,含进院供应商、二级供应商、业务模式、是否默认、计划生效时间、关联授权变更申请、备注、添加产品|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_022**(高)|必填合法提交成功|验证要点:填写必填供应商与业务模式并添加至少 1 个产品后「提交」,列表出现新申请单,状态为待提交或已进入审批(以产品规则为准)且字段一致|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_023**(中)|添加产品|验证要点:点击「添加产品」可选择产品并回填到明细,明细条数增加|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_024**(中)|导入 Excel 明细|验证要点:点击「导入excel」按模板导入产品明细,成功后明细条数与有效行一致|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_025**(高)|待提交行编辑打开并回填|验证要点:行内「编辑」回填进院供应商、二级供应商、业务模式、是否默认、计划生效时间,与列表该行一致|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_026**(高)|编辑修改后提交落库|验证要点:在回填正确的编辑页修改备注或是否默认后提交,再次打开为新值|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_027**(中)|新增或编辑放弃不落库|验证要点:返回且不提交时,列表不新增或不改变原单|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 边界验证点 + +- **TP_ADMIT_SUP_028**(中)|计划生效时间为当天|验证要点:计划生效时间选当天可提交,或按规则提示,行为明确|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_029**(中)|只添加 1 个产品|验证要点:明细仅 1 条时可以提交成功|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_030**(低)|备注为空|验证要点:备注非必填时允许为空提交|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 异常验证点 + +- **TP_ADMIT_SUP_031**(高)|进院供应商为空不可提交|验证要点:未选进院供应商时提交被拦截,不产生申请单|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_032**(高)|二级供应商为空不可提交|验证要点:未选二级供应商时提交被拦截,不产生申请单|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_033**(高)|业务模式为空不可提交|验证要点:未选业务模式时提交被拦截,不产生申请单|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_034**(中)|业务模式与是否默认选项可见|验证要点:业务模式、是否默认可展开选项(含页面上的分销/直销或是/否等等价项),只切换不提交时列表无新单|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_035**(高)|未添加产品不可提交|验证要点:产品明细为 0 时提交被拦截并提示|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_036**(中)|Excel 模板错误|验证要点:非模板或空文件被拒绝,已填表头不被脏数据覆盖|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_037**(中)|重复点击提交|验证要点:连续点击「提交」只产生一张申请单|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_038**(中)|非待提交行不可编辑|验证要点:审批中或已通过行无「编辑」,或进入后提交被拒绝|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 集成验证点 + +- **TP_ADMIT_SUP_039**(高)|提交后进入审批流|验证要点:提交成功后该单出现在审批中或待提交(按是否直接送审),审批进程可打开且与单据状态一致|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_040**(中)|默认供货关系与是否默认一致|验证要点:是否默认选「是」并审批通过后,该供应商关系按默认供货生效,同一产品不出现互相矛盾的多条默认|关联需求:UI-SPEC-SPD-ADMIT-005 + +--- + +### 批量提交与详情 + +#### 功能验证点 + +- **TP_ADMIT_SUP_041**(高)|勾选待提交后批量提交|验证要点:勾选待提交行后「批量提交」计数与勾选数一致,确认后这些单离开待提交|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_042**(中)|取消批量提交|验证要点:确认框取消后单据仍为待提交|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_043**(高)|打开详情|验证要点:「详情」展示供货目录详情,含审批进程、单据日志、返回,明细与申请时产品一致|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_044**(中)|返回保持列表条件|验证要点:从详情返回后筛选与 Tab 保持|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 边界验证点 + +- **TP_ADMIT_SUP_045**(中)|只勾选 1 条批量提交|验证要点:批量提交 1 条时只改变该条状态|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 异常验证点 + +- **TP_ADMIT_SUP_046**(高)|未勾选或勾选非待提交|验证要点:未勾选时批量提交不可用或计数为 0;勾选审批中/已通过行时被排除或整批拒绝,并提示|关联需求:UI-SPEC-SPD-ADMIT-005 +- **TP_ADMIT_SUP_047**(中)|批量提交接口失败|验证要点:失败有提示,已选单据不部分假成功|关联需求:UI-SPEC-SPD-ADMIT-005 + +#### 集成验证点 + +- **TP_ADMIT_SUP_048**(中)|批量提交与单张提交结果一致|验证要点:批量提交后的状态、审批进程与在编辑页单张「提交」相同|关联需求:UI-SPEC-SPD-ADMIT-005 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 导航、筛选、状态 Tab、新增、添加产品、Excel 导入、编辑回填与提交、分字段必填、提交、批量提交、详情与审批进程;共 **48** 条 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 登录与日期规则仅保留供货目录 URL 与供应商/业务模式差异;编号前缀 `TP_ADMIT_SUP` | +| 待评审项 | ① 新增提交后是待提交还是直接审批中 ② 「是否默认」冲突时是覆盖旧默认还是拒绝 ③ 关联授权变更申请是否必填 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 JSON `data/scan/admit-supply-elements.json` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..83335c5 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\345\207\206\345\205\245\344\273\273\345\212\241\345\210\206\351\205\215-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,121 @@ +# 基础管理-准入管理-准入任务分配 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-ADMIT-006.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/accessKind/list` +**版本 / 定稿日期**:2026-09-22 +**元素扫描**:`data/scan/admit-task-elements.json`(158 项) +**定位器**:`locators/spd/admit-task.locator.yaml` + +--- + +## 基础设置 · 准入管理 · 准入任务分配 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_ADMIT_TSK_001**(高)|菜单路径可达准入任务分配|验证要点:登录后通过「基础设置 → 准入管理 → 准入任务分配」进入,不误入供货目录申请或物料准入管理|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选区、待分类/已分类 Tab、批量分配与列表列头可见|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_003**(中)|同组菜单可切换|验证要点:切换同组其它准入菜单后 URL 对应,再切回本页正确|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 边界验证点 + +- **TP_ADMIT_TSK_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开准入任务分配 URL,页面正常渲染|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 异常验证点 + +- **TP_ADMIT_TSK_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录页,不展示待分类任务|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_006**(中)|登录态过期|验证要点:会话失效后查询或分配被拦截并提示重新登录|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 集成验证点 + +- **TP_ADMIT_TSK_008**(中)|顶栏院区与任务范围一致|验证要点:列表产品与进院供应商范围和顶栏院区不矛盾|关联需求:UI-SPEC-SPD-ADMIT-006 + +--- + +### 筛选与分类 Tab + +#### 功能验证点 + +- **TP_ADMIT_TSK_009**(高)|默认条件查询|验证要点:点击「查询」后待分类或已分类列表有数据或空态明确,分页「共 N 条」与结果一致|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_010**(高)|新增日期与操作日期筛选|验证要点:分别按新增日期、操作日期区间查询,结果落在所选区间|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_011**(高)|进院供应商、产品名称、注册证号、器械注册人、规格型号筛选|验证要点:按上述字段查询,对应列匹配|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_012**(高)|准入类型与准入状态筛选|验证要点:选择准入类型、准入状态后查询,结果与所选值匹配|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_013**(高)|耗材类型与组合查询|验证要点:耗材类型及其余已填条件同时生效|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_014**(中)|重置与收起|验证要点:「重置」恢复默认;「收起」后再展开,已填值保留|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_015**(高)|待分类与已分类切换|验证要点:切换「待分类」「已分类」后列表范围与 Tab 计数一致,已分类不再出现在待分类|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 边界验证点 + +- **TP_ADMIT_TSK_016**(中)|日期起止同一天|验证要点:新增日期或操作日期起止同一天时仅返回该日数据|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_017**(中)|已分类计数为 0|验证要点:无已分类数据时 Tab 为 0,列表空态,不报错|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 异常验证点 + +- **TP_ADMIT_TSK_018**(中)|日期开始晚于结束|验证要点:新增日期或操作日期开始晚于结束时拒绝查询或自动纠正并提示|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_019**(中)|查询失败与无匹配空态|验证要点:接口失败有提示;无匹配时总数为 0|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 集成验证点 + +- **TP_ADMIT_TSK_020**(中)|待分类任务来自准入申请|验证要点:物料准入或产品准入中待分类的产品,能在本页待分类按产品编号或名称查到|关联需求:UI-SPEC-SPD-ADMIT-006 + +--- + +### 列表与分页 + +#### 功能验证点 + +- **TP_ADMIT_TSK_021**(高)|列表核心列|验证要点:展示序号、准入类型、产品编号、产品名称、规格型号、规格、型号、器械注册人、操作「分配类型」|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_022**(中)|勾选、分页与翻页保持条件|验证要点:可勾选行;翻页和每页条数正确;翻页不丢失筛选与当前分类 Tab|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_023**(中)|跳至指定页|验证要点:分页「跳至」输入合法页码后进入该页,列表与页码一致|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 边界验证点 + +- **TP_ADMIT_TSK_024**(中)|首页上一页与末页下一页|验证要点:第 1 页上一页、最后一页下一页不可用|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 异常验证点 + +- **TP_ADMIT_TSK_025**(低)|未勾选时批量分配|验证要点:未勾选行时「批量分配」不可用或提示先选择数据|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 集成验证点 + +- **TP_ADMIT_TSK_026**(中)|分页总数与当前 Tab 计数一致|验证要点:当前分类 Tab 下分页「共 N 条」与 Tab 括号数量在同一筛选条件下一致|关联需求:UI-SPEC-SPD-ADMIT-006 + +--- + +### 分配类型与批量分配 + +#### 功能验证点 + +- **TP_ADMIT_TSK_027**(高)|单行分配类型|验证要点:待分类行点击「分配类型」可选择分类并确认,该行从待分类进入已分类|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_028**(中)|取消分配|验证要点:打开分配后取消或关闭,该行仍留在待分类|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_029**(高)|批量分配|验证要点:勾选多条待分类后「批量分配」使用同一分类,成功后这些行都进入已分类,条数与勾选数一致|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_030**(中)|已分类可查看分配结果|验证要点:切换到已分类后,已分配行展示的类型与分配时所选一致|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 边界验证点 + +- **TP_ADMIT_TSK_031**(中)|只分配 1 条|验证要点:批量或单行只处理 1 条时,仅该条变更分类,其它待分类不变|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 异常验证点 + +- **TP_ADMIT_TSK_032**(高)|未选择分类不可提交|验证要点:分配层不选类型即确认时被拦截,行仍为待分类|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_033**(中)|已分类行不可按待分类再次分配|验证要点:已分类行无「分配类型」,或再次分配被拒绝;不把已分类行重复计入待分类|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_034**(中)|分配接口失败|验证要点:失败有提示,勾选行不部分假成功|关联需求:UI-SPEC-SPD-ADMIT-006 + +#### 集成验证点 + +- **TP_ADMIT_TSK_035**(高)|分配结果回写准入产品|验证要点:分配成功后,在物料准入管理或产品准入申请中查看该产品,其分类/耗材类型与本次分配一致|关联需求:UI-SPEC-SPD-ADMIT-006 +- **TP_ADMIT_TSK_036**(中)|待分类与已分类计数联动|验证要点:分配 N 条后,待分类计数减少 N,已分类计数增加 N|关联需求:UI-SPEC-SPD-ADMIT-006 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 导航、双日期筛选、分类 Tab、列表、跳至、单行分配类型、批量分配、已分类不可重复分配、回写准入产品;共 **36** 条 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 登录与日期规则仅保留本页双日期和待分类/已分类差异;编号前缀 `TP_ADMIT_TSK` | +| 待评审项 | ① 分配类型的可选值清单与是否允许改已分类 ② 批量分配是否必须同一准入类型 ③ 扫描未打开分配弹窗,弹窗内字段以现网为准需补一轮元素扫描 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 JSON `data/scan/admit-task-elements.json` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..15995b2 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\345\215\225\345\210\227\350\241\250-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,103 @@ +# 基础管理-准入管理-物料准入单列表 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-ADMIT-003.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productOrder/zrlist` +**版本 / 定稿日期**:2026-09-22 +**元素扫描**:`data/scan/admit-material-list-elements.json`(79 项) +**定位器**:`locators/spd/admit-material-list.locator.yaml` + +--- + +## 基础设置 · 准入管理 · 物料准入单列表 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_ADMIT_MLS_001**(高)|菜单路径可达物料准入单列表|验证要点:登录后通过「基础设置 → 准入管理 → 物料准入单列表」进入,不误入产品准入单列表或物料准入管理|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选区、状态 Tab 与列表列头可见|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_003**(中)|同组菜单可切换|验证要点:切换同组其它准入菜单后 URL 对应,再切回本页正确|关联需求:UI-SPEC-SPD-ADMIT-003 + +#### 边界验证点 + +- **TP_ADMIT_MLS_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开物料准入单列表 URL,页面正常渲染|关联需求:UI-SPEC-SPD-ADMIT-003 + +#### 异常验证点 + +- **TP_ADMIT_MLS_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录页,不展示单据|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_006**(中)|登录态过期|验证要点:会话失效后查询或打开详情被拦截并提示重新登录|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-ADMIT-003 + +#### 集成验证点 + +- **TP_ADMIT_MLS_008**(中)|顶栏院区与单据范围一致|验证要点:列表单据所属院区或供应商范围与顶栏上下文不矛盾|关联需求:UI-SPEC-SPD-ADMIT-003 + +--- + +### 筛选、状态与列表 + +#### 功能验证点 + +- **TP_ADMIT_MLS_009**(高)|默认制单日期查询|验证要点:点击「查询」后列表有数据或空态明确,分页「共 N 条」与结果一致|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_010**(高)|制单日期、进院供应商、二级供应商、耗材类型、注册证号、器械注册人筛选|验证要点:分别按上述条件查询,结果与条件匹配|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_011**(高)|组合查询与重置|验证要点:多条件同时生效;「重置」恢复默认;「收起」后再展开值保留|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_012**(高)|状态 Tab|验证要点:全部、审批中、已通过、已驳回切换后,列表「状态」列与 Tab 一致,Tab 计数与分页总数一致|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_013**(高)|列表核心列|验证要点:展示序号、准入类型、准入单号、进院供应商、二级供应商、产品名称、注册证号、耗材类型、产品条数、状态、操作「详情」|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_014**(中)|分页与列设置|验证要点:翻页、每页条数、列设置生效;翻页保持筛选与当前 Tab|关联需求:UI-SPEC-SPD-ADMIT-003 + +#### 边界验证点 + +- **TP_ADMIT_MLS_015**(中)|制单日期起止同一天|验证要点:起止同一天仅返回该日单据|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_016**(中)|零计数 Tab 与首页末页|验证要点:计数为 0 的 Tab 为空态;第 1 页上一页、末页下一页不可用|关联需求:UI-SPEC-SPD-ADMIT-003 + +#### 异常验证点 + +- **TP_ADMIT_MLS_017**(中)|日期开始晚于结束|验证要点:拒绝查询或自动纠正并提示|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_018**(中)|查询失败与无匹配空态|验证要点:接口失败有提示且不白屏;无匹配时总数为 0|关联需求:UI-SPEC-SPD-ADMIT-003 + +#### 集成验证点 + +- **TP_ADMIT_MLS_019**(高)|单据来源于物料准入生成|验证要点:在物料准入管理生成的申请,可按准入单号在本列表查到,准入类型与状态一致|关联需求:UI-SPEC-SPD-ADMIT-003 + +--- + +### 单据详情 + +#### 功能验证点 + +- **TP_ADMIT_MLS_020**(高)|打开详情|验证要点:行内「详情」进入单据详情,标题为产品准入单详情,含审批进程、单据日志、返回|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_021**(高)|详情产品列表与产品条数一致|验证要点:详情内产品行数与列表「产品条数」一致|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_022**(中)|详情内按产品编码或名称过滤|验证要点:在详情搜索框输入产品编码或名称后,明细仅保留匹配行|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_023**(中)|审批进程与单据日志可打开|验证要点:点击「审批进程」「单据日志」展示对应记录,关闭后仍停留在该单据|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_024**(中)|明细查看与附件|验证要点:明细行「查看」打开产品信息;「附件」打开该行附件,无附件时数量为 0 且行为明确|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_025**(高)|返回列表|验证要点:点击「返回」回到物料准入单列表,原筛选与 Tab 保持|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_026**(中)|详情导出|验证要点:点击「导出」导出当前单据明细,条数与详情列表一致|关联需求:UI-SPEC-SPD-ADMIT-003 + +#### 边界验证点 + +- **TP_ADMIT_MLS_027**(中)|产品条数为 1|验证要点:仅 1 条明细时详情列表与导出均为 1 条|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_028**(低)|详情搜索无匹配|验证要点:搜索无匹配时明细空态明确,不清空单据头信息|关联需求:UI-SPEC-SPD-ADMIT-003 + +#### 异常验证点 + +- **TP_ADMIT_MLS_029**(中)|详情或导出失败|验证要点:详情加载或导出失败有提示,不白屏|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_030**(中)|已驳回单据不可通过|验证要点:已驳回单据上「通过」不可用或操作被拒绝,状态保持已驳回|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_031**(中)|已通过单据不可再次通过|验证要点:已通过单据详情中「通过」不可用或被拒绝,状态保持已通过|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_032**(中)|取消通过不改变状态|验证要点:若「通过」有确认框,点取消后单据状态与列表 Tab 计数不变|关联需求:UI-SPEC-SPD-ADMIT-003 + +#### 集成验证点 + +- **TP_ADMIT_MLS_033**(高)|审批通过后列表状态更新|验证要点:在允许审批的单据上「通过」成功后,返回列表该单状态为已通过,Tab 计数随之变化|关联需求:UI-SPEC-SPD-ADMIT-003 +- **TP_ADMIT_MLS_034**(中)|详情产品与物料准入管理中的产品主数据一致|验证要点:抽样核对详情中的产品名称、注册证号与物料准入管理详情一致|关联需求:UI-SPEC-SPD-ADMIT-003 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 导航、筛选、状态 Tab、列表、详情、审批进程、单据日志、附件、导出、通过确认与取消、已通过/已驳回不可再通过;共 **34** 条 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 与产品准入单列表共用的单据详情规则,本页保留物料单 URL、准入类型列与导出/附件差异;编号前缀 `TP_ADMIT_MLS` | +| 待评审项 | ① 「通过」是否仅审批角色可见 ② 已通过单据是否允许撤回 ③ 详情标题写「产品准入单详情」是否为物料单与产品单共用页 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 JSON `data/scan/admit-material-list-elements.json` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..7c2990c --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\207\206\345\205\245\347\256\241\347\220\206-\347\211\251\346\226\231\345\207\206\345\205\245\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,171 @@ +# 基础管理-准入管理-物料准入管理 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-ADMIT-001.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/dataSync/dataSync/productApply/ZRlist` +**版本 / 定稿日期**:2026-09-22 +**元素扫描**:`data/scan/admit-material-elements.json`(328 项) +**定位器**:`locators/spd/admit-material.locator.yaml` + +--- + +## 基础设置 · 准入管理 · 物料准入管理 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_ADMIT_MAT_001**(高)|菜单路径可达物料准入管理页|验证要点:登录后通过「基础设置 → 准入管理 → 物料准入管理」进入目标页,左侧菜单与顶栏 Tab 高亮为物料准入管理,不误入产品准入申请或物料准入单列表|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_002**(高)|页面主区加载完成|验证要点:整页渲染(无报表 iframe),筛选区、状态 Tab、工具栏与列表列头可见|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_003**(中)|同组准入管理菜单可切换|验证要点:切换到产品准入申请、物料准入单列表、产品准入单列表、供货目录申请、准入任务分配后 URL 与页面对应,再切回物料准入管理状态正确|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 边界验证点 + +- **TP_ADMIT_MAT_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开物料准入管理 URL,页面正常渲染|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 异常验证点 + +- **TP_ADMIT_MAT_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录页或提示重新登录,不展示准入列表数据|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_006**(中)|登录态过期访问|验证要点:会话失效后刷新或查询,提示重新登录且不把业务操作提交成功|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_007**(中)|无准入管理权限|验证要点:无权限账号不可见菜单或访问 URL 被拒绝|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 集成验证点 + +- **TP_ADMIT_MAT_008**(中)|顶栏院区上下文与列表数据范围一致|验证要点:顶栏当前院区与列表默认进院供应商、产品数据范围不矛盾|关联需求:UI-SPEC-SPD-ADMIT-001 + +--- + +### 筛选与查询 + +#### 功能验证点 + +- **TP_ADMIT_MAT_009**(高)|默认新增日期查询|验证要点:进入页面后点击「查询」,列表有数据或空态明确,底部分页「共 N 条」与当前结果一致|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_010**(高)|新增日期区间查询|验证要点:调整新增日期起止后查询,结果落在所选区间内|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_011**(高)|进院供应商筛选|验证要点:填写或选择进院供应商后查询,结果与条件匹配|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_012**(高)|耗材类型筛选|验证要点:选择耗材类型后查询,列表耗材类型与所选值匹配|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_013**(中)|产品名称、规格型号、注册证号、器械注册人筛选|验证要点:分别按上述字段查询,结果行对应列匹配关键字或所选值|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_014**(高)|多条件组合查询|验证要点:日期、供应商、耗材类型等同时填写时,结果同时满足全部已填条件|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_015**(中)|重置恢复默认|验证要点:填写筛选后点「重置」,各字段恢复默认,列表按默认规则刷新|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_016**(中)|收起与展开筛选区|验证要点:点击「收起」折叠筛选区,再展开后已填条件仍保留|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 边界验证点 + +- **TP_ADMIT_MAT_017**(中)|新增日期起止同一天|验证要点:开始日期等于结束日期时查询成功,仅返回该日数据|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_018**(低)|文本条件为空仅按日期查询|验证要点:产品名称等留空时不因空值报错,按其余条件查询|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 异常验证点 + +- **TP_ADMIT_MAT_019**(中)|新增日期开始晚于结束|验证要点:开始日期晚于结束日期时拒绝查询或自动纠正,并给出可理解提示|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_020**(中)|查询接口异常|验证要点:列表查询失败时有错误提示,不白屏、不把过期数据展示为成功态|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_021**(低)|无匹配结果空态|验证要点:条件无匹配时列表空态明确,分页总数为 0|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 集成验证点 + +- **TP_ADMIT_MAT_022**(中)|筛选结果与分页总数一致|验证要点:查询后底部分页「共 N 条」与当前筛选全量条数一致|关联需求:UI-SPEC-SPD-ADMIT-001 + +--- + +### 状态 Tab + +#### 功能验证点 + +- **TP_ADMIT_MAT_023**(高)|状态 Tab 切换|验证要点:在全部、待申请、审批中、已通过、已驳回、已作废、生效失败、已生效之间切换后,列表仅含该状态数据(「全部」不额外收窄)|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_024**(高)|Tab 计数与列表总数一致|验证要点:各状态 Tab 括号内数量与该状态下分页「共 N 条」一致|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 边界验证点 + +- **TP_ADMIT_MAT_025**(中)|数量为 0 的状态 Tab|验证要点:计数为 0 的 Tab 可进入,列表为空态,不报错|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 异常验证点 + +- **TP_ADMIT_MAT_026**(低)|切换 Tab 时查询未完成|验证要点:查询进行中切换状态 Tab,最终只展示最后一次有效结果,不叠加以旧数据|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 集成验证点 + +- **TP_ADMIT_MAT_027**(中)|状态 Tab 与筛选条件同时生效|验证要点:已填筛选后再切 Tab,结果同时满足筛选条件与所选状态|关联需求:UI-SPEC-SPD-ADMIT-001 + +--- + +### 列表、勾选与分页 + +#### 功能验证点 + +- **TP_ADMIT_MAT_028**(高)|列表核心列完整|验证要点:主表展示序号、准入类型、产品编号、产品名称、规格型号、规格、型号、器械注册人、单位、耗材类型、默认进院供应商、操作|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_029**(中)|列设置生效|验证要点:列设置勾选或取消列后,表头显示与设置一致|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_030**(中)|分页翻页与每页条数|验证要点:总条数大于一页时可翻页且数据更换;切换每页条数后当前页行数不超过所选值,总数不变|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_031**(中)|行勾选|验证要点:勾选单行或表头全选后,选中行与后续批量操作的对象一致|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 边界验证点 + +- **TP_ADMIT_MAT_032**(中)|首页上一页、末页下一页不可用|验证要点:第 1 页「上一页」禁用或无效,最后一页「下一页」禁用或无效|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 异常验证点 + +- **TP_ADMIT_MAT_033**(低)|未勾选时批量按钮状态|验证要点:未勾选行时,依赖勾选的批量操作不可用或提示先选择数据|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 集成验证点 + +- **TP_ADMIT_MAT_034**(中)|翻页保持当前筛选与状态 Tab|验证要点:带筛选和状态翻页后,新页数据仍满足原条件|关联需求:UI-SPEC-SPD-ADMIT-001 + +--- + +### 关联产品、生成申请单、一键生成与作废 + +#### 功能验证点 + +- **TP_ADMIT_MAT_035**(高)|关联产品打开选择层|验证要点:点击「关联产品」打开产品关联界面,取消关闭后列表不新增申请单、默认进院供应商不变|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_036**(高)|确认关联后关系落库|验证要点:在关联层选定产品并确认后,当前行关联关系或默认进院供应商与所选项一致,再次打开详情可回看|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_037**(高)|勾选待申请行生成申请单|验证要点:勾选允许生成的行后点「生成申请单」,产生对应准入申请且原行状态按规则离开待申请|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_038**(高)|一键生成按当前条件处理|验证要点:点击「一键生成」仅处理当前筛选且状态允许的记录,处理范围与提示条数一致|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_039**(高)|确认作废后状态变为已作废|验证要点:勾选允许作废的行并在确认框点确定后,该行进入已作废,已作废 Tab 可查到|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_040**(高)|取消作废不改变状态|验证要点:作废确认框点取消后,原行状态与分页总数不变|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 边界验证点 + +- **TP_ADMIT_MAT_041**(中)|仅勾选一条生成申请单|验证要点:只勾选 1 条合法行时生成 1 张申请,不多生成|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 异常验证点 + +- **TP_ADMIT_MAT_042**(高)|不允许的状态执行生成或作废|验证要点:对已生效、已作废等不允许的行执行生成申请单或作废时被拒绝,并提示原因,原状态不变|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_043**(中)|重复点击生成或作废|验证要点:连续点击只产生一次有效结果,不生成重复申请单|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_044**(中)|写接口失败|验证要点:生成或作废接口失败时有错误提示,列表不出现半成品成功态|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 集成验证点 + +- **TP_ADMIT_MAT_045**(高)|生成的申请单可在产品准入申请或准入单列表追溯|验证要点:生成成功后,对应申请单号或准入单可在产品准入申请、物料准入单列表中按单号查到,状态与来源行一致|关联需求:UI-SPEC-SPD-ADMIT-001 + +--- + +### 详情、编辑与日志 + +#### 功能验证点 + +- **TP_ADMIT_MAT_046**(高)|打开详情|验证要点:行内「详情」打开产品详情,展示产品名称、规格型号、注册证号、器械注册人、分类与医保信息,可返回列表|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_047**(高)|编辑打开并回填|验证要点:待申请行点「编辑」,产品名称、规格型号、注册证号、器械注册人与该行及详情原值一致|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_048**(高)|待申请行可编辑并保存|验证要点:在回填正确的编辑页修改可编辑字段后保存,再次打开为新值|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_049**(中)|编辑取消不落库|验证要点:修改后返回或取消,列表与再次打开仍为修改前值|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_050**(中)|打开操作日志|验证要点:行内「日志」打开操作日志,含该产品的操作记录,关闭后回到列表|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 边界验证点 + +- **TP_ADMIT_MAT_051**(中)|详情只读字段不可改|验证要点:详情态下产品主数据关键字段不可直接改写落库|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 异常验证点 + +- **TP_ADMIT_MAT_052**(高)|编辑清空必填被拦截|验证要点:清空产品名称等必填项后保存被拦截,原记录不被写成空|关联需求:UI-SPEC-SPD-ADMIT-001 +- **TP_ADMIT_MAT_053**(中)|非待申请行不提供编辑或编辑被拒绝|验证要点:已生效等行不出现「编辑」,或打开后保存被拒绝|关联需求:UI-SPEC-SPD-ADMIT-001 + +#### 集成验证点 + +- **TP_ADMIT_MAT_054**(中)|编辑保存后列表列与详情一致|验证要点:保存后列表产品名称、规格型号等与详情回看一致|关联需求:UI-SPEC-SPD-ADMIT-001 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 导航、筛选、状态 Tab、列表分页、关联产品(打开/确认)、生成申请单、一键生成、作废确认与取消、详情、编辑回填与保存、日志;共 **54** 条 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 登录拦截与同组其它准入页规则相同,本页仅保留物料准入菜单、URL 与待申请生成/作废差异;编号前缀 `TP_ADMIT_MAT` | +| 待评审项 | ① 一键生成是否二次确认、处理范围是否含当前 Tab 以外数据需产品确认 ② 已通过与已生效的编辑权限边界 ③ 关联产品是否改写默认进院供应商 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 JSON `data/scan/admit-material-elements.json` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..7268594 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\205\254\345\221\212\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,180 @@ +# 基础管理-医院资料-公告管理 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-BASIC-001.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/hospital/paltform/notice` +**版本 / 定稿日期**:2026-09-16 +**元素扫描**:`data/scan/basic-notice-elements.json`(82 项) +**定位器**:`locators/spd/basic-notice.locator.yaml` + +--- + +## 基础设置 · 医院资料 · 公告管理 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_BASIC_NTC_001**(高)|菜单路径可达公告管理页|验证要点:登录后通过「基础设置 → 医院资料 → 公告管理」进入目标页,左侧菜单与顶栏 Tab 高亮为公告管理,不误入医院信息登记/院区管理/财务月份设置|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_002**(高)|页面主区加载完成|验证要点:整页渲染(无报表 iframe),筛选区、新增按钮、列表列头与分页可见|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_003**(中)|同组医院资料菜单可切换|验证要点:切换到医院信息登记/院区管理/财务月份设置后 URL 与页面内容对应,再切回公告管理状态正确|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 边界验证点 + +- **TP_BASIC_NTC_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/hospital/paltform/notice`,页面正常渲染|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 异常验证点 + +- **TP_BASIC_NTC_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录页或提示重新登录,不展示公告列表数据|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_006**(中)|登录态过期访问|验证要点:会话失效后刷新或操作查询,提示重新登录且不提交业务请求成功|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_007**(中)|无医院资料/公告管理权限|验证要点:无权限账号不可见菜单或访问 URL 被拒绝|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 集成验证点 + +- **TP_BASIC_NTC_008**(中)|顶栏院区/用户上下文与列表发布人范围一致|验证要点:顶栏当前院区/用户与列表「发布人」「发布科室」展示范围不矛盾|关联需求:UI-SPEC-SPD-BASIC-001 + +--- + +### 筛选与查询 + +#### 功能验证点 + +- **TP_BASIC_NTC_009**(高)|默认条件查询|验证要点:进入页面后点击「查询」,列表有数据或空态明确,底部分页「共 N 条」与行数逻辑一致|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_010**(高)|公告标题模糊查询|验证要点:按标题关键字查询,结果行「公告标题」均包含该关键字(或产品约定的模糊规则)|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_011**(高)|发布日期区间查询|验证要点:按开始/结束日期查询,结果落在所选发布日期区间内|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_012**(高)|发布范围下拉查询|验证要点:分别选择全部/全院/中心库/进院供应商后查询,列表「发布范围」列与所选值匹配(选「全部」时不额外收窄)|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_013**(高)|标题+日期+范围组合查询|验证要点:多个已填条件同时生效,结果同时满足全部条件|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_014**(中)|重置恢复默认|验证要点:填写筛选后点「重置」,标题/日期/范围恢复默认,列表按默认规则刷新|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 边界验证点 + +- **TP_BASIC_NTC_015**(中)|发布日期起止同一天|验证要点:开始日期等于结束日期时查询成功,仅返回该日数据|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_016**(中)|公告标题超长关键字|验证要点:输入达到或超过字段允许长度的标题关键字,查询被接受并按规则截断或返回匹配结果,页面不崩溃|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_017**(低)|标题为空仅按日期/范围查询|验证要点:标题留空时按其余条件查询,不因空标题报错|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 异常验证点 + +- **TP_BASIC_NTC_018**(中)|发布日期开始晚于结束|验证要点:开始日期晚于结束日期时拒绝查询或自动纠正,并给出可理解提示|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_019**(中)|查询接口异常|验证要点:列表查询失败时有错误提示,不白屏、不展示过期脏数据为成功态|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_020**(低)|无匹配结果空态|验证要点:条件无匹配时列表空态或「暂无数据」明确,分页总数为 0|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 集成验证点 + +- **TP_BASIC_NTC_021**(中)|筛选结果与分页总数一致|验证要点:查询后主表可见行数与底部分页「共 N 条」在当前页逻辑下一致|关联需求:UI-SPEC-SPD-BASIC-001 + +--- + +### 列表与分页 + +#### 功能验证点 + +- **TP_BASIC_NTC_022**(高)|列表核心列完整|验证要点:主表展示序号、公告编号、公告标题、发布范围、发布科室、发布人、操作(编辑/删除)|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_023**(中)|表格密度切换|验证要点:密度(column-height)切换后列表仍可读,列不丢失|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_024**(中)|列设置生效|验证要点:列设置(setting)勾选/取消列后,对应列显示或隐藏与设置一致|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_025**(中)|分页翻页|验证要点:总条数大于一页时可翻至第 2 页,数据更换且页码高亮正确|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_026**(中)|每页条数切换|验证要点:切换「50 条/页」等页大小后,当前页行数不超过所选值,总数不变|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_027**(中)|跳至指定页|验证要点:分页「跳至」输入合法页码后跳到对应页|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 边界验证点 + +- **TP_BASIC_NTC_028**(中)|首页上一页不可用|验证要点:第 1 页时「上一页」禁用或点击无效|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_029**(中)|末页下一页不可用|验证要点:最后一页时「下一页」禁用或点击无效|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_030**(低)|跳至超出总页数|验证要点:跳至大于总页数的页码时被拒绝、纠正到末页或给出提示|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 异常验证点 + +- **TP_BASIC_NTC_031**(低)|跳至非数字页码|验证要点:跳至输入非法字符时不跳转异常页,输入被忽略或提示|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 集成验证点 + +- **TP_BASIC_NTC_032**(中)|翻页不丢失当前筛选|验证要点:带筛选条件翻页后,条件保持,新页数据仍满足原筛选|关联需求:UI-SPEC-SPD-BASIC-001 + +--- + +### 新增公告 + +#### 功能验证点 + +- **TP_BASIC_NTC_033**(高)|打开新增公告弹窗|验证要点:点击「新增」弹出「新增公告」,含标题、发布范围、置顶、截止日期、富文本内容、取消/发布|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_034**(高)|合法必填发布成功|验证要点:填写必填标题、发布范围、公告内容后点「发布」,弹窗关闭,列表出现新记录且字段与提交一致|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_035**(高)|发布范围四档可选|验证要点:新增弹窗单选全部/全院/中心库/进院供应商均可选中且互斥,发布后列表「发布范围」与所选一致|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_036**(中)|置顶显示是/否|验证要点:选择置顶「是」或「否」后发布,记录按产品规则置顶或保持普通排序|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_037**(中)|截止日期可选|验证要点:选择截止日期后发布,编辑回看该日期与提交一致|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_038**(中)|富文本内容可写入|验证要点:在 `.ql-editor` 输入正文后发布,编辑回看正文与提交一致(纯文本级)|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_039**(中)|取消不落库|验证要点:填写后点「取消」或关闭,列表不新增对应记录|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 边界验证点 + +- **TP_BASIC_NTC_040**(中)|标题达到允许最大长度|验证要点:标题取最大允许长度可发布成功|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_041**(中)|截止日期为当天|验证要点:截止日期选当天可发布或按产品规则提示,行为明确|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_042**(低)|截止日期不填|验证要点:截止日期为空时允许发布(非必填),记录无截止日期或展示为空|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 异常验证点 + +- **TP_BASIC_NTC_043**(高)|必填标题为空拦截|验证要点:标题为空点「发布」被拦截,出现必填提示,不关闭弹窗、不落库|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_044**(高)|必填内容为空拦截|验证要点:富文本内容为空点「发布」被拦截并提示,不落库|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_045**(中)|标题超长拦截|验证要点:标题超过最大长度时拒绝发布并提示|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_046**(中)|截止日期早于当天|验证要点:截止日期早于当前日期时拒绝或警告,行为与产品规则一致|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_047**(中)|发布接口失败|验证要点:保存接口失败时有错误提示,弹窗仍在,列表不出现半成品成功态|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_048**(中)|重复点击发布|验证要点:连续快速点击「发布」只产生一条记录或第二次被防重|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 集成验证点 + +- **TP_BASIC_NTC_049**(高)|新公告对目标范围可见|验证要点:按发布范围发布后,对应范围终端(全院/中心库/进院供应商)能看到该公告,范围外不可见(与 `API-SPD-BASIC-001` 通知公告约定一致)|关联需求:UI-SPEC-SPD-BASIC-001 + +--- + +### 编辑公告 + +#### 功能验证点 + +- **TP_BASIC_NTC_050**(高)|打开编辑公告弹窗|验证要点:行内「编辑」打开「编辑公告」,回填标题、范围、置顶、截止日期、富文本原文|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_051**(高)|修改后发布落库|验证要点:修改标题或内容后点「发布」,列表与再次编辑回看均为新值|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_052**(中)|编辑取消不保存|验证要点:修改后点「取消」,列表与再次打开仍为修改前值|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 边界验证点 + +- **TP_BASIC_NTC_053**(低)|不改字段直接发布|验证要点:打开编辑不改内容点「发布」,记录保持原值且不报错|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 异常验证点 + +- **TP_BASIC_NTC_054**(高)|编辑清空必填拦截|验证要点:清空标题或内容后发布被拦截,原记录不被写成空|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_055**(中)|编辑已删除记录|验证要点:记录已被他人删除后再发布,提示记录不存在,不产生幽灵更新成功|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 集成验证点 + +- **TP_BASIC_NTC_056**(中)|编辑后目标端内容同步|验证要点:修改已发布公告后,目标范围再次打开看到的是新标题/正文|关联需求:UI-SPEC-SPD-BASIC-001 + +--- + +### 删除公告 + +#### 功能验证点 + +- **TP_BASIC_NTC_057**(高)|删除需二次确认|验证要点:点行内「删除」出现确认框(标题「提示」),含取消/确定|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_058**(高)|确认删除后列表移除|验证要点:确认后该行消失,分页总数减 1,再查标题无该编号|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_059**(中)|取消删除保留记录|验证要点:确认框点「取消」后记录仍在,编号不变|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 边界验证点 + +- **TP_BASIC_NTC_060**(低)|删除列表最后一条|验证要点:删除当前筛选下最后一条后,空态或总数为 0,页面不报错|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 异常验证点 + +- **TP_BASIC_NTC_061**(中)|删除接口失败|验证要点:删除失败有提示,该行仍在列表|关联需求:UI-SPEC-SPD-BASIC-001 +- **TP_BASIC_NTC_062**(中)|重复确认删除|验证要点:记录已删后再确认,提示不存在或静默成功,不出现未捕获异常|关联需求:UI-SPEC-SPD-BASIC-001 + +#### 集成验证点 + +- **TP_BASIC_NTC_063**(中)|删除后目标端不再展示|验证要点:删除已发布公告后,原发布范围终端不再展示该条|关联需求:UI-SPEC-SPD-BASIC-001 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、筛选查询、列表分页、新增/编辑/删除、置顶与截止日期、富文本;共 **63** 条原子测试点 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 登录拦截、会话过期与同模块其它医院资料页规则相同,本页仅保留公告菜单/URL 差异;编号前缀 `TP_BASIC_NTC` | +| 待评审项 | ① 发布范围「进院供应商」与规范旧称「供应商」文案以现网为准 ② 置顶后列表排序规则需产品确认 ③ 截止日期过期后公告是否自动下架 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 `npm run discover:scan-basic` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..5f4993c --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\345\214\273\351\231\242\344\277\241\346\201\257\347\231\273\350\256\260-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,123 @@ +# 基础管理-医院资料-医院信息登记 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-BASIC-002.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/hospital/paltform/info` +**版本 / 定稿日期**:2026-09-16 +**元素扫描**:`data/scan/basic-hospital-info-elements.json`(38 项) +**定位器**:`locators/spd/basic-hospital-info.locator.yaml` + +--- + +## 基础设置 · 医院资料 · 医院信息登记 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_BASIC_HOS_001**(高)|菜单路径可达医院信息登记页|验证要点:登录后通过「基础设置 → 医院资料 → 医院信息登记」进入目标页,菜单与 Tab 高亮正确,不误入公告/院区/财务月份页|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_002**(高)|详情页加载完成|验证要点:整页渲染,查看模式下展示医院编码、医院名称、地址、联系人、联系方式、医院简称、登录页背景、医院 logo,工具栏「编辑」可见|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_003**(中)|同组医院资料菜单可切换|验证要点:切到公告管理等页再返回,详情字段仍为当前医院资料|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 边界验证点 + +- **TP_BASIC_HOS_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/hospital/paltform/info`,详情正常渲染|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 异常验证点 + +- **TP_BASIC_HOS_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示医院资料|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_006**(中)|登录态过期访问|验证要点:会话失效后刷新或保存,提示重新登录且不误更新资料|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_007**(中)|无医院信息登记权限|验证要点:无权限账号不可见菜单或访问被拒绝|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 集成验证点 + +- **TP_BASIC_HOS_008**(中)|展示资料与当前登录医院一致|验证要点:页面医院编码/名称与登录医院主数据、顶栏医院上下文一致|关联需求:UI-SPEC-SPD-BASIC-002 + +--- + +### 查看模式 + +#### 功能验证点 + +- **TP_BASIC_HOS_009**(高)|只读字段不可改|验证要点:查看模式下医院编码、医院名称、地址不可编辑|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_010**(高)|可编辑字段查看态只读|验证要点:未点「编辑」时联系人、联系方式、医院简称不以输入框提交,展示为只读文本|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_011**(中)|背景与 logo 可预览|验证要点:已上传时「预览文件」可打开预览,图片与已存文件对应|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 边界验证点 + +- **TP_BASIC_HOS_012**(低)|无背景或无 logo|验证要点:未上传时占位/提示文案明确(尺寸与格式说明可见),不报错|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 异常验证点 + +- **TP_BASIC_HOS_013**(中)|详情接口失败|验证要点:加载医院信息失败时有错误提示,不展示空白成功态或错院数据|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 集成验证点 + +- **TP_BASIC_HOS_014**(中)|只读字段与主数据接口一致|验证要点:医院编码/名称/地址与 `API-SPD-BASIC-001` 医院主数据查询结果一致|关联需求:UI-SPEC-SPD-BASIC-002 + +--- + +### 编辑与保存 + +#### 功能验证点 + +- **TP_BASIC_HOS_015**(高)|进入编辑模式|验证要点:点「编辑」后出现「保存」「取消」,联系人、联系方式、医院简称变为可输入|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_016**(高)|编辑态只读字段仍不可改|验证要点:编辑模式下医院编码、医院名称、地址仍不可改|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_017**(高)|保存后字段持久化|验证要点:修改联系人/联系方式/医院简称后保存,退出再进或刷新后仍为新值|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_018**(高)|取消不保存|验证要点:修改后点「取消」回到查看模式,字段仍为修改前值|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_019**(中)|保存后回到查看模式|验证要点:保存成功后「保存/取消」消失,「编辑」重新出现,字段恢复只读展示|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 边界验证点 + +- **TP_BASIC_HOS_020**(中)|联系人/简称达到最大长度|验证要点:达到允许最大长度可保存成功|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_021**(低)|不改内容直接保存|验证要点:进入编辑不改字段点保存,数据不变且成功或提示无变更,不报错|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 异常验证点 + +- **TP_BASIC_HOS_022**(高)|必填联系人为空拦截|验证要点:清空联系人后保存被拦截并提示,原值不被写成空|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_023**(高)|必填联系方式为空拦截|验证要点:清空联系方式后保存被拦截并提示|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_024**(高)|必填医院简称为空拦截|验证要点:清空医院简称后保存被拦截并提示|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_025**(中)|联系方式格式非法|验证要点:输入明显非法联系方式时拒绝保存或按产品规则校验并提示|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_026**(中)|超长字段拦截|验证要点:超过最大长度时拒绝保存并提示|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_027**(中)|保存接口失败|验证要点:更新失败有提示,页面仍为编辑态或回看旧值,不出现半保存成功|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_028**(中)|重复点击保存|验证要点:快速连点「保存」不产生重复提交错误或脏写|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 集成验证点 + +- **TP_BASIC_HOS_029**(中)|保存后登录页/其它入口简称一致|验证要点:医院简称保存后,依赖简称的展示入口(若有)与本页一致|关联需求:UI-SPEC-SPD-BASIC-002 + +--- + +### 图片上传 + +#### 功能验证点 + +- **TP_BASIC_HOS_030**(高)|编辑态可上传登录页背景|验证要点:编辑模式下可通过 `loginBgUploadInput` 上传符合说明的图片,保存后查看态可预览新图|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_031**(高)|编辑态可上传医院 logo|验证要点:通过 `logoUploadInput` 上传符合说明的图片,保存后查看态可预览新 logo|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_032**(中)|上传后取消不替换原图|验证要点:编辑态选了新图但点「取消」,原背景/logo 不变|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 边界验证点 + +- **TP_BASIC_HOS_033**(中)|背景尺寸上限|验证要点:按页面说明(约 1920×1040、png/jpg、不超过 2MB)的临界合法文件可上传成功|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_034**(中)|logo 大小上限|验证要点:按页面说明(png、不超过 1MB)的临界合法文件可上传成功|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 异常验证点 + +- **TP_BASIC_HOS_035**(中)|超限或非法格式被拒|验证要点:超过尺寸/大小或非 png/jpg 的文件被拒绝并提示,原图不被替换|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_036**(中)|上传接口失败|验证要点:上传失败有提示,保存后仍为原图或明确失败态|关联需求:UI-SPEC-SPD-BASIC-002 + +#### 集成验证点 + +- **TP_BASIC_HOS_037**(高)|登录页背景对登录页生效|验证要点:保存新登录页背景后,登录页实际展示为新图(或按产品约定的缓存刷新后生效)|关联需求:UI-SPEC-SPD-BASIC-002 +- **TP_BASIC_HOS_038**(高)|医院 logo 对系统展示生效|验证要点:保存新 logo 后,系统顶栏或约定入口展示新 logo|关联需求:UI-SPEC-SPD-BASIC-002 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、查看只读、编辑保存/取消、必填与格式、背景/logo 上传与登录页联动;共 **38** 条原子测试点 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 登录/权限类与公告管理共用规则,本页仅保留本 URL 与医院资料字段差异;编号前缀 `TP_BASIC_HOS` | +| 待评审项 | ① 联系方式校验规则(手机/座机/长度)需产品确认 ② 图片替换是否支持删除后无图 ③ 登录页缓存刷新时效 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 `npm run discover:scan-basic` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..e48c0a6 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\350\264\242\345\212\241\346\234\210\344\273\275\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,140 @@ +# 基础管理-医院资料-财务月份设置 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-BASIC-003.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/hospital/balance/month` +**版本 / 定稿日期**:2026-09-16 +**元素扫描**:`data/scan/basic-finance-month-elements.json`(43 项) +**定位器**:`locators/spd/basic-finance-month.locator.yaml` + +--- + +## 基础设置 · 医院资料 · 财务月份设置 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_BASIC_FMN_001**(高)|菜单路径可达财务月份设置页|验证要点:登录后通过「基础设置 → 医院资料 → 财务月份设置」进入目标页,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_002**(高)|页面主区加载完成|验证要点:整页渲染,财务月份筛选、查询/重置、列表列头与分页可见|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_003**(中)|同组医院资料菜单可切换|验证要点:切到其它医院资料页再返回,列表仍为财务月份数据|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 边界验证点 + +- **TP_BASIC_FMN_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/hospital/balance/month`,列表正常渲染|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 异常验证点 + +- **TP_BASIC_FMN_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示月份数据|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_006**(中)|登录态过期访问|验证要点:会话失效后查询或保存被拦截,不误改自动结算日|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_007**(中)|无财务月份设置权限|验证要点:无权限账号不可见菜单或访问被拒绝|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 集成验证点 + +- **TP_BASIC_FMN_008**(中)|列表月份与医院财务账期主数据一致|验证要点:抽样行的财务月份、开始/结束日期与财务主数据或结算模块使用的账期一致|关联需求:UI-SPEC-SPD-BASIC-003 + +--- + +### 筛选与查询 + +#### 功能验证点 + +- **TP_BASIC_FMN_009**(高)|默认查询出列表|验证要点:进入后点「查询」或默认加载,列表有历史/当前月份行或空态明确,分页「共 N 条」正确|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_010**(高)|按财务月份筛选|验证要点:选择某一财务月份后查询,结果仅含该月份或包含该月的约定集合|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_011**(中)|重置恢复默认|验证要点:选择月份后点「重置」,筛选恢复默认,列表按默认规则刷新|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 边界验证点 + +- **TP_BASIC_FMN_012**(低)|财务月份为空查询|验证要点:不选月份直接查询,返回默认全量或当前规则下的月份列表,不报错|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_013**(低)|筛选最早/最晚已有月份|验证要点:选择列表中最早或最晚月份能查到对应行|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 异常验证点 + +- **TP_BASIC_FMN_014**(中)|查询接口异常|验证要点:查询失败有错误提示,不白屏|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_015**(低)|无匹配月份空态|验证要点:筛选不存在的月份时列表空态明确,总数为 0|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 集成验证点 + +- **TP_BASIC_FMN_016**(中)|筛选结果与分页总数一致|验证要点:查询后可见行与「共 N 条」逻辑一致|关联需求:UI-SPEC-SPD-BASIC-003 + +--- + +### 列表与状态 + +#### 功能验证点 + +- **TP_BASIC_FMN_017**(高)|列表核心列完整|验证要点:展示财务月份、开始日期、结束日期、自动结算日期、状态、操作|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_018**(高)|当前月份唯一可改|验证要点:状态为「当前月份」的行显示「修改」;「历史月份」行无修改操作或为「--」|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_019**(中)|当前月份与历史月份互斥|验证要点:同一时点列表至多一行状态为「当前月份」|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_020**(中)|表格密度/列设置|验证要点:密度或列设置变更后核心列仍可核对,不丢「状态」「操作」|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 边界验证点 + +- **TP_BASIC_FMN_021**(中)|仅一行当前月份|验证要点:无论列表多长,可修改入口只出现在当前月份行|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 异常验证点 + +- **TP_BASIC_FMN_022**(中)|历史月份无法进入编辑|验证要点:历史月份行不能通过任何入口改开始/结束/自动结算日期|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 集成验证点 + +- **TP_BASIC_FMN_023**(高)|当前月份与业务结算使用账期一致|验证要点:本页「当前月份」与出入库/结算报表默认财务月份一致|关联需求:UI-SPEC-SPD-BASIC-003 + +--- + +### 当前月份修改 + +#### 功能验证点 + +- **TP_BASIC_FMN_024**(高)|修改进入行内编辑|验证要点:当前月份点「修改」后出现「保存」「取消」,自动结算日期变为日期框|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_025**(高)|开始/结束日期编辑态只读|验证要点:行内编辑时开始日期、结束日期不可改,仅自动结算日期可改|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_026**(高)|保存自动结算日期持久化|验证要点:修改自动结算日期并保存,刷新后该行仍为新日期|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_027**(高)|取消不保存|验证要点:改了日期点「取消」,自动结算日期恢复修改前值|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_028**(中)|保存后退出行内编辑|验证要点:保存成功后「保存/取消」消失,「修改」重新出现|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 边界验证点 + +- **TP_BASIC_FMN_029**(中)|自动结算日落在当月区间内|验证要点:选择落在该月开始~结束日期内的日期可保存成功|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_030**(中)|自动结算日等于开始或结束日|验证要点:选开始日或结束日当天可保存或按产品规则提示,行为明确|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 异常验证点 + +- **TP_BASIC_FMN_031**(高)|自动结算日为空拦截|验证要点:清空日期后保存被拦截并提示,原日期不被写成空|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_032**(高)|自动结算日超出当月区间|验证要点:早于开始日或晚于结束日时拒绝保存并提示|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_033**(中)|保存接口失败|验证要点:更新失败有提示,行上日期仍为旧值|关联需求:UI-SPEC-SPD-BASIC-003 +- **TP_BASIC_FMN_034**(中)|重复点击保存|验证要点:快速连点保存不产生重复错误或脏写|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 集成验证点 + +- **TP_BASIC_FMN_035**(高)|自动结算日变更影响自动结算任务|验证要点:保存新自动结算日后,依赖该日的自动结算/出账任务按新日期执行或调度(与结算模块约定一致)|关联需求:UI-SPEC-SPD-BASIC-003 + +--- + +### 分页 + +#### 功能验证点 + +- **TP_BASIC_FMN_036**(中)|分页翻页与页大小|验证要点:总条数大于一页时可翻页;切换每页条数后行数不超过所选值|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 边界验证点 + +- **TP_BASIC_FMN_037**(低)|首页上一页不可用|验证要点:第 1 页「上一页」禁用或无效|关联需求:UI-SPEC-SPD-BASIC-003 + +#### 异常验证点 + +无。本页分页无跳至输入(扫描未发现 jumper),无额外异常点。 + +#### 集成验证点 + +- **TP_BASIC_FMN_038**(低)|翻页不丢失月份筛选|验证要点:带财务月份条件翻页后条件保持|关联需求:UI-SPEC-SPD-BASIC-003 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、月份筛选、当前/历史状态、仅自动结算日可改、保存/取消、分页;共 **38** 条原子测试点 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分;分页异常写明「无」 | +| 去重 | 登录/权限与同模块其它页共用规则,本页强调当前月份唯一可改;编号前缀 `TP_BASIC_FMN` | +| 待评审项 | ① 自动结算日是否允许等于结束日之后的缓冲天数 ② 当前月份切换是否由系统日切自动完成、本页是否只读切换 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 `npm run discover:scan-basic` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..a8caeee --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\214\273\351\231\242\350\265\204\346\226\231-\351\231\242\345\214\272\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,135 @@ +# 基础管理-医院资料-院区管理 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-BASIC-004.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/hospital/paltform/area` +**版本 / 定稿日期**:2026-09-16 +**元素扫描**:`data/scan/basic-branch-elements.json`(60 项) +**定位器**:`locators/spd/basic-branch.locator.yaml` + +--- + +## 基础设置 · 医院资料 · 院区管理 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_BASIC_BRN_001**(高)|菜单路径可达院区管理页|验证要点:登录后通过「基础设置 → 医院资料 → 院区管理」进入目标页,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_002**(高)|页面主区加载完成|验证要点:整页渲染,新增按钮、院区名称搜索框、列表列头与分页可见;无独立「查询」「重置」按钮|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_003**(中)|同组医院资料菜单可切换|验证要点:切到公告/医院信息/财务月份再返回,院区列表仍正确|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 边界验证点 + +- **TP_BASIC_BRN_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/hospital/paltform/area`,列表正常渲染|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 异常验证点 + +- **TP_BASIC_BRN_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示院区数据|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_006**(中)|登录态过期访问|验证要点:会话失效后新增/编辑被拦截,不误写院区|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_007**(中)|无院区管理权限|验证要点:无权限账号不可见菜单或访问被拒绝|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 集成验证点 + +- **TP_BASIC_BRN_008**(中)|列表院区与顶栏可选院区同源|验证要点:本页院区名称集合与顶栏院区切换、其它模块院区下拉同源(允许按权限裁剪)|关联需求:UI-SPEC-SPD-BASIC-004 + +--- + +### 列表搜索与分页 + +#### 功能验证点 + +- **TP_BASIC_BRN_009**(高)|默认列出院区|验证要点:进入页面列表有数据或空态明确,分页「共 N 条」正确|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_010**(高)|院区名称搜索过滤|验证要点:在院区名称框输入关键字后列表仅保留名称匹配行(即时或回车,以现网为准)|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_011**(高)|列表核心列完整|验证要点:展示序号、院区编号、院区名称、院区地址、状态、启用云仓、操作(编辑)|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_012**(中)|分页翻页与页大小|验证要点:可翻页;切换每页条数后行数不超过所选值|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_013**(中)|表格密度/列设置|验证要点:密度或列设置变更后核心列仍可核对|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 边界验证点 + +- **TP_BASIC_BRN_014**(中)|名称搜索超长关键字|验证要点:超长关键字不崩溃,按规则截断或无匹配空态|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_015**(低)|名称搜索清空恢复全量|验证要点:清空搜索框后列表恢复未过滤的默认集合|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_016**(低)|首页上一页不可用|验证要点:第 1 页「上一页」禁用或无效|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 异常验证点 + +- **TP_BASIC_BRN_017**(中)|列表接口异常|验证要点:加载失败有错误提示,不白屏|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_018**(低)|搜索无匹配空态|验证要点:无匹配时暂无数据,总数为 0|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 集成验证点 + +- **TP_BASIC_BRN_019**(中)|搜索结果与分页总数一致|验证要点:过滤后「共 N 条」与过滤结果规模一致|关联需求:UI-SPEC-SPD-BASIC-004 + +--- + +### 新增院区 + +#### 功能验证点 + +- **TP_BASIC_BRN_020**(高)|打开新增院区弹窗|验证要点:点「新增」弹出「新增院区」,含院区名称、所在地区、详细地址、主院区标志、状态、取消/确定;**无**院区编号、**无**启用云仓|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_021**(高)|合法必填新增成功|验证要点:填写名称、地区、地址并选择主院区标志与状态后确定,弹窗关闭,列表出现新行且字段一致|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_022**(高)|主院区标志是/否|验证要点:选择「是」或「否」后保存,列表/编辑回看与所选一致|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_023**(高)|状态停用/启用|验证要点:新增选择停用或启用后,列表「状态」与所选一致|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_024**(中)|所在地区省市区级联|验证要点:选择省/市/区后提交,编辑回看地区路径与所选一致|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_025**(中)|取消不落库|验证要点:填写后点「取消」或关闭,列表不新增对应院区|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 边界验证点 + +- **TP_BASIC_BRN_026**(中)|院区名称达到最大长度|验证要点:名称取最大允许长度可保存成功|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_027**(中)|详细地址达到最大长度|验证要点:地址取最大允许长度可保存成功|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_028**(低)|主院区标志默认值|验证要点:未改主院区标志时默认「否」或产品约定默认值,提交结果与默认一致|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 异常验证点 + +- **TP_BASIC_BRN_029**(高)|院区名称为空拦截|验证要点:名称为空点确定被拦截并提示,不落库|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_030**(高)|所在地区为空拦截|验证要点:未选地区点确定被拦截并提示|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_031**(高)|详细地址为空拦截|验证要点:地址为空点确定被拦截并提示|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_032**(中)|院区名称重复|验证要点:与已有院区重名时拒绝或按产品规则提示,不产生无法区分的重名脏数据|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_033**(中)|名称/地址超长拦截|验证要点:超过最大长度时拒绝保存并提示|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_034**(中)|保存接口失败|验证要点:新增失败有提示,弹窗仍在,列表无半成品成功行|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_035**(中)|重复点击确定|验证要点:快速连点确定只产生一条院区或第二次被防重|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 集成验证点 + +- **TP_BASIC_BRN_036**(高)|新院区出现在其它模块院区下拉|验证要点:新增并启用后,统计报表等院区名称下拉能选到该院区(停用则不可选或按规则隐藏)|关联需求:UI-SPEC-SPD-BASIC-004 + +--- + +### 编辑院区 + +#### 功能验证点 + +- **TP_BASIC_BRN_037**(高)|打开编辑院区弹窗|验证要点:行内「编辑」打开「编辑院区」,回填编号、名称、地区、地址、主院区、状态,并**额外**展示启用云仓|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_038**(高)|院区编号回填且不可当新编号滥用|验证要点:编辑弹窗展示院区编号(`#form_item_id`),编号与列表「院区编号」一致;保存后编号不变(只读或改了也不应换号,以产品规则为准)|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_039**(高)|修改名称/地址后保存|验证要点:修改名称或详细地址后确定,列表与再次编辑均为新值|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_040**(高)|状态禁用/启用|验证要点:编辑态状态文案为「禁用/启用」(与新增「停用/启用」不同),保存后列表状态点与所选一致|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_041**(高)|启用云仓是/否|验证要点:编辑可改启用云仓,保存后列表「启用云仓」列与所选一致|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_042**(中)|编辑取消不保存|验证要点:修改后点「取消」,列表与再次打开仍为修改前值|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 边界验证点 + +- **TP_BASIC_BRN_043**(低)|不改字段直接确定|验证要点:打开编辑不改内容点确定,记录保持原值且不报错|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_044**(中)|主院区改为「是」时唯一性|验证要点:将某院区标为主院区后,是否只允许一个主院区;若是,原主院区应变为否或被拒绝并提示|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 异常验证点 + +- **TP_BASIC_BRN_045**(高)|编辑清空必填拦截|验证要点:清空名称、地区或地址后确定被拦截,原记录不被写成空|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_046**(中)|编辑已不存在的院区|验证要点:院区已被删除后再保存,提示不存在,不产生幽灵更新|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_047**(中)|停用/禁用后仍被业务引用|验证要点:院区仍被库存/单据引用时禁用,按产品规则拒绝或允许但下游不可新选,提示明确|关联需求:UI-SPEC-SPD-BASIC-004 + +#### 集成验证点 + +- **TP_BASIC_BRN_048**(高)|启用云仓影响云仓相关能力|验证要点:启用云仓=是后,约定的云仓功能对该院区可用;改为否后不可用|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_049**(高)|禁用院区后其它模块不可新选|验证要点:状态改为禁用/停用后,其它模块院区下拉不再提供该院区作新业务(历史单据仍可展示原名称)|关联需求:UI-SPEC-SPD-BASIC-004 +- **TP_BASIC_BRN_050**(中)|改名后下游展示同步|验证要点:修改院区名称后,已引用该院区的列表展示为新名称(或按产品约定的缓存刷新后一致)|关联需求:UI-SPEC-SPD-BASIC-004 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、名称搜索(无查询/重置)、分页、新增/编辑字段差异、主院区、状态文案差异、启用云仓、下游院区联动;共 **50** 条原子测试点 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 登录/权限与同模块其它页共用规则,本页强调新增 vs 编辑字段差;编号前缀 `TP_BASIC_BRN` | +| 待评审项 | ① 主院区是否全局唯一 ② 搜索是即时过滤还是回车 ③ 禁用已被业务引用的院区策略 ④ 院区编号编辑态是否只读 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 `npm run discover:scan-basic` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..1ff6926 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\212\202\345\201\207\346\227\245\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,69 @@ +# 基础管理-厂商管理-供应商节假日管理 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-010.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/basic/prov/holiday` +**版本 / 定稿日期**:2026-09-30 +**元素扫描**:`data/scan/vendor-holiday-elements.json`(80 项) +**定位器**:`locators/spd/vendor-holiday.locator.yaml` + +--- + +## 基础设置 · 厂商管理 · 供应商节假日管理 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_VENDOR_HLD_001**(高)|菜单路径可达供应商节假日管理|验证要点:登录后通过「基础设置 → 厂商管理 → 供应商节假日管理」进入,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-VENDOR-010 +- **TP_VENDOR_HLD_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选/主表/分页或左右分栏关键区可见|关联需求:UI-SPEC-SPD-VENDOR-010 +- **TP_VENDOR_HLD_003**(中)|同组厂商管理菜单可切换|验证要点:切到其它厂商管理页再返回供应商节假日管理,URL 与内容对应|关联需求:UI-SPEC-SPD-VENDOR-010 + +#### 边界验证点 + +- **TP_VENDOR_HLD_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/basic/prov/holiday`,页面正常渲染|关联需求:UI-SPEC-SPD-VENDOR-010 + +#### 异常验证点 + +- **TP_VENDOR_HLD_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-VENDOR-010 +- **TP_VENDOR_HLD_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录|关联需求:UI-SPEC-SPD-VENDOR-010 +- **TP_VENDOR_HLD_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-VENDOR-010 + +#### 集成验证点 + +- **TP_VENDOR_HLD_008**(中)|顶栏院区与厂商范围一致|验证要点:列表/筛选中的院区或供应商范围与顶栏院区不矛盾|关联需求:UI-SPEC-SPD-VENDOR-010 + +--- + +### 默认假日与供应商自定义 + +#### 功能验证点 + +- **TP_VENDOR_HLD_009**(高)|左列表加载|验证要点:供应商节假日设置列表展示年度、名称,可按名称/年度搜索|关联需求:UI-SPEC-SPD-VENDOR-010 +- **TP_VENDOR_HLD_010**(高)|选中行展示默认详情|验证要点:点击左侧行后右侧默认假日详情名称/年度/起止/创建人时间与选中行一致|关联需求:UI-SPEC-SPD-VENDOR-010 +- **TP_VENDOR_HLD_011**(高)|左侧新增/编辑|验证要点:新增或编辑默认假日设置后列表与详情刷新|关联需求:UI-SPEC-SPD-VENDOR-010 +- **TP_VENDOR_HLD_012**(高)|供应商自定义新增|验证要点:右侧「新增」可添加进院供应商自定义节假日|关联需求:UI-SPEC-SPD-VENDOR-010 +- **TP_VENDOR_HLD_013**(中)|批量删除|验证要点:勾选自定义行后批量删除,列表条数减少|关联需求:UI-SPEC-SPD-VENDOR-010 +- **TP_VENDOR_HLD_014**(中)|自定义列表列|验证要点:展示进院供应商编码/名称、起止、备注、创建人时间、操作|关联需求:UI-SPEC-SPD-VENDOR-010 + +#### 边界验证点 + +- **TP_VENDOR_HLD_015**(中)|起止同一天|验证要点:默认或自定义假日起止同一天可保存或按规则提示|关联需求:UI-SPEC-SPD-VENDOR-010 +- **TP_VENDOR_HLD_016**(低)|自定义列表空态|验证要点:无自定义数据时「暂无数据」明确|关联需求:UI-SPEC-SPD-VENDOR-010 + +#### 异常验证点 + +- **TP_VENDOR_HLD_017**(高)|结束早于开始|验证要点:日期非法时拒绝保存并提示|关联需求:UI-SPEC-SPD-VENDOR-010 +- **TP_VENDOR_HLD_018**(中)|未勾选批量删除|验证要点:无勾选时批量删除不可用或提示|关联需求:UI-SPEC-SPD-VENDOR-010 + +#### 集成验证点 + +- **TP_VENDOR_HLD_019**(中)|进院供应商主数据一致|验证要点:自定义列表中的进院供应商可在厂商/供应商主数据中找到|关联需求:UI-SPEC-SPD-VENDOR-010 + +--- + +## 质量自检摘要 + +- 覆盖:页面访问、筛选/Tab、列表分页、关键写操作或详情、权限与登录异常、同模块衔接。 +- 颗粒度:一条测试点只验证一件独立事;优先级已标高/中/低。 +- 待评审项:审批通过/驳回写操作是否自动化(建议只读详情冒烟);注册证差异对比 iframe 断言策略。 +- 下游:可转结构化 MD/CSV;自动化需先补 Page Object(`pages/spd/vendor-holiday.page.ts`)。 diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..38bd0d7 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\344\276\233\345\272\224\345\225\206\350\265\204\350\264\250\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,70 @@ +# 基础管理-厂商管理-供应商资质管理 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-002.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/basic/qcManage` +**版本 / 定稿日期**:2026-09-30 +**元素扫描**:`data/scan/vendor-qual-elements.json`(99 项) +**定位器**:`locators/spd/vendor-qual.locator.yaml` + +--- + +## 基础设置 · 厂商管理 · 供应商资质管理 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_VENDOR_QUL_001**(高)|菜单路径可达供应商资质管理|验证要点:登录后通过「基础设置 → 厂商管理 → 供应商资质管理」进入,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选/主表/分页或左右分栏关键区可见|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_003**(中)|同组厂商管理菜单可切换|验证要点:切到其它厂商管理页再返回供应商资质管理,URL 与内容对应|关联需求:UI-SPEC-SPD-VENDOR-002 + +#### 边界验证点 + +- **TP_VENDOR_QUL_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/basic/qcManage`,页面正常渲染|关联需求:UI-SPEC-SPD-VENDOR-002 + +#### 异常验证点 + +- **TP_VENDOR_QUL_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-VENDOR-002 + +#### 集成验证点 + +- **TP_VENDOR_QUL_008**(中)|顶栏院区与厂商范围一致|验证要点:列表/筛选中的院区或供应商范围与顶栏院区不矛盾|关联需求:UI-SPEC-SPD-VENDOR-002 + +--- + +### 证件 Tab、效期与列表 + +#### 功能验证点 + +- **TP_VENDOR_QUL_009**(高)|证件分类 Tab 切换|验证要点:证件效期提醒/授权链路效期预警/供应商三证/器械注册人/注册证号/授权书/承诺书/法人委托书/二级供应商三证可切换且列表字段随 Tab 变化|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_010**(高)|效期快捷筛选|验证要点:近三个月到期、近一个月到期、已过期切换后列表与 Tab 计数一致|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_011**(高)|证件效期提醒筛选|验证要点:按进院供应商名称、证件类型查询,结果匹配|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_012**(高)|授权链路预警筛选|验证要点:按授权书名称、授权单位、被授权单位查询,结果匹配|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_013**(中)|重置筛选|验证要点:重置后条件恢复默认,列表按默认规则刷新|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_014**(高)|行内查看|验证要点:点击「查看」打开证件/授权详情,关键字段与列表行一致|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_015**(中)|导出|验证要点:导出/导出明细与当前 Tab+筛选条件一致|关联需求:UI-SPEC-SPD-VENDOR-002 + +#### 边界验证点 + +- **TP_VENDOR_QUL_016**(中)|某效期 Tab 计数为 0|验证要点:列表空态明确,总数为 0|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_017**(低)|效期临界日|验证要点:即将到期与已过期边界日分类正确(以业务规则为准)|关联需求:UI-SPEC-SPD-VENDOR-002 + +#### 异常验证点 + +- **TP_VENDOR_QUL_018**(中)|查询失败|验证要点:接口失败有提示,不白屏|关联需求:UI-SPEC-SPD-VENDOR-002 +- **TP_VENDOR_QUL_019**(中)|导出失败|验证要点:导出失败有提示,不误导为成功|关联需求:UI-SPEC-SPD-VENDOR-002 + +#### 集成验证点 + +- **TP_VENDOR_QUL_020**(中)|效期提醒文案与有效期至一致|验证要点:「N 天后过期/即将过期」与有效期至抽样一致|关联需求:UI-SPEC-SPD-VENDOR-002 + +--- + +## 质量自检摘要 + +- 覆盖:页面访问、筛选/Tab、列表分页、关键写操作或详情、权限与登录异常、同模块衔接。 +- 颗粒度:一条测试点只验证一件独立事;优先级已标高/中/低。 +- 待评审项:审批通过/驳回写操作是否自动化(建议只读详情冒烟);注册证差异对比 iframe 断言策略。 +- 下游:可转结构化 MD/CSV;自动化需先补 Page Object(`pages/spd/vendor-qual.page.ts`)。 diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..7f0e55a --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\345\216\202\345\225\206\346\237\245\350\257\242-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,68 @@ +# 基础管理-厂商管理-厂商查询 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-001.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/factory` +**版本 / 定稿日期**:2026-09-30 +**元素扫描**:`data/scan/vendor-query-elements.json`(47 项) +**定位器**:`locators/spd/vendor-query.locator.yaml` + +--- + +## 基础设置 · 厂商管理 · 厂商查询 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_VENDOR_QRY_001**(高)|菜单路径可达厂商查询|验证要点:登录后通过「基础设置 → 厂商管理 → 厂商查询」进入,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-VENDOR-001 +- **TP_VENDOR_QRY_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选/主表/分页或左右分栏关键区可见|关联需求:UI-SPEC-SPD-VENDOR-001 +- **TP_VENDOR_QRY_003**(中)|同组厂商管理菜单可切换|验证要点:切到其它厂商管理页再返回厂商查询,URL 与内容对应|关联需求:UI-SPEC-SPD-VENDOR-001 + +#### 边界验证点 + +- **TP_VENDOR_QRY_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/factory`,页面正常渲染|关联需求:UI-SPEC-SPD-VENDOR-001 + +#### 异常验证点 + +- **TP_VENDOR_QRY_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-VENDOR-001 +- **TP_VENDOR_QRY_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录|关联需求:UI-SPEC-SPD-VENDOR-001 +- **TP_VENDOR_QRY_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-VENDOR-001 + +#### 集成验证点 + +- **TP_VENDOR_QRY_008**(中)|顶栏院区与厂商范围一致|验证要点:列表/筛选中的院区或供应商范围与顶栏院区不矛盾|关联需求:UI-SPEC-SPD-VENDOR-001 + +--- + +### Tab、搜索与列表 + +#### 功能验证点 + +- **TP_VENDOR_QRY_009**(高)|器械注册人 Tab|验证要点:切换「器械注册人」后列表为注册人数据,分页「共 N 条」与结果一致|关联需求:UI-SPEC-SPD-VENDOR-001 +- **TP_VENDOR_QRY_010**(高)|经销商 Tab|验证要点:切换「经销商」后列表为经销商数据,总数与器械注册人可不同|关联需求:UI-SPEC-SPD-VENDOR-001 +- **TP_VENDOR_QRY_011**(高)|按厂商名称搜索|验证要点:输入厂商名称关键字后结果名称匹配,无匹配时空态明确|关联需求:UI-SPEC-SPD-VENDOR-001 +- **TP_VENDOR_QRY_012**(高)|列表核心列|验证要点:展示序号、厂商编号、厂商名称、产地、详细地址、简称、开户银行、银行账号|关联需求:UI-SPEC-SPD-VENDOR-001 +- **TP_VENDOR_QRY_013**(中)|分页与每页条数|验证要点:翻页与每页条数切换正确,切换 Tab 后分页重置合理|关联需求:UI-SPEC-SPD-VENDOR-001 + +#### 边界验证点 + +- **TP_VENDOR_QRY_014**(中)|超长厂商名称关键字|验证要点:超长输入可查询或被截断并提示,不白屏|关联需求:UI-SPEC-SPD-VENDOR-001 +- **TP_VENDOR_QRY_015**(低)|仅空格关键字|验证要点:按空查询处理或提示,不报错|关联需求:UI-SPEC-SPD-VENDOR-001 + +#### 异常验证点 + +- **TP_VENDOR_QRY_016**(中)|查询接口失败|验证要点:失败有错误提示,列表不白屏|关联需求:UI-SPEC-SPD-VENDOR-001 +- **TP_VENDOR_QRY_017**(低)|无匹配空态|验证要点:总数为 0,空态文案明确|关联需求:UI-SPEC-SPD-VENDOR-001 + +#### 集成验证点 + +- **TP_VENDOR_QRY_018**(中)|Tab 与分页总数一致|验证要点:当前 Tab 下可见行逻辑与「共 N 条」一致|关联需求:UI-SPEC-SPD-VENDOR-001 + +--- + +## 质量自检摘要 + +- 覆盖:页面访问、筛选/Tab、列表分页、关键写操作或详情、权限与登录异常、同模块衔接。 +- 颗粒度:一条测试点只验证一件独立事;优先级已标高/中/低。 +- 待评审项:审批通过/驳回写操作是否自动化(建议只读详情冒烟);注册证差异对比 iframe 断言策略。 +- 下游:可转结构化 MD/CSV;自动化需先补 Page Object(`pages/spd/vendor-query.page.ts`)。 diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..abc62ee --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\345\210\235\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,70 @@ +# 基础管理-厂商管理-授权变更初审 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-003.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/authAudit` +**版本 / 定稿日期**:2026-09-30 +**元素扫描**:`data/scan/vendor-auth-first-elements.json`(40 项) +**定位器**:`locators/spd/vendor-auth-first.locator.yaml` + +--- + +## 基础设置 · 厂商管理 · 授权变更初审 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_VENDOR_AF1_001**(高)|菜单路径可达授权变更初审|验证要点:登录后通过「基础设置 → 厂商管理 → 授权变更初审」进入,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选/主表/分页或左右分栏关键区可见|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_003**(中)|同组厂商管理菜单可切换|验证要点:切到其它厂商管理页再返回授权变更初审,URL 与内容对应|关联需求:UI-SPEC-SPD-VENDOR-003 + +#### 边界验证点 + +- **TP_VENDOR_AF1_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/authAudit`,页面正常渲染|关联需求:UI-SPEC-SPD-VENDOR-003 + +#### 异常验证点 + +- **TP_VENDOR_AF1_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-VENDOR-003 + +#### 集成验证点 + +- **TP_VENDOR_AF1_008**(中)|顶栏院区与厂商范围一致|验证要点:列表/筛选中的院区或供应商范围与顶栏院区不矛盾|关联需求:UI-SPEC-SPD-VENDOR-003 + +--- + +### 授权变更列表与详情 + +#### 功能验证点 + +- **TP_VENDOR_AF1_009**(高)|制单日期查询|验证要点:按制单日期区间查询,结果制单时间落在区间内|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_010**(高)|单号与供应商筛选|验证要点:按单号、进院供应商、预转/原供应商查询,结果匹配|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_011**(高)|状态与变更类型|验证要点:状态下拉与变更类型筛选后列表状态/类型匹配|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_012**(中)|医院ID/医院名称筛选|验证要点:有该字段时按医院条件查询结果匹配|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_013**(中)|组合查询与重置|验证要点:多条件同时生效;重置恢复默认|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_014**(高)|列表核心列|验证要点:展示进院供应商名称、单号、状态、制单时间、制单人、变更类型及授权变更相关关键列、详情|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_015**(高)|打开详情|验证要点:行内「详情」展示单据头与明细,单号与列表一致|关联需求:UI-SPEC-SPD-VENDOR-003 + +#### 边界验证点 + +- **TP_VENDOR_AF1_016**(中)|制单日期起止同一天|验证要点:仅返回该日单据或空态明确|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_017**(低)|无匹配单据|验证要点:总数为 0,空态明确|关联需求:UI-SPEC-SPD-VENDOR-003 + +#### 异常验证点 + +- **TP_VENDOR_AF1_018**(中)|日期开始晚于结束|验证要点:拒绝查询或自动纠正并提示|关联需求:UI-SPEC-SPD-VENDOR-003 +- **TP_VENDOR_AF1_019**(中)|查询失败|验证要点:失败有提示,不白屏|关联需求:UI-SPEC-SPD-VENDOR-003 + +#### 集成验证点 + +- **TP_VENDOR_AF1_020**(高)|与授权变更终审单据衔接|验证要点:同一业务单在初审/终审列表状态流转一致,不串到非授权变更菜单|关联需求:UI-SPEC-SPD-VENDOR-003 + +--- + +## 质量自检摘要 + +- 覆盖:页面访问、筛选/Tab、列表分页、关键写操作或详情、权限与登录异常、同模块衔接。 +- 颗粒度:一条测试点只验证一件独立事;优先级已标高/中/低。 +- 待评审项:审批通过/驳回写操作是否自动化(建议只读详情冒烟);注册证差异对比 iframe 断言策略。 +- 下游:可转结构化 MD/CSV;自动化需先补 Page Object(`pages/spd/vendor-auth-first.page.ts`)。 diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..255dd34 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\216\210\346\235\203\345\217\230\346\233\264\347\273\210\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,69 @@ +# 基础管理-厂商管理-授权变更终审 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-004.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/authAuditFinal` +**版本 / 定稿日期**:2026-09-30 +**元素扫描**:`data/scan/vendor-auth-final-elements.json`(40 项) +**定位器**:`locators/spd/vendor-auth-final.locator.yaml` + +--- + +## 基础设置 · 厂商管理 · 授权变更终审 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_VENDOR_AF2_001**(高)|菜单路径可达授权变更终审|验证要点:登录后通过「基础设置 → 厂商管理 → 授权变更终审」进入,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-VENDOR-004 +- **TP_VENDOR_AF2_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选/主表/分页或左右分栏关键区可见|关联需求:UI-SPEC-SPD-VENDOR-004 +- **TP_VENDOR_AF2_003**(中)|同组厂商管理菜单可切换|验证要点:切到其它厂商管理页再返回授权变更终审,URL 与内容对应|关联需求:UI-SPEC-SPD-VENDOR-004 + +#### 边界验证点 + +- **TP_VENDOR_AF2_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/authAuditFinal`,页面正常渲染|关联需求:UI-SPEC-SPD-VENDOR-004 + +#### 异常验证点 + +- **TP_VENDOR_AF2_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-VENDOR-004 +- **TP_VENDOR_AF2_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录|关联需求:UI-SPEC-SPD-VENDOR-004 +- **TP_VENDOR_AF2_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-VENDOR-004 + +#### 集成验证点 + +- **TP_VENDOR_AF2_008**(中)|顶栏院区与厂商范围一致|验证要点:列表/筛选中的院区或供应商范围与顶栏院区不矛盾|关联需求:UI-SPEC-SPD-VENDOR-004 + +--- + +### 授权变更列表与详情 + +#### 功能验证点 + +- **TP_VENDOR_AF2_009**(高)|制单日期查询|验证要点:按制单日期区间查询,结果制单时间落在区间内|关联需求:UI-SPEC-SPD-VENDOR-004 +- **TP_VENDOR_AF2_010**(高)|单号与供应商筛选|验证要点:按单号、进院供应商、预转/原供应商查询,结果匹配|关联需求:UI-SPEC-SPD-VENDOR-004 +- **TP_VENDOR_AF2_011**(高)|状态与变更类型|验证要点:状态下拉与变更类型筛选后列表状态/类型匹配|关联需求:UI-SPEC-SPD-VENDOR-004 +- **TP_VENDOR_AF2_012**(中)|组合查询与重置|验证要点:多条件同时生效;重置恢复默认|关联需求:UI-SPEC-SPD-VENDOR-004 +- **TP_VENDOR_AF2_013**(高)|列表核心列|验证要点:展示进院供应商名称、单号、状态、制单时间、制单人、变更类型及授权变更相关关键列、详情|关联需求:UI-SPEC-SPD-VENDOR-004 +- **TP_VENDOR_AF2_014**(高)|打开详情|验证要点:行内「详情」展示单据头与明细,单号与列表一致|关联需求:UI-SPEC-SPD-VENDOR-004 + +#### 边界验证点 + +- **TP_VENDOR_AF2_015**(中)|制单日期起止同一天|验证要点:仅返回该日单据或空态明确|关联需求:UI-SPEC-SPD-VENDOR-004 +- **TP_VENDOR_AF2_016**(低)|无匹配单据|验证要点:总数为 0,空态明确|关联需求:UI-SPEC-SPD-VENDOR-004 + +#### 异常验证点 + +- **TP_VENDOR_AF2_017**(中)|日期开始晚于结束|验证要点:拒绝查询或自动纠正并提示|关联需求:UI-SPEC-SPD-VENDOR-004 +- **TP_VENDOR_AF2_018**(中)|查询失败|验证要点:失败有提示,不白屏|关联需求:UI-SPEC-SPD-VENDOR-004 + +#### 集成验证点 + +- **TP_VENDOR_AF2_019**(高)|与授权变更初审单据衔接|验证要点:同一业务单在初审/终审列表状态流转一致,不串到非授权变更菜单|关联需求:UI-SPEC-SPD-VENDOR-004 + +--- + +## 质量自检摘要 + +- 覆盖:页面访问、筛选/Tab、列表分页、关键写操作或详情、权限与登录异常、同模块衔接。 +- 颗粒度:一条测试点只验证一件独立事;优先级已标高/中/低。 +- 待评审项:审批通过/驳回写操作是否自动化(建议只读详情冒烟);注册证差异对比 iframe 断言策略。 +- 下游:可转结构化 MD/CSV;自动化需先补 Page Object(`pages/spd/vendor-auth-final.page.ts`)。 diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..93ef8cd --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\345\210\235\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,69 @@ +# 基础管理-厂商管理-注册证变更初审 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-005.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/regCertAudit` +**版本 / 定稿日期**:2026-09-30 +**元素扫描**:`data/scan/vendor-cert-first-elements.json`(40 项) +**定位器**:`locators/spd/vendor-cert-first.locator.yaml` + +--- + +## 基础设置 · 厂商管理 · 注册证变更初审 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_VENDOR_CF1_001**(高)|菜单路径可达注册证变更初审|验证要点:登录后通过「基础设置 → 厂商管理 → 注册证变更初审」进入,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-VENDOR-005 +- **TP_VENDOR_CF1_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选/主表/分页或左右分栏关键区可见|关联需求:UI-SPEC-SPD-VENDOR-005 +- **TP_VENDOR_CF1_003**(中)|同组厂商管理菜单可切换|验证要点:切到其它厂商管理页再返回注册证变更初审,URL 与内容对应|关联需求:UI-SPEC-SPD-VENDOR-005 + +#### 边界验证点 + +- **TP_VENDOR_CF1_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/regCertAudit`,页面正常渲染|关联需求:UI-SPEC-SPD-VENDOR-005 + +#### 异常验证点 + +- **TP_VENDOR_CF1_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-VENDOR-005 +- **TP_VENDOR_CF1_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录|关联需求:UI-SPEC-SPD-VENDOR-005 +- **TP_VENDOR_CF1_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-VENDOR-005 + +#### 集成验证点 + +- **TP_VENDOR_CF1_008**(中)|顶栏院区与厂商范围一致|验证要点:列表/筛选中的院区或供应商范围与顶栏院区不矛盾|关联需求:UI-SPEC-SPD-VENDOR-005 + +--- + +### 注册证变更列表与详情 + +#### 功能验证点 + +- **TP_VENDOR_CF1_009**(高)|制单日期查询|验证要点:按制单日期区间查询,结果制单时间落在区间内|关联需求:UI-SPEC-SPD-VENDOR-005 +- **TP_VENDOR_CF1_010**(高)|单号与注册证号筛选|验证要点:按单号、注册证号(新)、变更类型、状态查询,结果匹配|关联需求:UI-SPEC-SPD-VENDOR-005 +- **TP_VENDOR_CF1_011**(中)|变更情况说明筛选|验证要点:有该字段时按说明关键字查询或精确匹配|关联需求:UI-SPEC-SPD-VENDOR-005 +- **TP_VENDOR_CF1_012**(中)|组合查询与重置|验证要点:多条件同时生效;重置恢复默认;收起后再展开值保留|关联需求:UI-SPEC-SPD-VENDOR-005 +- **TP_VENDOR_CF1_013**(高)|列表核心列|验证要点:展示进院供应商名称、单号、状态、制单时间、制单人、变更类型、注册证号(新)、详情|关联需求:UI-SPEC-SPD-VENDOR-005 +- **TP_VENDOR_CF1_014**(高)|打开详情|验证要点:行内「详情」展示变更前后注册证信息,单号与列表一致|关联需求:UI-SPEC-SPD-VENDOR-005 + +#### 边界验证点 + +- **TP_VENDOR_CF1_015**(中)|制单日期起止同一天|验证要点:仅返回该日单据或空态明确|关联需求:UI-SPEC-SPD-VENDOR-005 +- **TP_VENDOR_CF1_016**(低)|无匹配单据|验证要点:总数为 0,空态明确|关联需求:UI-SPEC-SPD-VENDOR-005 + +#### 异常验证点 + +- **TP_VENDOR_CF1_017**(中)|日期开始晚于结束|验证要点:拒绝查询或自动纠正并提示|关联需求:UI-SPEC-SPD-VENDOR-005 +- **TP_VENDOR_CF1_018**(中)|查询失败|验证要点:失败有提示,不白屏|关联需求:UI-SPEC-SPD-VENDOR-005 + +#### 集成验证点 + +- **TP_VENDOR_CF1_019**(高)|与注册证变更终审单据衔接|验证要点:同一业务单在初审/终审列表状态流转一致,不串到非注册证变更菜单|关联需求:UI-SPEC-SPD-VENDOR-005 + +--- + +## 质量自检摘要 + +- 覆盖:页面访问、筛选/Tab、列表分页、关键写操作或详情、权限与登录异常、同模块衔接。 +- 颗粒度:一条测试点只验证一件独立事;优先级已标高/中/低。 +- 待评审项:审批通过/驳回写操作是否自动化(建议只读详情冒烟);注册证差异对比 iframe 断言策略。 +- 下游:可转结构化 MD/CSV;自动化需先补 Page Object(`pages/spd/vendor-cert-first.page.ts`)。 diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..a0d6906 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\217\230\346\233\264\347\273\210\345\256\241-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,69 @@ +# 基础管理-厂商管理-注册证变更终审 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-006.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal` +**版本 / 定稿日期**:2026-09-30 +**元素扫描**:`data/scan/vendor-cert-final-elements.json`(40 项) +**定位器**:`locators/spd/vendor-cert-final.locator.yaml` + +--- + +## 基础设置 · 厂商管理 · 注册证变更终审 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_VENDOR_CF2_001**(高)|菜单路径可达注册证变更终审|验证要点:登录后通过「基础设置 → 厂商管理 → 注册证变更终审」进入,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-VENDOR-006 +- **TP_VENDOR_CF2_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选/主表/分页或左右分栏关键区可见|关联需求:UI-SPEC-SPD-VENDOR-006 +- **TP_VENDOR_CF2_003**(中)|同组厂商管理菜单可切换|验证要点:切到其它厂商管理页再返回注册证变更终审,URL 与内容对应|关联需求:UI-SPEC-SPD-VENDOR-006 + +#### 边界验证点 + +- **TP_VENDOR_CF2_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/regCertAuditFinal`,页面正常渲染|关联需求:UI-SPEC-SPD-VENDOR-006 + +#### 异常验证点 + +- **TP_VENDOR_CF2_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-VENDOR-006 +- **TP_VENDOR_CF2_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录|关联需求:UI-SPEC-SPD-VENDOR-006 +- **TP_VENDOR_CF2_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-VENDOR-006 + +#### 集成验证点 + +- **TP_VENDOR_CF2_008**(中)|顶栏院区与厂商范围一致|验证要点:列表/筛选中的院区或供应商范围与顶栏院区不矛盾|关联需求:UI-SPEC-SPD-VENDOR-006 + +--- + +### 注册证变更列表与详情 + +#### 功能验证点 + +- **TP_VENDOR_CF2_009**(高)|制单日期查询|验证要点:按制单日期区间查询,结果制单时间落在区间内|关联需求:UI-SPEC-SPD-VENDOR-006 +- **TP_VENDOR_CF2_010**(高)|单号与注册证号筛选|验证要点:按单号、注册证号(新)、变更类型、状态查询,结果匹配|关联需求:UI-SPEC-SPD-VENDOR-006 +- **TP_VENDOR_CF2_011**(中)|变更情况说明筛选|验证要点:有该字段时按说明关键字查询或精确匹配|关联需求:UI-SPEC-SPD-VENDOR-006 +- **TP_VENDOR_CF2_012**(中)|组合查询与重置|验证要点:多条件同时生效;重置恢复默认;收起后再展开值保留|关联需求:UI-SPEC-SPD-VENDOR-006 +- **TP_VENDOR_CF2_013**(高)|列表核心列|验证要点:展示进院供应商名称、单号、状态、制单时间、制单人、变更类型、注册证号(新)、详情|关联需求:UI-SPEC-SPD-VENDOR-006 +- **TP_VENDOR_CF2_014**(高)|打开详情|验证要点:行内「详情」展示变更前后注册证信息,单号与列表一致|关联需求:UI-SPEC-SPD-VENDOR-006 + +#### 边界验证点 + +- **TP_VENDOR_CF2_015**(中)|制单日期起止同一天|验证要点:仅返回该日单据或空态明确|关联需求:UI-SPEC-SPD-VENDOR-006 +- **TP_VENDOR_CF2_016**(低)|无匹配单据|验证要点:总数为 0,空态明确|关联需求:UI-SPEC-SPD-VENDOR-006 + +#### 异常验证点 + +- **TP_VENDOR_CF2_017**(中)|日期开始晚于结束|验证要点:拒绝查询或自动纠正并提示|关联需求:UI-SPEC-SPD-VENDOR-006 +- **TP_VENDOR_CF2_018**(中)|查询失败|验证要点:失败有提示,不白屏|关联需求:UI-SPEC-SPD-VENDOR-006 + +#### 集成验证点 + +- **TP_VENDOR_CF2_019**(高)|与注册证变更初审单据衔接|验证要点:同一业务单在初审/终审列表状态流转一致,不串到非注册证变更菜单|关联需求:UI-SPEC-SPD-VENDOR-006 + +--- + +## 质量自检摘要 + +- 覆盖:页面访问、筛选/Tab、列表分页、关键写操作或详情、权限与登录异常、同模块衔接。 +- 颗粒度:一条测试点只验证一件独立事;优先级已标高/中/低。 +- 待评审项:审批通过/驳回写操作是否自动化(建议只读详情冒烟);注册证差异对比 iframe 断言策略。 +- 下游:可转结构化 MD/CSV;自动化需先补 Page Object(`pages/spd/vendor-cert-final.page.ts`)。 diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..d88afa8 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\346\263\250\345\206\214\350\257\201\345\267\256\345\274\202\345\257\271\346\257\224-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,72 @@ +# 基础管理-厂商管理-注册证差异对比 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-007.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/scjs/yc/certCompare` +**版本 / 定稿日期**:2026-09-30 +**元素扫描**:`data/scan/vendor-cert-diff-elements.json`(54 项) +**定位器**:`locators/spd/vendor-cert-diff.locator.yaml` + +--- + +## 基础设置 · 厂商管理 · 注册证差异对比 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_VENDOR_CDF_001**(高)|菜单路径可达注册证差异对比|验证要点:登录后通过「基础设置 → 厂商管理 → 注册证差异对比」进入,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选/主表/分页或左右分栏关键区可见|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_003**(中)|同组厂商管理菜单可切换|验证要点:切到其它厂商管理页再返回注册证差异对比,URL 与内容对应|关联需求:UI-SPEC-SPD-VENDOR-007 + +#### 边界验证点 + +- **TP_VENDOR_CDF_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/scjs/yc/certCompare`,页面正常渲染|关联需求:UI-SPEC-SPD-VENDOR-007 + +#### 异常验证点 + +- **TP_VENDOR_CDF_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-VENDOR-007 + +#### 集成验证点 + +- **TP_VENDOR_CDF_008**(中)|顶栏院区与厂商范围一致|验证要点:列表/筛选中的院区或供应商范围与顶栏院区不矛盾|关联需求:UI-SPEC-SPD-VENDOR-007 + +--- + +### 差异筛选与对比结果 + +#### 功能验证点 + +- **TP_VENDOR_CDF_009**(高)|默认查询|验证要点:进入后查询有数据或空态明确,分页总数正确|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_010**(高)|物料/规格/厂家筛选|验证要点:按物料名称、规格型号、生产厂家查询,结果匹配|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_011**(高)|望海与 FLI+ 注册证号筛选|验证要点:按两侧注册证号查询,结果匹配|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_012**(中)|是否医疗器械与状态|验证要点:状态下拉与是否医疗器械条件生效|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_013**(高)|一致/不一致 Tab|验证要点:全部、一致、不一致切换后对比结论匹配,计数合理|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_014**(中)|立即更新|验证要点:点击立即更新后时间戳刷新或提示成功,列表可刷新|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_015**(中)|导出|验证要点:导出与当前筛选+Tab 条件一致|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_016**(高)|对比列完整|验证要点:展示物料编号/名称、规格、厂家、医保编码及望海/FLI+注册证号与效期字段|关联需求:UI-SPEC-SPD-VENDOR-007 + +#### 边界验证点 + +- **TP_VENDOR_CDF_017**(中)|不一致 Tab 无数据|验证要点:空态明确,总数为 0|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_018**(低)|包含长期有效勾选项|验证要点:勾选「包含FLI+长期有效数据」后结果集变化符合规则|关联需求:UI-SPEC-SPD-VENDOR-007 + +#### 异常验证点 + +- **TP_VENDOR_CDF_019**(中)|查询或更新失败|验证要点:失败有提示,不白屏|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_020**(中)|iframe/内嵌页加载失败|验证要点:内嵌对比页失败有提示,外壳不假成功|关联需求:UI-SPEC-SPD-VENDOR-007 + +#### 集成验证点 + +- **TP_VENDOR_CDF_021**(高)|一致行两侧关键字段抽样一致|验证要点:标记为一致的行望海与 FLI+ 注册证号/效期抽样相同|关联需求:UI-SPEC-SPD-VENDOR-007 +- **TP_VENDOR_CDF_022**(高)|不一致行至少一侧差异|验证要点:标记为不一致的行两侧字段至少一处不同|关联需求:UI-SPEC-SPD-VENDOR-007 + +--- + +## 质量自检摘要 + +- 覆盖:页面访问、筛选/Tab、列表分页、关键写操作或详情、权限与登录异常、同模块衔接。 +- 颗粒度:一条测试点只验证一件独立事;优先级已标高/中/低。 +- 待评审项:审批通过/驳回写操作是否自动化(建议只读详情冒烟);注册证差异对比 iframe 断言策略。 +- 下游:可转结构化 MD/CSV;自动化需先补 Page Object(`pages/spd/vendor-cert-diff.page.ts`)。 diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..a38d422 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\214\207\346\240\207\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,70 @@ +# 基础管理-厂商管理-评价指标维护 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-009.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/indicators` +**版本 / 定稿日期**:2026-09-30 +**元素扫描**:`data/scan/vendor-eval-indicator-elements.json`(151 项) +**定位器**:`locators/spd/vendor-eval-indicator.locator.yaml` + +--- + +## 基础设置 · 厂商管理 · 评价指标维护 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_VENDOR_EVI_001**(高)|菜单路径可达评价指标维护|验证要点:登录后通过「基础设置 → 厂商管理 → 评价指标维护」进入,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选/主表/分页或左右分栏关键区可见|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_003**(中)|同组厂商管理菜单可切换|验证要点:切到其它厂商管理页再返回评价指标维护,URL 与内容对应|关联需求:UI-SPEC-SPD-VENDOR-009 + +#### 边界验证点 + +- **TP_VENDOR_EVI_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/indicators`,页面正常渲染|关联需求:UI-SPEC-SPD-VENDOR-009 + +#### 异常验证点 + +- **TP_VENDOR_EVI_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-VENDOR-009 + +#### 集成验证点 + +- **TP_VENDOR_EVI_008**(中)|顶栏院区与厂商范围一致|验证要点:列表/筛选中的院区或供应商范围与顶栏院区不矛盾|关联需求:UI-SPEC-SPD-VENDOR-009 + +--- + +### 指标分类树与指标列表 + +#### 功能验证点 + +- **TP_VENDOR_EVI_009**(高)|左树加载|验证要点:指标分类树可见,可搜索分类名称|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_010**(高)|选中分类刷新右表|验证要点:点击分类后右侧列表为该分类指标|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_011**(高)|指标编码/名称筛选|验证要点:右侧按编码、名称查询结果匹配|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_012**(高)|列表核心列|验证要点:展示评价类型、指标编码、指标名称、状态、操作(编辑/详情/日志)|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_013**(高)|新增分类|验证要点:左侧「新增」可创建分类,树中出现新节点|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_014**(高)|新增指标|验证要点:右侧「新增」可创建指标并回填到列表|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_015**(中)|编辑/详情/日志|验证要点:行内编辑打开回填;详情只读;日志可打开|关联需求:UI-SPEC-SPD-VENDOR-009 + +#### 边界验证点 + +- **TP_VENDOR_EVI_016**(中)|分类下无指标|验证要点:右表空态明确,总数为 0|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_017**(低)|分类名超长|验证要点:超长被拦截或截断并提示|关联需求:UI-SPEC-SPD-VENDOR-009 + +#### 异常验证点 + +- **TP_VENDOR_EVI_018**(高)|必填为空拦截|验证要点:指标编码/名称为空提交失败并提示|关联需求:UI-SPEC-SPD-VENDOR-009 +- **TP_VENDOR_EVI_019**(中)|删除或停用受引用指标|验证要点:若被方案引用则拒绝或提示(以产品规则为准)|关联需求:UI-SPEC-SPD-VENDOR-009 + +#### 集成验证点 + +- **TP_VENDOR_EVI_020**(中)|状态启用后可供方案使用|验证要点:状态为启用的指标可在评价方案中被选择|关联需求:UI-SPEC-SPD-VENDOR-009 + +--- + +## 质量自检摘要 + +- 覆盖:页面访问、筛选/Tab、列表分页、关键写操作或详情、权限与登录异常、同模块衔接。 +- 颗粒度:一条测试点只验证一件独立事;优先级已标高/中/低。 +- 待评审项:审批通过/驳回写操作是否自动化(建议只读详情冒烟);注册证差异对比 iframe 断言策略。 +- 下游:可转结构化 MD/CSV;自动化需先补 Page Object(`pages/spd/vendor-eval-indicator.page.ts`)。 diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..522eb77 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\216\202\345\225\206\347\256\241\347\220\206-\350\257\204\344\273\267\346\226\271\346\241\210\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,68 @@ +# 基础管理-厂商管理-评价方案维护 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-VENDOR-008.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/evaluationPlan` +**版本 / 定稿日期**:2026-09-30 +**元素扫描**:`data/scan/vendor-eval-scheme-elements.json`(57 项) +**定位器**:`locators/spd/vendor-eval-scheme.locator.yaml` + +--- + +## 基础设置 · 厂商管理 · 评价方案维护 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_VENDOR_EVS_001**(高)|菜单路径可达评价方案维护|验证要点:登录后通过「基础设置 → 厂商管理 → 评价方案维护」进入,菜单与 Tab 高亮正确|关联需求:UI-SPEC-SPD-VENDOR-008 +- **TP_VENDOR_EVS_002**(高)|页面主区加载完成|验证要点:整页渲染,筛选/主表/分页或左右分栏关键区可见|关联需求:UI-SPEC-SPD-VENDOR-008 +- **TP_VENDOR_EVS_003**(中)|同组厂商管理菜单可切换|验证要点:切到其它厂商管理页再返回评价方案维护,URL 与内容对应|关联需求:UI-SPEC-SPD-VENDOR-008 + +#### 边界验证点 + +- **TP_VENDOR_EVS_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `http://spdtest.cmic.com.cn:8080/spd/basic/mfrs/paltform/mfrsName/evaluationPlan`,页面正常渲染|关联需求:UI-SPEC-SPD-VENDOR-008 + +#### 异常验证点 + +- **TP_VENDOR_EVS_005**(高)|未登录访问拦截|验证要点:无登录态访问跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-VENDOR-008 +- **TP_VENDOR_EVS_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录|关联需求:UI-SPEC-SPD-VENDOR-008 +- **TP_VENDOR_EVS_007**(中)|无菜单权限|验证要点:无权限账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-VENDOR-008 + +#### 集成验证点 + +- **TP_VENDOR_EVS_008**(中)|顶栏院区与厂商范围一致|验证要点:列表/筛选中的院区或供应商范围与顶栏院区不矛盾|关联需求:UI-SPEC-SPD-VENDOR-008 + +--- + +### 方案筛选与维护 + +#### 功能验证点 + +- **TP_VENDOR_EVS_009**(高)|按创建/启用日期筛选|验证要点:按日期区间查询结果落在区间内|关联需求:UI-SPEC-SPD-VENDOR-008 +- **TP_VENDOR_EVS_010**(高)|方案编码/名称筛选|验证要点:按编码、名称查询结果匹配|关联需求:UI-SPEC-SPD-VENDOR-008 +- **TP_VENDOR_EVS_011**(中)|重置|验证要点:重置后条件恢复默认|关联需求:UI-SPEC-SPD-VENDOR-008 +- **TP_VENDOR_EVS_012**(高)|打开新增|验证要点:点击「新增」进入方案编辑,含方案编码/名称等必填区|关联需求:UI-SPEC-SPD-VENDOR-008 +- **TP_VENDOR_EVS_013**(高)|列表核心列|验证要点:展示方案编码、名称、创建人/时间、启用人/时间、停用人/时间、状态、备注、操作|关联需求:UI-SPEC-SPD-VENDOR-008 +- **TP_VENDOR_EVS_014**(中)|空列表态|验证要点:无数据时「暂无数据」空态明确|关联需求:UI-SPEC-SPD-VENDOR-008 + +#### 边界验证点 + +- **TP_VENDOR_EVS_015**(中)|起止同一天|验证要点:仅返回该日创建/启用的方案|关联需求:UI-SPEC-SPD-VENDOR-008 + +#### 异常验证点 + +- **TP_VENDOR_EVS_016**(高)|必填为空提交拦截|验证要点:方案编码或名称为空时提交失败并提示|关联需求:UI-SPEC-SPD-VENDOR-008 +- **TP_VENDOR_EVS_017**(中)|查询失败|验证要点:失败有提示|关联需求:UI-SPEC-SPD-VENDOR-008 + +#### 集成验证点 + +- **TP_VENDOR_EVS_018**(中)|方案与评价指标可关联|验证要点:方案引用的指标在评价指标维护中存在或可配置|关联需求:UI-SPEC-SPD-VENDOR-008 + +--- + +## 质量自检摘要 + +- 覆盖:页面访问、筛选/Tab、列表分页、关键写操作或详情、权限与登录异常、同模块衔接。 +- 颗粒度:一条测试点只验证一件独立事;优先级已标高/中/低。 +- 待评审项:审批通过/驳回写操作是否自动化(建议只读详情冒烟);注册证差异对比 iframe 断言策略。 +- 下游:可转结构化 MD/CSV;自动化需先补 Page Object(`pages/spd/vendor-eval-scheme.page.ts`)。 diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..42bd1b0 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\345\272\223\345\214\272\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,257 @@ +# 基础管理-库房管理-库区设置 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-WH-001.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/storehouse/platform/storehouse/area` +**版本 / 定稿日期**:2026-10-20 +**元素扫描**:`data/scan/warehouse-zone-elements.json`(106 项) +**定位器**:`locators/spd/warehouse-zone.locator.yaml` + +--- + +## 基础设置 · 库房管理 · 库区设置 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_WH_ZON_001**(高)|菜单路径可达库区设置|验证要点:登录后通过「基础设置 → 库房管理 → 库区设置」进入 `/spd/basic/storehouse/platform/storehouse/area`,左侧菜单与顶栏 Tab 高亮为库区设置,不串到同组其它页|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_002**(高)|页面主区加载完成|验证要点:左右分栏:左侧「库房信息」树、右侧库区列表、新增与分页可见,无报错遮罩|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_003**(中)|同组库房管理菜单可切换|验证要点:在 库区设置 / 探针绑定 间切换后 URL 与页面内容对应,再返回库区设置状态正确|关联需求:UI-SPEC-SPD-WH-001 + +#### 边界验证点 + +- **TP_WH_ZON_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/storehouse/platform/storehouse/area`,页面正常渲染|关联需求:UI-SPEC-SPD-WH-001 + +#### 异常验证点 + +- **TP_WH_ZON_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录,不误写|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_007**(中)|无菜单权限|验证要点:无库区设置权限的账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-WH-001 + +#### 集成验证点 + +- **TP_WH_ZON_008**(中)|顶栏身份与可管理范围一致|验证要点:顶栏当前库房/院区与本页可维护库房范围不矛盾|关联需求:UI-SPEC-SPD-WH-001 + +--- + +### 左侧库房树 + +#### 功能验证点 + +- **TP_WH_ZON_009**(高)|库房树默认有选中项|验证要点:进入后左侧「库房信息」有节点且一项处于选中,右侧为该库房的库区|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_010**(高)|按库房名称过滤树|验证要点:在左侧「库房名称」输入关键字后,仅保留名称匹配的库房节点|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_011**(中)|清空库房名称恢复全量树|验证要点:清空关键字后树恢复为过滤前的全量节点|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_012**(高)|切换库房刷新右侧列表|验证要点:点选另一库房后,右侧库区编号/名称/地址属于该库房,「共 N 条」同步变化|关联需求:UI-SPEC-SPD-WH-001 + +#### 边界验证点 + +- **TP_WH_ZON_013**(中)|超长库房名称关键字|验证要点:输入达到或超过允许长度,过滤被接受或截断,页面不崩溃|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_014**(低)|仅空格库房名称|验证要点:按空过滤处理或提示,不报错|关联需求:UI-SPEC-SPD-WH-001 + +#### 异常验证点 + +- **TP_WH_ZON_015**(低)|无匹配库房空树|验证要点:关键字无匹配时树为空或明确无数据,右侧列表不残留上一库房库区|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_016**(中)|库房树加载失败|验证要点:库房接口失败有错误提示,不把过期树展示为成功态|关联需求:UI-SPEC-SPD-WH-001 + +#### 集成验证点 + +- **TP_WH_ZON_017**(中)|库房节点与院区主数据同源|验证要点:树中库房名称与院区/库房主数据一致(允许按权限裁剪)|关联需求:UI-SPEC-SPD-WH-001 + +--- + +### 右侧库区列表与分页 + +#### 功能验证点 + +- **TP_WH_ZON_018**(高)|列表核心列完整|验证要点:主表展示库区编号、库区名称、库区地址、是否专项库区、操作(编辑/人员管理/货位管理/停用或启用)|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_019**(高)|默认库区以标签展示|验证要点:默认库区在名称旁显示「默认库区」标签,不是独立列;同一库房至多一个默认库区|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_020**(中)|专项库区取值|验证要点:「是否专项库区」仅为「是」或「否」|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_021**(中)|分页翻页|验证要点:总条数大于一页时可翻至第 2 页,数据更换且页码高亮正确|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_022**(中)|每页条数切换|验证要点:切换「50 条/页」等后当前页行数不超过所选值,总数不变|关联需求:UI-SPEC-SPD-WH-001 + +#### 边界验证点 + +- **TP_WH_ZON_023**(中)|首页上一页不可用|验证要点:第 1 页时「上一页」禁用或点击无效|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_024**(中)|末页下一页不可用|验证要点:最后一页时「下一页」禁用或点击无效|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_025**(低)|库房无库区时分页为 0|验证要点:选中尚无库区的库房时列表空态明确,「共 0 条」|关联需求:UI-SPEC-SPD-WH-001 + +#### 异常验证点 + +- **TP_WH_ZON_026**(中)|库区列表加载失败|验证要点:失败有错误提示,不白屏、不把上一库房数据展示为当前库房成功态|关联需求:UI-SPEC-SPD-WH-001 + +#### 集成验证点 + +- **TP_WH_ZON_027**(中)|翻页不串库房|验证要点:带当前库房选中翻页后,新页库区仍属于该库房|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_028**(中)|列表条数与共 N 条一致|验证要点:可见行逻辑与底部分页「共 N 条」一致|关联需求:UI-SPEC-SPD-WH-001 + +--- + +### 库区搜索 + +#### 功能验证点 + +- **TP_WH_ZON_029**(高)|按库区编号搜索|验证要点:在「库区编号/名称」输入已存在编号,结果包含该编号行|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_030**(高)|按库区名称搜索|验证要点:输入已存在名称关键字,结果名称匹配|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_031**(中)|搜索范围限于当前库房|验证要点:搜索结果全部属于左侧当前选中库房|关联需求:UI-SPEC-SPD-WH-001 + +#### 边界验证点 + +- **TP_WH_ZON_032**(中)|超长库区关键字|验证要点:超长输入可检索或被截断,页面不崩溃|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_033**(低)|仅空格库区关键字|验证要点:按空搜索处理或提示,不报错|关联需求:UI-SPEC-SPD-WH-001 + +#### 异常验证点 + +- **TP_WH_ZON_034**(低)|无匹配库区空态|验证要点:无命中时列表为空且分页为 0|关联需求:UI-SPEC-SPD-WH-001 + +#### 集成验证点 + +- **TP_WH_ZON_035**(中)|切换库房后搜索条件不串结果|验证要点:换库房后结果属于新库房;若保留关键字则在新库房范围内过滤|关联需求:UI-SPEC-SPD-WH-001 + +--- + +### 新增库区 + +#### 功能验证点 + +- **TP_WH_ZON_036**(高)|打开新增库区弹窗|验证要点:点「新增」弹出「新增库区」,含库区编号、库区名称、库区地址、联系人、联系方式及取消/确定|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_037**(高)|合法必填保存成功|验证要点:填写必填后确定,弹窗关闭,当前库房列表出现新记录且字段与提交一致|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_038**(中)|取消不落库|验证要点:填写后点「取消」或关闭,列表不新增对应库区|关联需求:UI-SPEC-SPD-WH-001 + +#### 边界验证点 + +- **TP_WH_ZON_039**(中)|编号名称达最大允许长度|验证要点:库区编号/名称取最大允许长度可保存成功|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_040**(低)|地址可与编号或名称相同|验证要点:库区地址填写与编号或名称相同的文本可保存|关联需求:UI-SPEC-SPD-WH-001 + +#### 异常验证点 + +- **TP_WH_ZON_041**(高)|必填为空拦截|验证要点:库区编号或名称为空点确定被拦截并提示,不关闭弹窗、不落库|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_042**(高)|同一库房库区编号重复拦截|验证要点:使用当前库房已存在编号保存失败并提示,不覆盖原库区|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_043**(中)|保存接口失败|验证要点:失败有错误提示,弹窗仍在,列表不出现半成品成功态|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_044**(中)|重复点击确定|验证要点:连续快速点击只产生一条记录或第二次被防重|关联需求:UI-SPEC-SPD-WH-001 + +#### 集成验证点 + +- **TP_WH_ZON_045**(中)|新增归属当前选中库房|验证要点:新增成功后该库区仅出现在当前库房列表,切换其它库房不可见|关联需求:UI-SPEC-SPD-WH-001 + +--- + +### 编辑库区 + +#### 功能验证点 + +- **TP_WH_ZON_046**(高)|打开编辑并回填|验证要点:行内「编辑」打开「编辑库区信息」,回填库区编号、名称、地址、联系人、联系方式|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_047**(高)|修改后确定落库|验证要点:修改名称或地址后确定,列表与再次编辑回看均为新值|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_048**(中)|编辑取消不保存|验证要点:修改后点「取消」,列表与再次打开仍为修改前值|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_049**(中)|专项库区回填一致|验证要点:列表「是否专项库区」与编辑页回填一致(若编辑页含该字段)|关联需求:UI-SPEC-SPD-WH-001 + +#### 边界验证点 + +- **TP_WH_ZON_050**(低)|不改字段直接确定|验证要点:打开编辑不改内容点确定,记录保持原值且不报错|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_051**(中)|默认库区编辑后仍唯一|验证要点:编辑保存后该库房仍至多一个「默认库区」标签|关联需求:UI-SPEC-SPD-WH-001 + +#### 异常验证点 + +- **TP_WH_ZON_052**(高)|编辑清空必填拦截|验证要点:清空库区名称后确定被拦截,原记录不被写成空|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_053**(中)|编辑已停用库区|验证要点:停用行仍可打开编辑;若禁止编辑则入口禁用并提示,不以成功态改数|关联需求:UI-SPEC-SPD-WH-001 + +#### 集成验证点 + +- **TP_WH_ZON_054**(中)|编辑不影响其它库房同名库区|验证要点:仅更新当前库房该行,其它库房同编号/名称库区保持原值|关联需求:UI-SPEC-SPD-WH-001 + +--- + +### 停用与启用 + +#### 功能验证点 + +- **TP_WH_ZON_055**(高)|停用后操作变为启用|验证要点:启用行点「停用」后该行操作变为「启用」,状态持久化|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_056**(高)|启用后操作变为停用|验证要点:停用行点「启用」后该行操作变为「停用」,再次进入仍保持|关联需求:UI-SPEC-SPD-WH-001 + +#### 边界验证点 + +- **TP_WH_ZON_057**(中)|默认库区停用限制|验证要点:默认库区若不允许停用则被拦截并提示;若允许停用则该库房须仍满足「至多一个默认库区」|关联需求:UI-SPEC-SPD-WH-001 + +#### 异常验证点 + +- **TP_WH_ZON_058**(中)|停用/启用失败有提示|验证要点:接口失败有错误提示,行操作文案不翻转|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_059**(中)|停用须确认才生效|验证要点:若有确认框,取消后行仍为启用、操作仍为「停用」|关联需求:UI-SPEC-SPD-WH-001 + +#### 集成验证点 + +- **TP_WH_ZON_060**(中)|停用库区不可再被探针绑定为新货位|验证要点:停用后探针绑定弹窗按货位状态/启用过滤时不应再选出该库区下可绑定货位(以现网过滤规则为准)|关联需求:UI-SPEC-SPD-WH-001 + +--- + +### 人员管理 + +#### 功能验证点 + +- **TP_WH_ZON_061**(高)|打开人员管理|验证要点:行内「人员管理」打开弹层或子页,标题表明为人员管理且对应当前库区|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_062**(中)|关闭后回到库区列表|验证要点:关闭人员管理后仍停留在当前库房的库区列表,选中库房不变|关联需求:UI-SPEC-SPD-WH-001 + +#### 边界验证点 + +- **TP_WH_ZON_063**(低)|无人员时列表空态|验证要点:未配置人员时空态明确,不报错|关联需求:UI-SPEC-SPD-WH-001 + +#### 异常验证点 + +- **TP_WH_ZON_064**(中)|人员管理加载失败|验证要点:失败有错误提示,不把其它库区人员展示为当前库区|关联需求:UI-SPEC-SPD-WH-001 + +#### 集成验证点 + +- **TP_WH_ZON_065**(中)|人员与用户管理账号同源|验证要点:可选人员来自用户/角色主数据(允许按权限裁剪)|关联需求:UI-SPEC-SPD-WH-001 + +--- + +### 货位管理 + +#### 功能验证点 + +- **TP_WH_ZON_066**(高)|打开货位管理|验证要点:行内「货位管理」打开弹层或子页,标题表明为货位且对应当前库区|关联需求:UI-SPEC-SPD-WH-001 +- **TP_WH_ZON_067**(中)|关闭后回到库区列表|验证要点:关闭货位管理后当前库房选中与库区列表保持|关联需求:UI-SPEC-SPD-WH-001 + +#### 边界验证点 + +- **TP_WH_ZON_068**(低)|无货位时空态|验证要点:库区无货位时列表空态明确|关联需求:UI-SPEC-SPD-WH-001 + +#### 异常验证点 + +- **TP_WH_ZON_069**(中)|货位管理加载失败|验证要点:失败有提示,不串其它库区货位|关联需求:UI-SPEC-SPD-WH-001 + +#### 集成验证点 + +- **TP_WH_ZON_070**(高)|货位可被探针绑定弹窗检索到|验证要点:本库区启用货位应出现在探针绑定「货位绑定探针」弹窗对应库房/库区筛选结果中|关联需求:UI-SPEC-SPD-WH-001 + +--- + +### 同步库房信息 + +#### 功能验证点 + +- **TP_WH_ZON_071**(中)|同步刷新库房树|验证要点:点「同步」后树与列表按最新主数据刷新,不丢当前可维护库房|关联需求:UI-SPEC-SPD-WH-001 + +#### 边界验证点 + +- **TP_WH_ZON_072**(低)|同步期间不可重复提交|验证要点:同步进行中按钮禁用或再次点击被忽略,不并发写坏树|关联需求:UI-SPEC-SPD-WH-001 + +#### 异常验证点 + +- **TP_WH_ZON_073**(中)|同步失败有提示|验证要点:失败有错误提示,树不空白、不把失败展示为成功|关联需求:UI-SPEC-SPD-WH-001 + +#### 集成验证点 + +- **TP_WH_ZON_074**(中)|同步结果与院区库房主数据一致|验证要点:同步后新增/更名的库房节点与主数据一致|关联需求:UI-SPEC-SPD-WH-001 + + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、库房树过滤/切换、库区列表分页、库区搜索、新增/编辑弹窗、停用启用、人员/货位入口、同步;共 **74** 条原子测试点(未声称全覆盖,断网类与仓库其它基础管理页同样省略) | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分;无某类时保留标题并说明 | +| 去重 | 登录拦截、会话过期与同模块其它页规则相同,本页仅保留菜单/URL/字段差异;编号前缀 `TP_WH_ZON` | +| 待评审项 | ① 人员管理/货位管理扫描未打开面板,入口以行内按钮为准,子页字段待补扫 ② 默认库区如何指定(编辑字段或单独接口)以现网为准 ③ 停用默认库区是否允许待产品确认 ④ 新增弹窗扫描未见「是否专项库区」控件,列表有该列,编辑是否可改为待评审 | +| 下游 | 可转结构化 MD/CSV;扫描 `npm run discover:scan-warehouse`;自动化建议 `pages/spd/warehouse-zone.page.ts` | + diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..9db4eff --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\345\272\223\346\210\277\347\256\241\347\220\206-\346\216\242\351\222\210\347\273\221\345\256\232-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,168 @@ +# 基础管理-库房管理-探针绑定 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-WH-002.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/storehouse/storehouse/probe` +**版本 / 定稿日期**:2026-10-20 +**元素扫描**:`data/scan/warehouse-probe-elements.json`(78 项) +**定位器**:`locators/spd/warehouse-probe.locator.yaml` + +--- + +## 基础设置 · 库房管理 · 探针绑定 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_WH_PRB_001**(高)|菜单路径可达探针绑定|验证要点:登录后通过「基础设置 → 库房管理 → 探针绑定」进入 `/spd/basic/storehouse/storehouse/probe`,左侧菜单与顶栏 Tab 高亮为探针绑定,不串到同组其它页|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_002**(高)|页面主区加载完成|验证要点:整页单表:探针ID/主机编码/是否绑定货位筛选、查询重置、列表与分页可见,无报错遮罩|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_003**(中)|同组库房管理菜单可切换|验证要点:在 库区设置 / 探针绑定 间切换后 URL 与页面内容对应,再返回探针绑定状态正确|关联需求:UI-SPEC-SPD-WH-002 + +#### 边界验证点 + +- **TP_WH_PRB_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/storehouse/storehouse/probe`,页面正常渲染|关联需求:UI-SPEC-SPD-WH-002 + +#### 异常验证点 + +- **TP_WH_PRB_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录,不误写|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_007**(中)|无菜单权限|验证要点:无探针绑定权限的账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-WH-002 + +#### 集成验证点 + +- **TP_WH_PRB_008**(中)|顶栏身份与可管理范围一致|验证要点:顶栏当前库房/院区与本页可维护库房范围不矛盾|关联需求:UI-SPEC-SPD-WH-002 + +--- + +### 筛选与查询 + +#### 功能验证点 + +- **TP_WH_PRB_009**(高)|默认条件查询|验证要点:进入后点「查询」,列表有数据或空态明确,底部分页「共 N 条」与行数逻辑一致|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_010**(高)|按探针ID查询|验证要点:输入已存在探针ID后结果探针ID匹配,精确命中时至多对应行|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_011**(高)|按主机编码查询|验证要点:输入主机编码后结果「主机编码」与条件一致(多探针可共用同一主机)|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_012**(高)|按是否绑定货位查询|验证要点:选择「已绑定」仅保留已绑定行;选择「未绑定」仅保留未绑定行|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_013**(高)|探针ID+主机编码+是否绑定组合查询|验证要点:多个已填条件同时生效,结果同时满足全部条件|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_014**(中)|重置恢复默认|验证要点:填写筛选后点「重置」,条件恢复默认,列表按默认规则刷新|关联需求:UI-SPEC-SPD-WH-002 + +#### 边界验证点 + +- **TP_WH_PRB_015**(中)|超长探针ID或主机编码|验证要点:超长输入可查询或被截断,页面不崩溃|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_016**(低)|仅空格关键字|验证要点:按空查询处理或提示,不报错|关联需求:UI-SPEC-SPD-WH-002 + +#### 异常验证点 + +- **TP_WH_PRB_017**(中)|查询接口失败|验证要点:失败有错误提示,不白屏、不把过期数据展示为成功态|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_018**(低)|无匹配结果空态|验证要点:条件无匹配时「暂无数据」明确,分页总数为 0|关联需求:UI-SPEC-SPD-WH-002 + +#### 集成验证点 + +- **TP_WH_PRB_019**(中)|筛选结果与分页总数一致|验证要点:查询后可见行逻辑与「共 N 条」一致|关联需求:UI-SPEC-SPD-WH-002 + +--- + +### 列表与分页 + +#### 功能验证点 + +- **TP_WH_PRB_020**(高)|列表核心列完整|验证要点:主表展示序号、探针ID、主机编码、是否绑定货位、绑定的货位、操作(绑定货位)|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_021**(高)|已绑定行数量大于 0|验证要点:「是否绑定货位」为已绑定时,「绑定的货位」数量大于 0|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_022**(高)|未绑定行数量为 0|验证要点:未绑定行「绑定的货位」为 `0`|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_023**(中)|绑定的货位列为数量不是货位号|验证要点:主表该列为数字;货位号只在弹窗「货位号」列|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_024**(中)|分页翻页|验证要点:总条数大于一页时可翻页,数据更换且页码正确|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_025**(中)|每页条数切换|验证要点:切换每页条数后当前页行数不超过所选值,总数不变|关联需求:UI-SPEC-SPD-WH-002 + +#### 边界验证点 + +- **TP_WH_PRB_026**(中)|首页上一页不可用|验证要点:第 1 页时「上一页」禁用或点击无效|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_027**(中)|末页下一页不可用|验证要点:最后一页时「下一页」禁用或点击无效|关联需求:UI-SPEC-SPD-WH-002 + +#### 异常验证点 + +- **TP_WH_PRB_028**(低)|跳至非数字页码|验证要点:非法页码不跳转异常页|关联需求:UI-SPEC-SPD-WH-002 + +#### 集成验证点 + +- **TP_WH_PRB_029**(中)|翻页不丢失当前筛选|验证要点:带筛选翻页后条件保持,新页仍满足原筛选|关联需求:UI-SPEC-SPD-WH-002 + +--- + +### 打开绑定货位弹窗 + +#### 功能验证点 + +- **TP_WH_PRB_030**(高)|行内打开货位绑定探针|验证要点:点「绑定货位」弹出「货位绑定探针」,含库房/库区/货位筛选与货位列表|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_031**(中)|取消关闭不改主列表|验证要点:未确定即点「取消」或关闭,主列表绑定数量与筛选条件保持|关联需求:UI-SPEC-SPD-WH-002 + +#### 边界验证点 + +- **TP_WH_PRB_032**(低)|未选货位时确定计数为 0|验证要点:底部按钮文案为「确定(0)」或等价,未选时确定不新增绑定|关联需求:UI-SPEC-SPD-WH-002 + +#### 异常验证点 + +- **TP_WH_PRB_033**(中)|弹窗加载失败|验证要点:货位列表失败有提示,不把主列表「共 N 条」与弹窗总条数混用|关联需求:UI-SPEC-SPD-WH-002 + +#### 集成验证点 + +- **TP_WH_PRB_034**(中)|弹窗分页独立于主列表|验证要点:弹窗「共 N 条」(货位量级)不得用于断言主列表探针条数|关联需求:UI-SPEC-SPD-WH-002 + +--- + +### 弹窗筛选货位 + +#### 功能验证点 + +- **TP_WH_PRB_035**(高)|按当前库房筛选|验证要点:选择库房后列表「当前库房」与所选一致|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_036**(高)|按当前库区筛选|验证要点:选择库区后列表「当前库区」与所选一致|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_037**(高)|按货位号查询|验证要点:「货位查询」输入货位号关键字后结果货位号匹配|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_038**(中)|弹窗内重置|验证要点:填写弹窗筛选后点「重置」,条件恢复,列表按弹窗默认规则刷新|关联需求:UI-SPEC-SPD-WH-002 + +#### 边界验证点 + +- **TP_WH_PRB_039**(中)|超长货位号关键字|验证要点:超长输入可查询或被截断,弹窗不崩溃|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_040**(低)|库区下拉随库房收敛|验证要点:切换库房后库区选项属于该库房,不残留上一库房库区|关联需求:UI-SPEC-SPD-WH-002 + +#### 异常验证点 + +- **TP_WH_PRB_041**(低)|弹窗无匹配空态|验证要点:条件无命中时货位列表空、弹窗分页为 0|关联需求:UI-SPEC-SPD-WH-002 + +#### 集成验证点 + +- **TP_WH_PRB_042**(高)|库房库区与库区设置同源|验证要点:弹窗库房/库区选项来自库区设置中已维护且可用的库房与库区|关联需求:UI-SPEC-SPD-WH-002 + +--- + +### 勾选货位并保存绑定 + +#### 功能验证点 + +- **TP_WH_PRB_043**(高)|勾选后确定计数增加|验证要点:勾选 N 个货位后确定按钮计数为 N|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_044**(高)|确定后主列表绑定数量增加|验证要点:为未绑定探针勾选货位并确定后,该行变为已绑定,数量等于本次保存的货位数(或按产品规则累加)|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_045**(中)|已绑货位行展示探针ID|验证要点:弹窗中已被该探针绑定的货位「探针ID」列显示该探针号,未绑定为 `--`|关联需求:UI-SPEC-SPD-WH-002 + +#### 边界验证点 + +- **TP_WH_PRB_046**(中)|取消勾选后计数回退|验证要点:勾选后再取消,确定计数回到实际已选数|关联需求:UI-SPEC-SPD-WH-002 + +#### 异常验证点 + +- **TP_WH_PRB_047**(中)|绑定保存失败|验证要点:失败有提示,主列表绑定数量不增加,再次打开不是半成功勾选|关联需求:UI-SPEC-SPD-WH-002 +- **TP_WH_PRB_048**(中)|货位已被其它探针占用|验证要点:占用行再绑定时按换绑或拒绝执行;以保存后主列表数量与弹窗探针ID列为准,不得静默双绑|关联需求:UI-SPEC-SPD-WH-002 + +#### 集成验证点 + +- **TP_WH_PRB_049**(高)|绑定结果与货位管理一致|验证要点:保存后在对应库区货位侧能看到该探针占用;解绑/换绑后两侧同步|关联需求:UI-SPEC-SPD-WH-002 + + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、探针筛选查询、列表数量规则、绑定弹窗筛选分页、勾选确定/取消、与库区货位集成;共 **49** 条原子测试点(未声称全覆盖,断网类与仓库其它基础管理页同样省略) | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分;无某类时保留标题并说明 | +| 去重 | 登录拦截、会话过期与同模块其它页规则相同,本页仅保留菜单/URL/字段差异;编号前缀 `TP_WH_PRB` | +| 待评审项 | ① 同一货位已有其它探针ID时是换绑还是拒绝,以保存后主列表数量与弹窗探针ID列为准 ② 确定按钮文案为「确定(N)」,未选为 0 ③ 主表无新增探针入口,探针主数据来源不在本页维护 | +| 下游 | 可转结构化 MD/CSV;扫描 `npm run discover:scan-warehouse`;自动化建议 `pages/spd/warehouse-probe.page.ts` | + diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..03b6db7 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\345\237\272\347\241\200\345\214\205\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,147 @@ +# 基础管理-手术套包设置-基础包维护 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-SURG-001.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/operationSetting/baseMaintenance` +**版本 / 定稿日期**:2026-10-13 +**元素扫描**:`data/scan/surgery-base-pkg-elements.json`(87 项) +**定位器**:`locators/spd/surgery-base-pkg.locator.yaml` + +--- + +## 基础设置 · 手术套包设置 · 基础包维护 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_SURG_BASE_002**(高)|页面主区加载完整|验证要点:左右分栏渲染,左侧分类树(含分类名称搜索框)与右侧筛选区/列表/分页均可见,无报错遮罩|关联需求:UI-SPEC-SPD-SURG-001 + +#### 边界验证点 + +- **TP_SURG_BASE_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/operationSetting/baseMaintenance`,列表正常渲染|关联需求:UI-SPEC-SPD-SURG-001 + +#### 异常验证点 + +- **TP_SURG_BASE_006**(中)|登录态过期访问|验证要点:会话失效后新增/移除被拦截,不产生误写|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_007**(中)|无手术套包设置权限|验证要点:无权限账号不可见菜单或访问被拒绝|关联需求:UI-SPEC-SPD-SURG-001 + +#### 集成验证点 + +- **TP_SURG_BASE_008**(中)|左侧分类树与基础包分类主数据同源|验证要点:本页分类节点集合与「标准套包维护/添加基础包」弹窗中的分类树一致(允许按权限裁剪)|关联需求:UI-SPEC-SPD-SURG-001 + +--- + +### 左侧分类树 + +#### 功能验证点 + +- **TP_SURG_BASE_009**(高)|分类名称搜索过滤|验证要点:在左侧「分类名称」框输入关键字后树节点收敛为匹配项(即时或回车,以现网为准)|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_010**(高)|选择分类节点联动右侧列表|验证要点:点击某分类节点后右侧列表仅展示该分类下基础包,且与「共 N 条」一致|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_011**(中)|分类树展开/折叠|验证要点:可展开父级、折叠子级,展开后子节点层级与计数(若有)正确|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_012**(中)|新增分类入口可达|验证要点:点击「新增分类」弹出「新增分类」弹窗,含分类名称输入与取消/确认|关联需求:UI-SPEC-SPD-SURG-001 + +#### 边界验证点 + +- **TP_SURG_BASE_013**(低)|分类搜索清空恢复全量|验证要点:清空分类搜索框后树恢复未过滤的完整节点|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_014**(低)|分类搜索超长关键字|验证要点:超长关键字不崩溃,按规则截断或无匹配空态|关联需求:UI-SPEC-SPD-SURG-001 + +#### 异常验证点 + +- **TP_SURG_BASE_015**(中)|分类树加载失败|验证要点:分类接口异常时左栏有错误提示或空态,不白屏且不影响右栏可恢复|关联需求:UI-SPEC-SPD-SURG-001 + +#### 集成验证点 + +- **TP_SURG_BASE_016**(中)|新增分类后树节点同步出现|验证要点:新增分类保存成功后,左栏树无需整页刷新即出现新节点并可选中|关联需求:UI-SPEC-SPD-SURG-001 + +--- + +### 右侧筛选与列表 + +#### 功能验证点 + +- **TP_SURG_BASE_017**(高)|默认列出基础包|验证要点:进入页面右侧列表有数据或空态明确,分页「共 N 条」正确|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_018**(高)|产品名称筛选生效|验证要点:产品名称框(`#table_search_form_keyword`)输入关键字查询后仅保留匹配行|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_019**(高)|规格型号筛选生效|验证要点:规格型号框(`#table_search_form_goodsSpec`)输入后列表按规格过滤|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_020**(高)|器械注册人筛选生效|验证要点:器械注册人框(`#table_search_form_goodsMfrsName`)输入后列表按注册人过滤|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_021**(中)|耗材类型下拉筛选|验证要点:耗材类型选择「低值耗材/高值耗材」后列表仅保留对应类型|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_022**(高)|列表核心列完整|验证要点:展示序号、产品编号、产品名称、规格型号、器械注册人、单位、产地、耗材类型、操作(编辑/移除)|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_023**(中)|查询与重置|验证要点:点「查询」按当前条件过滤;点「重置」清空所有筛选并恢复默认列表|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_024**(中)|分页翻页与页大小|验证要点:可翻页;切换每页条数后行数不超过所选值,总数不变|关联需求:UI-SPEC-SPD-SURG-001 + +#### 边界验证点 + +- **TP_SURG_BASE_025**(低)|筛选多条件叠加无匹配|验证要点:组合条件无结果时展示空态,总数为 0|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_026**(低)|首页上一页不可用|验证要点:第 1 页「上一页」禁用或无效|关联需求:UI-SPEC-SPD-SURG-001 + +#### 异常验证点 + +- **TP_SURG_BASE_027**(中)|列表接口异常|验证要点:加载失败有错误提示,不白屏|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_028**(低)|搜索无匹配空态|验证要点:无匹配时「暂无数据」,总数为 0|关联需求:UI-SPEC-SPD-SURG-001 + +#### 集成验证点 + +- **TP_SURG_BASE_029**(中)|筛选结果与分页总数一致|验证要点:过滤后「共 N 条」与过滤结果规模一致|关联需求:UI-SPEC-SPD-SURG-001 + +--- + +### 新增基础包 + +#### 功能验证点 + +- **TP_SURG_BASE_030**(高)|打开新增基础包弹窗|验证要点:点「新增基础包」弹出弹窗,含基础包名称、所属分类、包编码、备注及取消/确认|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_031**(高)|合法数据新增成功|验证要点:填写名称、所属分类、包编码后确定,弹窗关闭,列表出现新行且字段一致|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_032**(中)|所属分类可选择|验证要点:所属分类支持从分类树/下拉中选取,保存后编辑回看分类一致|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_033**(中)|添加产品到构成列表|验证要点:弹窗内「添加产品」可将产品加入已选列表,构成数量可编辑,单位/耗材类型/是否收费展示正确|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_034**(中)|取消不落库|验证要点:填写后点「取消」或关闭,列表不新增对应基础包|关联需求:UI-SPEC-SPD-SURG-001 + +#### 边界验证点 + +- **TP_SURG_BASE_035**(中)|基础包名称达到最大长度|验证要点:名称取最大允许长度可保存成功|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_036**(低)|备注可为空|验证要点:备注留空可保存,回看为空而非脏数据|关联需求:UI-SPEC-SPD-SURG-001 + +#### 异常验证点 + +- **TP_SURG_BASE_037**(高)|基础包名称为空拦截|验证要点:名称为空点确定被拦截并提示,不落库|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_038**(中)|所属分类为空拦截|验证要点:未选分类点确定被拦截并提示|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_039**(中)|包编码重复|验证要点:与已有基础包包编码重复时拒绝或按产品规则提示,不产生重码脏数据|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_040**(中)|重复点击确定|验证要点:快速连点确定只产生一条基础包或第二次被防重|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_041**(中)|保存接口失败|验证要点:新增失败有提示,弹窗仍在,列表无半成品成功行|关联需求:UI-SPEC-SPD-SURG-001 + +#### 集成验证点 + +- **TP_SURG_BASE_042**(高)|新增基础包可在标准套包中被引用|验证要点:新增成功的基础包,在「标准套包维护 → 添加基础包」弹窗中可被检索并选用|关联需求:UI-SPEC-SPD-SURG-003 + +--- + +### 行内操作(编辑 / 移除) + +#### 功能验证点 + +- **TP_SURG_BASE_043**(高)|行内「编辑」入口可用|验证要点:点击某行「编辑」打开编辑弹窗并回填当前行数据|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_044**(中)|行内「移除」删除构成项|验证要点:点击「移除」并有确认时,确认后该构成项从列表消失,总数 -1|关联需求:UI-SPEC-SPD-SURG-001 + +#### 边界验证点 + +- **TP_SURG_BASE_045**(低)|仅一行时移除后空态|验证要点:删除最后一行后列表显示空态,总数为 0|关联需求:UI-SPEC-SPD-SURG-001 + +#### 异常验证点 + +- **TP_SURG_BASE_046**(高)|移除二次确认防误删|验证要点:「移除」为写操作,执行前有确认,取消则不删除|关联需求:UI-SPEC-SPD-SURG-001 +- **TP_SURG_BASE_047**(中)|移除已被引用的构成项|验证要点:被标准套包引用时按产品规则拒绝并提示,不产生悬空引用|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_BASE_048**(中)|编辑已不存在的记录|验证要点:记录已被删除后再保存,提示不存在,不产生幽灵更新|关联需求:UI-SPEC-SPD-SURG-001 + +#### 集成验证点 + +- **TP_SURG_BASE_049**(中)|编辑名称后下游展示同步|验证要点:修改基础包名称/编码后,标准套包维护等引用处展示为新值|关联需求:UI-SPEC-SPD-SURG-003 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:左侧分类树(搜索/选择/新增分类)、右侧筛选(产品名称/产品编号/规格型号/器械注册人/耗材类型)、列表列、分页、新增基础包弹窗、行内编辑/移除;共 **46** 条原子测试点 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 登录/权限与同模块其它页共用规则,本页强调分类树联动与基础包 CRUD;编号前缀 `TP_SURG_BASE` | +| 待评审项 | ① 分类搜索是即时过滤还是回车 ② 包编码是否唯一约束 ③「移除」是否二次确认 ④ 被标准套包引用时能否移除 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 `npm run discover:scan-surgery` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..48000b5 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\234\257\347\247\215\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,170 @@ +# 基础管理-手术套包设置-术种维护 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-SURG-002.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/operationSetting/typeMaintenance` +**版本 / 定稿日期**:2026-10-13 +**元素扫描**:`data/scan/surgery-oper-type-elements.json`(69 项) +**定位器**:`locators/spd/surgery-oper-type.locator.yaml` + +--- + +## 基础设置 · 手术套包设置 · 术种维护 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_SURG_TYPE_001**(高)|菜单路径可达术种维护页|验证要点:登录后通过「基础设置 → 手术套包设置 → 术种维护」进入目标页,菜单项与页内 Tab 均高亮|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_002**(高)|页面主区加载完整|验证要点:左右分栏渲染,左侧术种分类树(含分类名称搜索框)与右侧筛选区/状态 Tab/列表/分页均可见,无报错遮罩|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_003**(中)|同组三个页签可切换|验证要点:切到「基础包维护」「标准套包维护」再返回,本页左侧树与右侧列表恢复正确|关联需求:UI-SPEC-SPD-SURG-002 + +#### 边界验证点 + +- **TP_SURG_TYPE_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/operationSetting/typeMaintenance`,列表正常渲染|关联需求:UI-SPEC-SPD-SURG-002 + +#### 异常验证点 + +- **TP_SURG_TYPE_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示术种数据|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_006**(中)|登录态过期访问|验证要点:会话失效后新增/编辑/停用被拦截,不产生误写|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_007**(中)|无手术套包设置权限|验证要点:无权限账号不可见菜单或访问被拒绝|关联需求:UI-SPEC-SPD-SURG-002 + +#### 集成验证点 + +- **TP_SURG_TYPE_008**(中)|左侧术种分类树与手术套包分类主数据同源|验证要点:本页分类节点集合与「标准套包维护」左侧分类树一致(允许按权限裁剪)|关联需求:UI-SPEC-SPD-SURG-003 + +--- + +### 左侧术种分类树 + +#### 功能验证点 + +- **TP_SURG_TYPE_009**(高)|术种分类名称搜索过滤|验证要点:在左侧「术种分类名称」框输入关键字后树节点收敛为匹配项(即时或回车,以现网为准)|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_010**(高)|选择分类节点联动右侧列表|验证要点:点击某分类节点后右侧列表仅展示该分类下术种,且与「共 N 条」一致|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_011**(中)|分类树层级下钻与计数|验证要点:下钻(如 骨外科(8) → 骨科(2))层级正确,括号内计数与该分类下术种数一致|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_012**(中)|分类树展开/折叠|验证要点:可展开父级、折叠子级,展示层级正确|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_013**(中)|新增术种分类|验证要点:点击「新增」弹出「分类维护」弹窗,含分类名称与取消/保存;保存成功后树出现新分类|关联需求:UI-SPEC-SPD-SURG-002 + +#### 边界验证点 + +- **TP_SURG_TYPE_014**(低)|分类搜索清空恢复全量|验证要点:清空分类搜索框后树恢复未过滤的完整节点|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_015**(低)|分类搜索超长关键字|验证要点:超长关键字不崩溃,按规则截断或无匹配空态|关联需求:UI-SPEC-SPD-SURG-002 + +#### 异常验证点 + +- **TP_SURG_TYPE_016**(中)|分类树加载失败|验证要点:分类接口异常时左栏有错误提示或空态,不白屏|关联需求:UI-SPEC-SPD-SURG-002 + +#### 集成验证点 + +- **TP_SURG_TYPE_017**(中)|新增分类后树节点同步出现|验证要点:新增分类保存成功后,左栏树无需整页刷新即出现新节点并可选中|关联需求:UI-SPEC-SPD-SURG-002 + +--- + +### 右侧筛选与状态 Tab + +#### 功能验证点 + +- **TP_SURG_TYPE_018**(高)|术种名称筛选生效|验证要点:术种名称框(`#table_search_form_operationName`)输入关键字查询后仅保留匹配行|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_019**(高)|病种名称筛选生效|验证要点:病种名称框(`#table_search_form_disease`)输入后列表按病种过滤|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_020**(高)|状态 Tab 切换|验证要点:「全部 / 停用 / 未建分类」切换后列表仅展示对应状态术种,Tab 括号计数与结果一致|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_021**(中)|查询与重置|验证要点:点「查询」按当前条件过滤;「重置」清空筛选并按当前 Tab 恢复默认列表|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_022**(中)|左树 + 筛选 + Tab 三条件叠加|验证要点:分类节点、筛选关键字、状态 Tab 组合后列表结果与三者交集一致|关联需求:UI-SPEC-SPD-SURG-002 + +#### 边界验证点 + +- **TP_SURG_TYPE_023**(低)|无匹配组合空态|验证要点:组合条件无结果时展示空态,总数为 0|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_024**(低)|停用 Tab 无数据|验证要点:停用数为 0 时该 Tab 列表为空态且计数为 0|关联需求:UI-SPEC-SPD-SURG-002 + +#### 异常验证点 + +- **TP_SURG_TYPE_025**(中)|列表接口异常|验证要点:加载失败有错误提示,不白屏|关联需求:UI-SPEC-SPD-SURG-002 + +#### 集成验证点 + +- **TP_SURG_TYPE_026**(中)|筛选结果与分页总数一致|验证要点:过滤后「共 N 条」与过滤结果规模一致|关联需求:UI-SPEC-SPD-SURG-002 + +--- + +### 术种列表与编码展示 + +#### 功能验证点 + +- **TP_SURG_TYPE_027**(高)|列表核心列完整|验证要点:展示序号、术种名称、病种名称、诊断编码(ICD-10)、诊断名称(ICD-10)、操作编码(ICD-9-CM-3)、操作名称(ICD-9-CM-3)、操作|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_028**(中)|ICD-10 诊断编码展示完整|验证要点:诊断编码(如 `A02.000x010`)不截断、无乱码|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_029**(中)|ICD-9-CM-3 操作编码展示完整|验证要点:操作编码(如 `00.0200`)与操作名称(如 `心脏治疗性超声`)对应正确|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_030**(中)|分页翻页与页大小|验证要点:可翻页;切换每页条数后行数不超过所选值,总数不变|关联需求:UI-SPEC-SPD-SURG-002 + +#### 边界验证点 + +- **TP_SURG_TYPE_031**(中)|超长术种/病种名称列宽与换行|验证要点:超长文本列宽与换行正常,不撑破布局或互相覆盖|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_032**(低)|首页上一页不可用|验证要点:第 1 页「上一页」禁用或无效|关联需求:UI-SPEC-SPD-SURG-002 + +#### 异常验证点 + +- **TP_SURG_TYPE_033**(低)|编码缺失展示|验证要点:无 ICD 编码的术种展示为占位或空,不出现 `undefined/null`|关联需求:UI-SPEC-SPD-SURG-002 + +#### 集成验证点 + +- **TP_SURG_TYPE_034**(中)|ICD 编码与字典同源|验证要点:列表展示的 ICD-10/ICD-9-CM-3 编码与标准诊断/操作字典一致|关联需求:UI-SPEC-SPD-SURG-002 + +--- + +### 新增术种 + +#### 功能验证点 + +- **TP_SURG_TYPE_035**(高)|打开新增术种入口|验证要点:点「新增术种」打开新增弹窗/页面,含术种名称、病种名称、分类及编码相关字段与取消/保存|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_036**(高)|合法数据新增成功|验证要点:填写必填项并保存后,列表出现新术种且字段一致,所选分类计数 +1|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_037**(中)|新增未建分类术种|验证要点:不选分类新增后,术种出现在「未建分类」Tab 且该 Tab 计数 +1|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_038**(中)|取消不落库|验证要点:填写后取消或关闭,列表不新增对应术种|关联需求:UI-SPEC-SPD-SURG-002 + +#### 边界验证点 + +- **TP_SURG_TYPE_039**(中)|术种名称达到最大长度|验证要点:名称取最大允许长度可保存成功|关联需求:UI-SPEC-SPD-SURG-002 + +#### 异常验证点 + +- **TP_SURG_TYPE_040**(高)|术种名称为空拦截|验证要点:名称为空点保存被拦截并提示,不落库|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_041**(中)|术种名称重复|验证要点:与已有术种重名时拒绝或按产品规则提示,不产生无法区分的重名脏数据|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_042**(中)|重复点击保存|验证要点:快速连点保存只产生一条术种或第二次被防重|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_043**(中)|保存接口失败|验证要点:新增失败有提示,弹窗仍在,列表无半成品成功行|关联需求:UI-SPEC-SPD-SURG-002 + +#### 集成验证点 + +- **TP_SURG_TYPE_044**(高)|新增术种可在标准套包中关联|验证要点:新增术种后,在「标准套包维护」按术种分类/术种可检索并建立套包关联|关联需求:UI-SPEC-SPD-SURG-003 + +--- + +### 行内操作(编辑 / 停用) + +#### 功能验证点 + +- **TP_SURG_TYPE_045**(高)|行内「编辑」回填并保存|验证要点:点击某行「编辑」打开弹窗并回填当前术种数据,修改后保存列表同步更新|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_046**(高)|「停用」状态流转|验证要点:点击「停用」并有确认时,确认后该术种状态变为停用|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_047**(中)|「启用」恢复|验证要点:对停用术种执行「启用」后恢复为启用状态|关联需求:UI-SPEC-SPD-SURG-002 + +#### 边界验证点 + +- **TP_SURG_TYPE_048**(低)|停用后 Tab 计数变化|验证要点:停用一条后「停用」Tab 计数 +1、「全部」总数不变、原状态 Tab 计数 -1|关联需求:UI-SPEC-SPD-SURG-002 + +#### 异常验证点 + +- **TP_SURG_TYPE_049**(高)|停用二次确认防误操作|验证要点:「停用」为写操作,执行前有确认,取消则不改变状态|关联需求:UI-SPEC-SPD-SURG-002 +- **TP_SURG_TYPE_050**(中)|停用已被套包引用的术种|验证要点:被标准套包引用时按产品规则拒绝或允许但下游不可新选,提示明确|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_TYPE_051**(中)|编辑已不存在的记录|验证要点:记录被删除后再保存,提示不存在,不产生幽灵更新|关联需求:UI-SPEC-SPD-SURG-002 + +#### 集成验证点 + +- **TP_SURG_TYPE_052**(高)|停用术种后下游不可新选|验证要点:术种停用后,标准套包维护等处不可再作为新业务选择(历史引用仍展示原名称)|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_TYPE_053**(中)|编辑术种名称后下游同步|验证要点:修改术种/病种名称后,引用处展示为新值|关联需求:UI-SPEC-SPD-SURG-003 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、左侧分类树(搜索/下钻/新增分类维护)、筛选(术种名称/病种名称)、状态 Tab(全部/停用/未建分类)、ICD-10/ICD-9-CM-3 列展示、分页、新增术种、行内编辑/停用/启用;共 **53** 条原子测试点 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 登录/权限与同模块其它页共用规则,本页强调状态 Tab 计数守恒与 ICD 编码展示;编号前缀 `TP_SURG_TYPE` | +| 待评审项 | ① 分类搜索是即时过滤还是回车 ②「停用」是否需确认框 ③ 停用后 Tab 计数与「全部」的口径 ④ 被套包引用时停用策略 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 `npm run discover:scan-surgery` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..fa1bf90 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\211\213\346\234\257\345\245\227\345\214\205\350\256\276\347\275\256-\346\240\207\345\207\206\345\245\227\345\214\205\347\273\264\346\212\244-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,174 @@ +# 基础管理-手术套包设置-标准套包维护 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-SURG-003.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/operationSetting/standardMaintenance` +**版本 / 定稿日期**:2026-10-13 +**元素扫描**:`data/scan/surgery-std-pkg-elements.json`(61 项) +**定位器**:`locators/spd/surgery-std-pkg.locator.yaml` + +--- + +## 基础设置 · 手术套包设置 · 标准套包维护 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_SURG_STD_001**(高)|菜单路径可达标准套包维护页|验证要点:登录后通过「基础设置 → 手术套包设置 → 标准套包维护」进入目标页,菜单项与页内 Tab 均高亮|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_002**(高)|页面主区加载完整|验证要点:左右分栏渲染,左侧术种分类树(含搜索框)与右侧筛选区/构成列表/分页均可见,无报错遮罩|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_003**(中)|同组三个页签可切换|验证要点:切到「基础包维护」「术种维护」再返回,本页左侧树与右侧构成列表恢复正确|关联需求:UI-SPEC-SPD-SURG-003 + +#### 边界验证点 + +- **TP_SURG_STD_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/operationSetting/standardMaintenance`,构成列表正常渲染|关联需求:UI-SPEC-SPD-SURG-003 + +#### 异常验证点 + +- **TP_SURG_STD_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示套包数据|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_006**(中)|登录态过期访问|验证要点:会话失效后新增/编辑/移除被拦截,不产生误写|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_007**(中)|无手术套包设置权限|验证要点:无权限账号不可见菜单或访问被拒绝|关联需求:UI-SPEC-SPD-SURG-003 + +#### 集成验证点 + +- **TP_SURG_STD_008**(中)|左侧术种分类树与术种维护同源|验证要点:本页分类节点集合与「术种维护」左侧分类树一致(允许按权限裁剪)|关联需求:UI-SPEC-SPD-SURG-002 + +--- + +### 左侧术种分类树 + +#### 功能验证点 + +- **TP_SURG_STD_009**(高)|术种分类名称搜索过滤|验证要点:在左侧「术种分类名称」框输入关键字后树节点收敛为匹配项(即时或回车,以现网为准)|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_010**(高)|选择分类节点联动右侧构成列表|验证要点:点击某分类节点后右侧仅展示该分类下标准套包,且与「共 N 条」一致|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_011**(中)|分类树展开/折叠与计数|验证要点:可展开/折叠,括号内计数与该分类下套包数一致|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_012**(中)|「未建分类」节点|验证要点:未划分分类的标准套包归入「未建分类」节点,计数正确|关联需求:UI-SPEC-SPD-SURG-003 + +#### 边界验证点 + +- **TP_SURG_STD_013**(低)|分类搜索清空恢复全量|验证要点:清空搜索框后树恢复未过滤的完整节点|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_014**(低)|分类搜索超长关键字|验证要点:超长关键字不崩溃,按规则截断或无匹配空态|关联需求:UI-SPEC-SPD-SURG-003 + +#### 异常验证点 + +- **TP_SURG_STD_015**(中)|分类树加载失败|验证要点:分类接口异常时左栏有错误提示或空态,不白屏|关联需求:UI-SPEC-SPD-SURG-003 + +#### 集成验证点 + +- **TP_SURG_STD_016**(中)|分类变更后树同步|验证要点:术种维护新增/调整分类后,本页左树刷新可见一致节点|关联需求:UI-SPEC-SPD-SURG-002 + +--- + +### 右侧筛选与构成列表 + +#### 功能验证点 + +- **TP_SURG_STD_017**(高)|默认展示标准套包构成|验证要点:进入页面右侧列表按「包名称」分组展示,有数据或空态明确,分页「共 N 条」正确|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_018**(高)|产品名称筛选生效|验证要点:产品名称框(`#table_search_form_keyword`)输入后仅保留匹配子项|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_019**(高)|规格型号筛选生效|验证要点:规格型号框(`#table_search_form_goodsSpec`)输入后按规格过滤|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_020**(高)|器械注册人筛选生效|验证要点:器械注册人框(`#table_search_form_goodsMfrsName`)输入后按注册人过滤|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_021**(中)|耗材类型下拉筛选|验证要点:耗材类型选择「低值耗材/高值耗材」后仅保留对应类型子项|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_022**(高)|构成列表核心列完整|验证要点:展示包名称、包数量/每包含量、产品编号、产品名称、规格型号、器械注册人、单位、产地、耗材类型、操作(编辑/移除)|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_023**(高)|包名称展开/收起子项|验证要点:展开某「包名称」后展示其子项(基础包/散件)行,收起后隐藏;展开状态不影响分组计数|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_024**(中)|查询与重置|验证要点:点「查询」按条件过滤;「重置」清空筛选并恢复默认列表|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_025**(中)|分页翻页与页大小|验证要点:可翻页;切换每页条数后行数不超过所选值,总数不变|关联需求:UI-SPEC-SPD-SURG-003 + +#### 边界验证点 + +- **TP_SURG_STD_026**(中)|包数量/每包含量边界值|验证要点:数量为最小/最大允许值时展示与计算正确(含步进器上下限)|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_027**(低)|多条件叠加无匹配|验证要点:组合条件无结果时展示空态,总数为 0|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_028**(低)|首页上一页不可用|验证要点:第 1 页「上一页」禁用或无效|关联需求:UI-SPEC-SPD-SURG-003 + +#### 异常验证点 + +- **TP_SURG_STD_029**(中)|构成列表接口异常|验证要点:加载失败有错误提示,不白屏|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_030**(低)|空套包/无子项展示|验证要点:无子项的包展示为空态而非错行,不出现 `undefined/null`|关联需求:UI-SPEC-SPD-SURG-003 + +#### 集成验证点 + +- **TP_SURG_STD_031**(中)|成分与基础包/术种主数据一致|验证要点:构成子项的产品/基础包字段与基础包维护、术种维护主数据一致|关联需求:UI-SPEC-SPD-SURG-001 + +--- + +### 添加基础包弹窗 + +#### 功能验证点 + +- **TP_SURG_STD_032**(高)|打开添加基础包弹窗|验证要点:点「添加基础包」弹出弹窗,左侧基础包分类树 + 右侧基础包明细,含取消/确定|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_033**(高)|弹窗内筛选与选择基础包|验证要点:按分类/名称检索基础包(`请输入分类/基础包名称`),勾选后明细与确定按钮计数(确定(N))同步变化|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_034**(中)|弹窗内构成数量可编辑|验证要点:明细行「构成数量」可编辑,步进器加减生效且合计随之更新|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_035**(高)|确定后构成列表刷新|验证要点:选定基础包点确定,弹窗关闭,标准套包构成列表出现新增基础包行且数量一致|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_036**(中)|取消不落库|验证要点:选定后点「取消」或关闭,构成列表不新增对应行|关联需求:UI-SPEC-SPD-SURG-003 + +#### 边界验证点 + +- **TP_SURG_STD_037**(低)|确定按钮零选禁用|验证要点:未选择任何基础包时「确定(0)」置灰或点击无效果|关联需求:UI-SPEC-SPD-SURG-003 + +#### 异常验证点 + +- **TP_SURG_STD_038**(中)|重复添加同一基础包|验证要点:重复添加同一基础包时按产品规则拒绝/合并/累加,不产生重复脏行|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_039**(中)|提交接口失败|验证要点:确定失败有提示,弹窗仍在,构成列表无半成品成功行|关联需求:UI-SPEC-SPD-SURG-003 + +#### 集成验证点 + +- **TP_SURG_STD_040**(高)|弹窗可选基础包来源正确|验证要点:弹窗可选基础包集合与「基础包维护」中启用的基础包一致|关联需求:UI-SPEC-SPD-SURG-001 + +--- + +### 添加散件弹窗 + +#### 功能验证点 + +- **TP_SURG_STD_041**(高)|打开添加散件弹窗|验证要点:点「添加散件」弹出弹窗,展示「产品列表」视图,含筛选产品名称/规格型号/器械注册人/耗材类型与分页|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_042**(高)|产品列表筛选与选择散件|验证要点:按条件检索散件产品并勾选,加入应拣明细,计数同步变化|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_043**(高)|散件规格明细/应拣明细视图|验证要点:切换到规格明细视图,展示耗材品种数、耗材总数与应拣数量、产品编号/名称/规格/注册人/单位/耗材类型/是否收费列|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_044**(中)|明细统计与实际一致|验证要点:耗材品种数 = 明细去重产品数,耗材总数 = 应拣数量合计;与实际所选一致|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_045**(高)|确定后构成列表刷新散件行|验证要点:选定散件点确定,标准套包构成列表新增散件行,数量与明细一致|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_046**(中)|取消不落库|验证要点:选定散件后取消或关闭,构成列表不新增对应行|关联需求:UI-SPEC-SPD-SURG-003 + +#### 边界验证点 + +- **TP_SURG_STD_047**(中)|万级散件产品列表性能与首屏|验证要点:产品列表总量达万级(示例「共 16299 条」)时首屏可交互、分页/滚动不卡死|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_048**(低)|应拣数量边界|验证要点:应拣数量取最小/最大允许值时保存与合计正确|关联需求:UI-SPEC-SPD-SURG-003 + +#### 异常验证点 + +- **TP_SURG_STD_049**(中)|散件产品列表接口异常|验证要点:加载失败有错误提示,不白屏,可重试|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_050**(中)|提交散件接口失败|验证要点:确定失败有提示,弹窗仍在,构成列表无半成品成功行|关联需求:UI-SPEC-SPD-SURG-003 + +#### 集成验证点 + +- **TP_SURG_STD_051**(高)|散件来自产品主数据且有库存口径|验证要点:散件产品集合与产品主数据一致;应拣数量与库存/应拣口径一致(以接口契约 API-SPD-SURGERY-001 为准)|关联需求:API-SPD-SURGERY-001 + +--- + +### 行内操作(编辑 / 移除) + +#### 功能验证点 + +- **TP_SURG_STD_052**(高)|行内「编辑」构成项|验证要点:点击某构成行「编辑」可修改数量等可编辑字段,保存后列表同步更新|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_053**(中)|行内「移除」构成项|验证要点:点击「移除」并有确认时,确认后该构成项消失,包分组计数随之更新|关联需求:UI-SPEC-SPD-SURG-003 + +#### 边界验证点 + +- **TP_SURG_STD_054**(低)|移除整包最后一子项|验证要点:移除某包最后一个子项后该包分组显示为空态或被移除,不残留空分组标题|关联需求:UI-SPEC-SPD-SURG-003 + +#### 异常验证点 + +- **TP_SURG_STD_055**(高)|移除二次确认防误删整包|验证要点:「移除」为写操作,执行前有确认,取消则不删除,避免误删整包|关联需求:UI-SPEC-SPD-SURG-003 +- **TP_SURG_STD_056**(中)|编辑已不存在的构成项|验证要点:记录被删除后再保存,提示不存在,不产生幽灵更新|关联需求:UI-SPEC-SPD-SURG-003 + +#### 集成验证点 + +- **TP_SURG_STD_057**(高)|套包变更对下游术种套包可用性影响|验证要点:标准套包构成变更后,关联术种在手术套包业务中取到的构成为最新值|关联需求:API-SPD-SURGERY-001 + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、左侧术种分类树(搜索/未建分类)、筛选(产品名称/规格型号/器械注册人/耗材类型)、构成列表分组展开、分页、添加基础包弹窗、添加散件弹窗(产品列表 + 规格/应拣明细)、行内编辑/移除;共 **57** 条原子测试点 | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分 | +| 去重 | 登录/权限与同模块其它页共用规则,本页强调嵌套弹窗计数一致与万级数据性能;编号前缀 `TP_SURG_STD` | +| 待评审项 | ① 重复添加同一基础包/散件的合并策略 ② 耗材品种数/耗材总数的精确口径 ③ 移除是否二次确认 ④ 万级散件列表是否支持全量选择 | +| 下游 | 可生成 MD/CSV 与 Playwright;扫描 `npm run discover:scan-surgery` | diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..3ca13b1 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\345\256\236\344\276\213-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,128 @@ +# 基础管理-流程设置-审批流实例 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-FLOW-002.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/flowSet/workflow/flowCase` +**版本 / 定稿日期**:2026-10-20 +**元素扫描**:`data/scan/flow-inst-elements.json`(62 项) +**定位器**:`locators/spd/flow-inst.locator.yaml` + +--- + +## 基础设置 · 流程设置 · 审批流实例 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_FLOW_INS_001**(高)|菜单路径可达审批流实例|验证要点:登录后通过「基础设置 → 流程设置 → 审批流实例」进入 `/spd/basic/flowSet/workflow/flowCase`,左侧菜单与顶栏 Tab 高亮为审批流实例,不串到同组其它页|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_002**(高)|页面主区加载完成|验证要点:整页单表:单据编号/审批状态/发起时间/业务类型/摘要筛选、查询重置、列表与分页可见,无报错遮罩|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_003**(中)|同组流程设置菜单可切换|验证要点:在 审批流程设置 / 审批流实例 间切换后 URL 与页面内容对应,再返回审批流实例状态正确|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 边界验证点 + +- **TP_FLOW_INS_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/flowSet/workflow/flowCase`,页面正常渲染|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 异常验证点 + +- **TP_FLOW_INS_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录,不误写|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_007**(中)|无菜单权限|验证要点:无审批流实例权限的账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 集成验证点 + +- **TP_FLOW_INS_008**(中)|顶栏身份与可管理范围一致|验证要点:顶栏登录身份与本页可查看的审批单据范围不矛盾|关联需求:UI-SPEC-SPD-FLOW-002 + +--- + +### 筛选与查询 + +#### 功能验证点 + +- **TP_FLOW_INS_009**(高)|默认日期区间查询|验证要点:进入后带默认发起时间区间,点「查询」列表有数据或空态明确,「共 N 条」与结果逻辑一致|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_010**(高)|单据编号精确查询|验证要点:输入完整单号后至多一条,且编号、业务类型与该行一致|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_011**(高)|按审批状态筛选|验证要点:选择「审批通过」或「审批中」后列表状态列与所选一致|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_012**(高)|按业务类型筛选|验证要点:选择某类型后列表「业务类型」均为该类型|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_013**(高)|按发起时间区间筛选|验证要点:结果行「发起时间」落在起止日期内(含边界日)|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_014**(中)|按摘要关键字筛选|验证要点:输入摘要关键字后结果摘要包含该关键字;摘要为 `--` 的行不被非空关键字命中|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_015**(高)|多条件组合查询|验证要点:状态、业务类型、发起时间、摘要同时生效,抽样行满足全部已填条件|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_016**(中)|重置恢复默认|验证要点:填写后点「重置」,日期回到默认区间,其余条件清空,「共 N 条」恢复|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 边界验证点 + +- **TP_FLOW_INS_017**(中)|超长单据编号或摘要|验证要点:超长输入可查询或被截断,页面不崩溃|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_018**(中)|开始日期晚于结束日期|验证要点:非法区间被拒绝或自动纠正,不返回错乱时间结果|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_019**(低)|仅空格摘要|验证要点:按空查询处理或提示,不报错|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 异常验证点 + +- **TP_FLOW_INS_020**(中)|查询接口失败|验证要点:失败有错误提示,不白屏、不把过期列表展示为成功态|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_021**(低)|无匹配空态|验证要点:总数为 0,空态明确|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 集成验证点 + +- **TP_FLOW_INS_022**(中)|筛选结果与分页总数一致|验证要点:查询后可见行逻辑与「共 N 条」一致|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_023**(高)|业务类型与审批流程设置同源|验证要点:「业务类型」下拉与审批流程设置左侧类型一致(允许按权限裁剪)|关联需求:UI-SPEC-SPD-FLOW-002 + +--- + +### 列表与分页 + +#### 功能验证点 + +- **TP_FLOW_INS_024**(高)|列表核心列完整|验证要点:主表展示序号、单据编号、业务类型、发起人、发起部门、发起时间、摘要、审批状态、操作(详情)|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_025**(中)|单据编号前缀与业务类型对应|验证要点:`PLAN` 对应采购计划、`DB` 对应二级库调拨、`GC` 对应产品信息变更、`BUY` 对应请领单(以现网枚举为准)|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_026**(中)|空摘要展示为 --|验证要点:无摘要行显示 `--`,长摘要可截断|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_027**(中)|窄列截断不影响查询|验证要点:发起人/部门/时间截断展示时,按完整单号搜索仍能命中|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_028**(中)|分页翻页|验证要点:总条数大于一页时可翻页,数据更换|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_029**(中)|每页条数切换|验证要点:切换后当前页行数不超过所选值,总数不变|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_030**(中)|跳至指定页|验证要点:输入合法页码后跳到对应页|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 边界验证点 + +- **TP_FLOW_INS_031**(中)|首页上一页不可用|验证要点:第 1 页时「上一页」禁用或点击无效|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_032**(中)|末页下一页不可用|验证要点:最后一页时「下一页」禁用或点击无效|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_033**(低)|跳至超出总页数|验证要点:大于总页数时被拒绝、纠正到末页或提示|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 异常验证点 + +- **TP_FLOW_INS_034**(低)|跳至非数字页码|验证要点:非法输入不跳转异常页|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 集成验证点 + +- **TP_FLOW_INS_035**(中)|翻页不丢失当前筛选|验证要点:带筛选翻页后条件保持,新页数据仍满足原筛选|关联需求:UI-SPEC-SPD-FLOW-002 + +--- + +### 审批详情 + +#### 功能验证点 + +- **TP_FLOW_INS_036**(高)|打开详情对应当前行|验证要点:点「详情」打开该单据审批过程,标题/单号与当前行单据编号一致|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_037**(中)|详情只读|验证要点:详情内不能改审批人或条件;改配置须回到审批流程设置|关联需求:UI-SPEC-SPD-FLOW-002 +- **TP_FLOW_INS_038**(中)|关闭详情保留列表状态|验证要点:返回后筛选条件与页码保持,列表仍是打开前的查询结果|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 边界验证点 + +- **TP_FLOW_INS_039**(低)|审批中与已通过均可打开详情|验证要点:两种状态行都能进入详情,节点展示与状态相符|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 异常验证点 + +- **TP_FLOW_INS_040**(中)|详情加载失败|验证要点:失败有提示,不把其它单据内容展示为当前单|关联需求:UI-SPEC-SPD-FLOW-002 + +#### 集成验证点 + +- **TP_FLOW_INS_041**(高)|详情审批链与流程设置一致|验证要点:新单据详情中的节点/人员与该业务类型在审批流程设置中命中的默认或自定义链一致|关联需求:UI-SPEC-SPD-FLOW-002 + + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、单据编号/状态/类型/时间/摘要筛选、列表列与前缀规则、分页、只读详情、与流程设置类型同源;共 **41** 条原子测试点(未声称全覆盖,断网类与仓库其它基础管理页同样省略) | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分;无某类时保留标题并说明 | +| 去重 | 登录拦截、会话过期与同模块其它页规则相同,本页仅保留菜单/URL/字段差异;编号前缀 `TP_FLOW_INS` | +| 待评审项 | ① 默认发起时间跨度大,断言「共 N 条」须先收窄日期 ② 本页无新增/导出/改审批人;写配置只在审批流程设置 ③ 详情扫描打开「采购计划详情」,其它业务类型详情标题以现网为准 | +| 下游 | 可转结构化 MD/CSV;扫描 `npm run discover:scan-flow`;自动化建议 `pages/spd/flow-inst.page.ts` | + diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..cf11516 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\346\265\201\347\250\213\350\256\276\347\275\256-\345\256\241\346\211\271\346\265\201\347\250\213\350\256\276\347\275\256-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,160 @@ +# 基础管理-流程设置-审批流程设置 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-FLOW-001.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/flowSet/workflow/flowSet` +**版本 / 定稿日期**:2026-10-20 +**元素扫描**:`data/scan/flow-def-elements.json`(43 项) +**定位器**:`locators/spd/flow-def.locator.yaml` + +--- + +## 基础设置 · 流程设置 · 审批流程设置 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_FLOW_DEF_001**(高)|菜单路径可达审批流程设置|验证要点:登录后通过「基础设置 → 流程设置 → 审批流程设置」进入 `/spd/basic/flowSet/workflow/flowSet`,左侧菜单与顶栏 Tab 高亮为审批流程设置,不串到同组其它页|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_002**(高)|页面主区加载完成|验证要点:左右分栏:左侧业务类型、右侧默认审批流与自定义审批流、保存/重置可见,无报错遮罩|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_003**(中)|同组流程设置菜单可切换|验证要点:在 审批流程设置 / 审批流实例 间切换后 URL 与页面内容对应,再返回审批流程设置状态正确|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 边界验证点 + +- **TP_FLOW_DEF_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/flowSet/workflow/flowSet`,页面正常渲染|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 异常验证点 + +- **TP_FLOW_DEF_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示业务数据|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录,不误写|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_007**(中)|无菜单权限|验证要点:无审批流程设置权限的账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 集成验证点 + +- **TP_FLOW_DEF_008**(中)|顶栏身份与可管理范围一致|验证要点:顶栏登录身份与本页可查看的审批单据范围不矛盾|关联需求:UI-SPEC-SPD-FLOW-001 + +--- + +### 左侧业务类型 + +#### 功能验证点 + +- **TP_FLOW_DEF_009**(高)|业务类型列表可见|验证要点:左侧列出采购计划、请领单、产品准入等类型,当前项高亮|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_010**(高)|按流程名称过滤类型|验证要点:输入关键字后仅保留名称匹配的业务类型|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_011**(中)|清空关键字恢复全量|验证要点:清空后类型列表恢复过滤前全量|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_012**(高)|切换类型刷新右侧配置|验证要点:点选另一类型后,默认审批流与自定义审批流与该类型已保存配置一致,不残留上一类型节点|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 边界验证点 + +- **TP_FLOW_DEF_013**(中)|超长流程名称关键字|验证要点:超长输入可过滤或被截断,页面不崩溃|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_014**(低)|仅空格关键字|验证要点:按空过滤处理或提示,不报错|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 异常验证点 + +- **TP_FLOW_DEF_015**(低)|无匹配类型|验证要点:无命中时左侧为空或明确无数据,右侧不展示上一类型配置为当前配置|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 集成验证点 + +- **TP_FLOW_DEF_016**(高)|类型与审批流实例业务类型同源|验证要点:左侧类型集合与审批流实例「业务类型」下拉选项一致(允许按权限裁剪)|关联需求:UI-SPEC-SPD-FLOW-001 + +--- + +### 默认审批流 + +#### 功能验证点 + +- **TP_FLOW_DEF_017**(高)|默认审批流节点可见|验证要点:右侧「默认审批流」展示节点卡片,含节点类型、账号、人员|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_018**(中)|可追加默认流节点|验证要点:点节点旁「+」可增加审批节点(保存前仅在当前页可见)|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 边界验证点 + +- **TP_FLOW_DEF_019**(低)|未配置自定义流时走默认链|验证要点:该类型无自定义流时,新单据按默认审批人链流转(以审批流实例抽样为准)|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 异常验证点 + +- **TP_FLOW_DEF_020**(中)|默认流节点人员为空拦截|验证要点:保存时默认链缺少人员被拦截并提示,不写入空节点|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 集成验证点 + +- **TP_FLOW_DEF_021**(中)|只改自定义流时默认链不变|验证要点:仅增删自定义行并保存后,默认审批人节点保持原值|关联需求:UI-SPEC-SPD-FLOW-001 + +--- + +### 自定义审批流 + +#### 功能验证点 + +- **TP_FLOW_DEF_022**(高)|新增流程增加一行|验证要点:点「新增流程」自定义流增加一行,含流程名称、条件、审批流程、删除|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_023**(中)|删除行未保存不落库|验证要点:删除自定义行后不点保存,重新进入该类型行仍在|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_024**(高)|保存后自定义流持久化|验证要点:填写名称/条件/节点后保存,重新进入该类型配置与保存时一致|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 边界验证点 + +- **TP_FLOW_DEF_025**(中)|流程名称必填|验证要点:名称空时保存被拦截|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_026**(中)|条件必填|验证要点:条件空时保存被拦截|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_027**(中)|审批流程必填|验证要点:无审批节点时保存被拦截|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 异常验证点 + +- **TP_FLOW_DEF_028**(高)|必填为空拦截|验证要点:流程名称/条件/审批流程任一为空点保存被拦截并提示,不关闭、不落库|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_029**(中)|保存接口失败|验证要点:失败有提示,再次进入不是半成功配置|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 集成验证点 + +- **TP_FLOW_DEF_030**(高)|自定义条件命中后新单走该链|验证要点:保存后新产生的符合条件单据在审批流实例中按该自定义链流转,而非默认链|关联需求:UI-SPEC-SPD-FLOW-001 + +--- + +### 条件配置 + +#### 功能验证点 + +- **TP_FLOW_DEF_031**(高)|打开条件配置|验证要点:条件列「编辑」打开「条件配置」,可添加条件行并保存/取消|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_032**(高)|保存条件回写行内文案|验证要点:配置如「是否超限:是」或「科室:中心库(洪山)」后保存,行内条件展示与配置一致|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_033**(中)|取消不改行内条件|验证要点:修改后点取消,行内条件仍为打开前的值|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 边界验证点 + +- **TP_FLOW_DEF_034**(低)|可删除条件行|验证要点:条件配置内「删除」去掉一行,保存后不再出现该条件|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 异常验证点 + +- **TP_FLOW_DEF_035**(中)|条件不完整拦截|验证要点:条件类型或取值空时保存被拦截|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 集成验证点 + +- **TP_FLOW_DEF_036**(中)|科室条件与库房/科室主数据同源|验证要点:条件选科室时选项来自主数据,与库区设置/用户所属科室不矛盾|关联需求:UI-SPEC-SPD-FLOW-001 + +--- + +### 保存与重置 + +#### 功能验证点 + +- **TP_FLOW_DEF_037**(高)|保存写入当前业务类型|验证要点:点「保存」只持久化当前选中类型的默认流与自定义流|关联需求:UI-SPEC-SPD-FLOW-001 +- **TP_FLOW_DEF_038**(高)|重置放弃未保存改动|验证要点:改节点或自定义行后点「重置」,页面回到上次保存结果|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 边界验证点 + +- **TP_FLOW_DEF_039**(低)|无改动点保存不报错|验证要点:未修改直接保存成功或提示无变更,配置不变|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 异常验证点 + +- **TP_FLOW_DEF_040**(中)|未保存切换类型|验证要点:有未保存改动时切换业务类型须提示后丢弃或不允许切换,不得把上一类型的改动写入下一类型|关联需求:UI-SPEC-SPD-FLOW-001 + +#### 集成验证点 + +- **TP_FLOW_DEF_041**(中)|保存不影响历史审批实例|验证要点:改审批人后,已存在的审批流实例详情仍为发起时的快照或按产品规则说明;不得无说明地改写历史已通过单|关联需求:UI-SPEC-SPD-FLOW-001 + + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、业务类型过滤/切换、默认审批流、自定义流增删、条件配置、保存重置、与审批流实例类型同源;共 **41** 条原子测试点(未声称全覆盖,断网类与仓库其它基础管理页同样省略) | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分;无某类时保留标题并说明 | +| 去重 | 登录拦截、会话过期与同模块其它页规则相同,本页仅保留菜单/URL/字段差异;编号前缀 `TP_FLOW_DEF` | +| 待评审项 | ① 保存会改线上审批路由,自动化冒烟只读结构、不提交 ② 未保存切类型按「提示后丢弃、不串写」写点,若自动保存需改判定 ③ 历史已通过实例是否快照冻结待产品确认 | +| 下游 | 可转结构化 MD/CSV;扫描 `npm run discover:scan-flow`;自动化建议 `pages/spd/flow-def.page.ts` | + diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..fd5db15 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\345\214\273\345\205\261\344\275\223\347\224\250\346\210\267\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,161 @@ +# 基础管理-用户管理-医共体用户管理 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-USER-002.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/user/platform/personCom` +**版本 / 定稿日期**:2026-10-20 +**元素扫描**:`data/scan/basic-user-union-elements.json`(221 项) +**定位器**:`locators/spd/basic-user-union.locator.yaml` + +--- + +## 基础设置 · 用户管理 · 医共体用户管理 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_USER_UNI_001**(高)|菜单路径可达医共体用户管理|验证要点:登录后通过「基础设置 → 用户管理 → 医共体用户管理」进入目标页,左侧菜单与顶栏 Tab 高亮为医共体用户管理,不串到同组其它页|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_002**(高)|页面主区加载完成|验证要点:整页单表渲染:所属医院等筛选、工具栏、列表列头与分页可见,无报错遮罩|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_003**(中)|同组用户管理菜单可切换|验证要点:在 用户管理 / 医共体用户管理 / 角色管理 间切换后 URL 与页面内容对应,再返回医共体用户管理状态正确|关联需求:UI-SPEC-SPD-USER-002 + +#### 边界验证点 + +- **TP_USER_UNI_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/user/platform/personCom`,页面正常渲染|关联需求:UI-SPEC-SPD-USER-002 + +#### 异常验证点 + +- **TP_USER_UNI_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示用户/角色业务数据|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录,不误写|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_007**(中)|无菜单权限|验证要点:无医共体用户管理权限的账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-USER-002 + +#### 集成验证点 + +- **TP_USER_UNI_008**(中)|顶栏身份与可管理范围一致|验证要点:顶栏院区/登录医院与本页可管理的「所属医院」范围一致|关联需求:UI-SPEC-SPD-USER-002 + +--- + +### 筛选与查询 + +#### 功能验证点 + +- **TP_USER_UNI_009**(高)|默认条件查询|验证要点:进入后点「查询」,列表有数据或空态明确,「共 N 条」与行数逻辑一致|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_010**(高)|姓名/登录账号关键字|验证要点:「姓名」框(占位「姓名、登录账号」)输入后结果姓名或账号匹配|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_011**(高)|所属医院筛选|验证要点:切换「所属医院」后列表仅该院用户,「共 N 条」同步变化|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_012**(高)|联系方式筛选|验证要点:按「联系方式」关键字查询,结果联系方式匹配|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_013**(高)|姓名+医院+联系方式组合查询|验证要点:多条件同时生效,结果同时满足|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_014**(中)|重置恢复默认|验证要点:点「重置」后医院等条件恢复默认(含默认医院),列表刷新|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_015**(中)|展开高级筛选|验证要点:点「高级筛选」展开后可见所属医院、联系方式、科室、角色及「筛选」|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_016**(中)|高级筛选按科室过滤|验证要点:高级筛选选择科室后点「筛选」,列表「所属科室」与所选一致|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_017**(中)|高级筛选按角色过滤|验证要点:高级筛选选择角色后点「筛选」,列表「角色」列包含该角色|关联需求:UI-SPEC-SPD-USER-002 + +#### 边界验证点 + +- **TP_USER_UNI_018**(中)|超长关键字|验证要点:超长姓名或联系方式关键字不崩溃,按规则截断或无匹配空态|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_019**(低)|仅空格联系方式|验证要点:空格按空查询处理或提示,不报错|关联需求:UI-SPEC-SPD-USER-002 + +#### 异常验证点 + +- **TP_USER_UNI_020**(中)|查询接口失败|验证要点:失败有提示,不白屏|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_021**(低)|无匹配空态|验证要点:总数为 0,空态明确|关联需求:UI-SPEC-SPD-USER-002 + +#### 集成验证点 + +- **TP_USER_UNI_022**(中)|筛选结果与分页总数一致|验证要点:当前医院+条件下可见行逻辑与「共 N 条」一致|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_023**(高)|默认医院与顶栏/登录医院一致|验证要点:进入页时所属医院默认值与当前登录医院或顶栏院区约定一致|关联需求:UI-SPEC-SPD-USER-002 + +--- + +### 列表与分页 + +#### 功能验证点 + +- **TP_USER_UNI_024**(高)|列表核心列完整且顺序正确|验证要点:列顺序为登录账号、姓名、联系方式、所属医院、所属科室、医院员工编号、角色、科室任职数、操作(编辑/授权/重置密码);勿用用户管理页列断言|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_025**(中)|登录账号列截断|验证要点:长账号在列宽内截断;按账号搜索/断言用筛选框而非截断后的单元格文本|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_026**(中)|分页翻页|验证要点:总条数大于一页时可翻至第 2 页,数据更换且页码高亮正确|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_027**(中)|每页条数切换|验证要点:切换每页条数后当前页行数不超过所选值,总数不变|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_028**(中)|跳至指定页|验证要点:分页「跳至」输入合法页码后跳到对应页|关联需求:UI-SPEC-SPD-USER-002 + +#### 边界验证点 + +- **TP_USER_UNI_029**(中)|首页上一页不可用|验证要点:第 1 页「上一页」禁用或无效|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_030**(中)|末页下一页不可用|验证要点:最后一页时「下一页」禁用或点击无效|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_031**(低)|某医院无用户|验证要点:切换到无用户医院时空态明确,总数为 0|关联需求:UI-SPEC-SPD-USER-002 + +#### 异常验证点 + +- **TP_USER_UNI_032**(低)|跳至非数字页码|验证要点:非法页码不跳转异常页|关联需求:UI-SPEC-SPD-USER-002 + +#### 集成验证点 + +- **TP_USER_UNI_033**(高)|与用户管理页同一用户字段一致|验证要点:同一登录账号在两页的姓名、角色、所属科室一致,本页仅多「所属医院」维度|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_034**(中)|翻页不丢失医院条件|验证要点:带所属医院筛选翻页后条件保持|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_035**(中)|科室任职数规则同用户管理|验证要点:本页「科室任职数」判定同 TP_USER_USR 任职数与任职记录一致,无任职为 `--`|关联需求:UI-SPEC-SPD-USER-002 + +--- + +### 新增与编辑 + +#### 功能验证点 + +- **TP_USER_UNI_036**(高)|打开新增弹窗|验证要点:点「新增」弹出用户表单,含账号、姓名、联系方式等及取消/确定|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_037**(高)|合法保存落在当前医院|验证要点:保存成功后列表在当前「所属医院」下可见该账号|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_038**(中)|新增取消不落库|验证要点:填写后点取消,当前医院列表不出现该账号|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_039**(高)|打开编辑并回填|验证要点:行内「编辑」打开「编辑用户」,回填与列表行一致|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_040**(高)|修改后确定落库|验证要点:修改后确定,本页列表与再次编辑回看均为新值|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_041**(中)|编辑取消不保存|验证要点:修改后点取消,列表与再次打开仍为修改前值|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_042**(中)|启用禁用规则同用户管理|验证要点:编辑弹窗启用/禁用后,按状态筛选能命中;禁用登录受限同 TP_USER_USR|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_043**(中)|签名规则同用户管理|验证要点:编辑弹窗签名上传/展示规则同 TP_USER_USR,本页不重复展开|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_044**(中)|Excel 导入入口可达|验证要点:「Excel导入」可打开导入流程|关联需求:UI-SPEC-SPD-USER-002 + +#### 边界验证点 + +- **TP_USER_UNI_045**(中)|字段最大长度|验证要点:账号/姓名最大长度可保存|关联需求:UI-SPEC-SPD-USER-002 + +#### 异常验证点 + +- **TP_USER_UNI_046**(高)|必填为空拦截|验证要点:账号或姓名为空确定被拦截,不落库|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_047**(高)|跨院重复账号不静默覆盖|验证要点:使用他院已占用登录账号保存时拒绝并提示,不覆盖他院用户|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_048**(中)|Excel 导入失败有提示|验证要点:导入文件失败或模板不符时有错误提示,不误导为成功|关联需求:UI-SPEC-SPD-USER-002 + +#### 集成验证点 + +- **TP_USER_UNI_049**(中)|本页编辑结果回写用户管理|验证要点:在医共体页修改姓名/联系方式后,用户管理页同一账号字段一致|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_050**(中)|Excel 导入归属当前医院|验证要点:导入成功的数据出现在当前「所属医院」列表,不写入其它院|关联需求:UI-SPEC-SPD-USER-002 + +--- + +### 授权与重置密码 + +#### 功能验证点 + +- **TP_USER_UNI_051**(高)|行内授权可打开|验证要点:点「授权」打开授权层;勾选持久化规则同 TP_USER_USR,本页只校验当前行|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_052**(高)|行内重置密码可执行|验证要点:点「重置密码」可完成改密;改密后登录规则同 TP_USER_USR|关联需求:UI-SPEC-SPD-USER-002 + +#### 边界验证点 + +- **TP_USER_UNI_053**(低)|对他院用户写操作不可用|验证要点:筛选他院后,对非当前医院用户的编辑/授权/重置入口不可见或拒绝|关联需求:UI-SPEC-SPD-USER-002 + +#### 异常验证点 + +- **TP_USER_UNI_054**(中)|授权保存失败|验证要点:接口失败有提示,再次打开不是半成功勾选|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_055**(中)|重置密码失败|验证要点:接口失败有提示,原密码仍可登录|关联需求:UI-SPEC-SPD-USER-002 + +#### 集成验证点 + +- **TP_USER_UNI_056**(高)|编辑不串改他院同名用户|验证要点:保存编辑后仅当前所属医院下该账号变更,其它院区同名用户不变|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_057**(高)|授权不串改他院账号|验证要点:授权只作用于当前行登录账号+所属医院,其它院区同账号勾选不变|关联需求:UI-SPEC-SPD-USER-002 +- **TP_USER_UNI_058**(高)|重置密码不串改他院账号|验证要点:重置密码只改当前行,其它院区同账号原密码仍有效|关联需求:UI-SPEC-SPD-USER-002 + + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、所属医院筛选、列顺序差异、跨院不覆盖/不串改;启用禁用/签名/任职/授权持久化引用 TP_USER_USR,本页不重复展开;共 **58** 条原子测试点(未声称全覆盖,断网类与仓库其它基础管理页同样省略) | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分;无某类时保留标题并说明 | +| 去重 | 登录拦截、会话过期与同模块其它用户管理页规则相同,本页仅保留菜单/URL/字段差异;编号前缀 `TP_USER_UNI` | +| 待评审项 | ① 新增/授权弹窗扫描未稳定打开,以现网为准 ② 跨院登录账号按拒绝且不覆盖写点,若产品允许共用需改判定 ③ 所属医院默认值与顶栏约定以现网为准 ④ 签名/启用禁用/任职明细/改密后登录同用户管理对应 TP,本页只保留医院维度 | +| 下游 | 可转结构化 MD/CSV;扫描 `npm run discover:scan-user`;自动化建议 `pages/spd/basic-user-union.page.ts` | + diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..83ece1f --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,216 @@ +# 基础管理-用户管理-用户管理 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-USER-001.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/user/platform/person` +**版本 / 定稿日期**:2026-10-20 +**元素扫描**:`data/scan/basic-user-elements.json`(219 项) +**定位器**:`locators/spd/basic-user.locator.yaml` + +--- + +## 基础设置 · 用户管理 · 用户管理 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_USER_USR_001**(高)|菜单路径可达用户管理|验证要点:一级菜单与叶子菜单均名「用户管理」,须点「基础设置 → 用户管理(子菜单)→ 用户管理(叶子)」进入 `/person`,不误入医共体用户管理或角色管理|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_002**(高)|页面主区加载完成|验证要点:整页单表渲染:筛选区、工具栏(新增/Excel导入)、列表列头与分页可见,无报错遮罩|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_003**(中)|同组用户管理菜单可切换|验证要点:在 用户管理 / 医共体用户管理 / 角色管理 间切换后 URL 与页面内容对应,再返回用户管理状态正确|关联需求:UI-SPEC-SPD-USER-001 + +#### 边界验证点 + +- **TP_USER_USR_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/user/platform/person`,页面正常渲染|关联需求:UI-SPEC-SPD-USER-001 + +#### 异常验证点 + +- **TP_USER_USR_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示用户/角色业务数据|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录,不误写|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_007**(中)|无菜单权限|验证要点:无用户管理权限的账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-USER-001 + +#### 集成验证点 + +- **TP_USER_USR_008**(中)|顶栏身份与可管理范围一致|验证要点:顶栏院区、登录用户与本页可管理用户范围一致|关联需求:UI-SPEC-SPD-USER-001 + +--- + +### 筛选与查询 + +#### 功能验证点 + +- **TP_USER_USR_009**(高)|默认条件查询|验证要点:进入后点「查询」,列表有数据或空态明确,底部分页「共 N 条」与行数逻辑一致|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_010**(高)|姓名/登录账号关键字|验证要点:在「姓名」框(占位「姓名、登录账号」)输入关键字后,结果姓名或登录账号匹配|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_011**(高)|科室下拉筛选|验证要点:选择某科室后列表「所属科室」与所选一致(未分配显示 `--` 的行不应出现在指定科室结果中)|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_012**(高)|角色下拉筛选|验证要点:选择某角色后列表「角色」列包含该角色|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_013**(高)|姓名+科室+角色组合查询|验证要点:多个已填条件同时生效,结果同时满足全部条件|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_014**(中)|重置恢复默认|验证要点:填写筛选后点「重置」,条件恢复默认,列表按默认规则刷新|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_015**(中)|展开高级筛选|验证要点:点「高级筛选」展开面板,可见姓名/科室/角色/状态及底部「重置」「筛选」|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_016**(高)|高级筛选按状态过滤|验证要点:高级筛选选择启用或禁用后点「筛选」,列表仅保留对应状态用户|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_017**(中)|高级筛选内重置|验证要点:高级筛选填写后点面板「重置」,条件清空,列表恢复默认|关联需求:UI-SPEC-SPD-USER-001 + +#### 边界验证点 + +- **TP_USER_USR_018**(中)|超长姓名/账号关键字|验证要点:输入达到或超过允许长度的关键字,查询被接受并按规则截断或返回匹配,页面不崩溃|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_019**(低)|仅空格关键字|验证要点:按空查询处理或提示,不报错|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_020**(低)|科室/角色下拉可搜索|验证要点:下拉内输入关键字后选项收敛为匹配项|关联需求:UI-SPEC-SPD-USER-001 + +#### 异常验证点 + +- **TP_USER_USR_021**(中)|查询接口失败|验证要点:列表查询失败时有错误提示,不白屏、不把过期数据展示为成功态|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_022**(低)|无匹配结果空态|验证要点:条件无匹配时「暂无数据」明确,分页总数为 0|关联需求:UI-SPEC-SPD-USER-001 + +#### 集成验证点 + +- **TP_USER_USR_023**(中)|筛选结果与分页总数一致|验证要点:查询后可见行逻辑与「共 N 条」一致|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_024**(高)|角色下拉与角色管理同源|验证要点:本页角色筛选项集合与「角色管理」左侧角色名称一致(允许按权限裁剪)|关联需求:UI-SPEC-SPD-USER-001 + +--- + +### 列表与分页 + +#### 功能验证点 + +- **TP_USER_USR_025**(高)|列表核心列完整|验证要点:主表展示序号、姓名、登录账号、联系方式、所属科室、医院员工编号、角色、科室任职数、操作(编辑/授权/重置密码)|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_026**(中)|列表不含所属医院列|验证要点:本页列中无「所属医院」,与医共体用户管理页区分|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_027**(中)|多角色列表截断展示|验证要点:用户绑定多个角色时列表「角色」列截断(如带省略号)|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_028**(中)|空值展示为 --|验证要点:未分配科室、未填工号、无任职时对应列显示 `--`|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_029**(中)|表格密度切换|验证要点:密度切换后列表仍可读,列不丢失|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_030**(低)|无批量操作|验证要点:序号列可勾选,但工具栏无批量启用/删除/授权;按「无批量」处理,不测批量提交|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_031**(中)|分页翻页|验证要点:总条数大于一页时可翻至第 2 页,数据更换且页码高亮正确|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_032**(中)|每页条数切换|验证要点:切换「50 条/页」等后当前页行数不超过所选值,总数不变|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_033**(中)|跳至指定页|验证要点:分页「跳至」输入合法页码后跳到对应页|关联需求:UI-SPEC-SPD-USER-001 + +#### 边界验证点 + +- **TP_USER_USR_034**(中)|首页上一页不可用|验证要点:第 1 页时「上一页」禁用或点击无效|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_035**(中)|末页下一页不可用|验证要点:最后一页时「下一页」禁用或点击无效|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_036**(低)|跳至超出总页数|验证要点:跳至大于总页数的页码时被拒绝、纠正到末页或给出提示|关联需求:UI-SPEC-SPD-USER-001 + +#### 异常验证点 + +- **TP_USER_USR_037**(低)|跳至非数字页码|验证要点:跳至输入非法字符时不跳转异常页,输入被忽略或提示|关联需求:UI-SPEC-SPD-USER-001 + +#### 集成验证点 + +- **TP_USER_USR_038**(中)|翻页不丢失当前筛选|验证要点:带筛选条件翻页后条件保持,新页数据仍满足原筛选|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_039**(中)|科室任职数与任职记录一致|验证要点:「科室任职数」等于该用户在各科室任职条数,无任职为 `--`|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_040**(中)|任职数展示或可下钻|验证要点:任职数为只读数字;若可点开明细,则明细条数与数字一致|关联需求:UI-SPEC-SPD-USER-001 + +--- + +### 新增用户 + +#### 功能验证点 + +- **TP_USER_USR_041**(高)|打开新增用户弹窗|验证要点:点「新增」弹出新增/编辑同类表单,含账号、姓名、联系方式、医院员工编号、启用/禁用、取消/确定|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_042**(高)|合法必填保存成功|验证要点:填写必填后确定,弹窗关闭,列表出现新记录且字段与提交一致|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_043**(中)|取消不落库|验证要点:填写后点「取消」或关闭,列表不新增对应账号|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_044**(中)|Excel 导入入口可达|验证要点:工具栏「Excel导入」可打开导入流程或选择文件|关联需求:UI-SPEC-SPD-USER-001 + +#### 边界验证点 + +- **TP_USER_USR_045**(中)|字段达到允许最大长度|验证要点:账号/姓名取最大允许长度可保存成功|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_046**(中)|联系方式合法号码|验证要点:填写合法手机号/电话可保存,列表联系方式与提交一致|关联需求:UI-SPEC-SPD-USER-001 + +#### 异常验证点 + +- **TP_USER_USR_047**(高)|必填为空拦截|验证要点:账号或姓名为空点确定被拦截并提示,不关闭弹窗、不落库|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_048**(高)|登录账号重复拦截|验证要点:使用已存在账号保存失败并提示,不覆盖原用户|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_049**(中)|联系方式非法拦截|验证要点:明显非法号码(过短数字)点确定被拦截并提示,不落库|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_050**(中)|保存接口失败|验证要点:失败有错误提示,弹窗仍在,列表不出现半成品成功态|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_051**(中)|重复点击确定|验证要点:连续快速点击只产生一条记录或第二次被防重|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_052**(中)|Excel 导入失败有提示|验证要点:导入文件失败或模板不符时有错误提示,不误导为成功|关联需求:UI-SPEC-SPD-USER-001 + +#### 集成验证点 + +- **TP_USER_USR_053**(中)|新用户可出现在医共体用户管理|验证要点:本页新增成功后,医共体用户管理按所属医院能查到同一登录账号(若业务归属医共体)|关联需求:UI-SPEC-SPD-USER-001 + +--- + +### 编辑用户 + +#### 功能验证点 + +- **TP_USER_USR_054**(高)|打开编辑用户弹窗并回填|验证要点:行内「编辑」打开「编辑用户」,回填账号、姓名、联系方式、医院员工编号、启用/禁用|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_055**(中)|编辑页可查看全部角色|验证要点:多角色用户在编辑弹窗能看到完整角色列表,不被列表截断值替代|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_056**(中)|签名图片可上传或展示|验证要点:编辑弹窗含签名图片,已有签名可展示,可重新上传后随确定保存|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_057**(高)|修改后确定落库|验证要点:修改姓名或联系方式后确定,列表与再次编辑回看均为新值|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_058**(中)|编辑取消不保存|验证要点:修改后点「取消」,列表与再次打开仍为修改前值|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_059**(高)|启用/禁用切换|验证要点:在编辑弹窗将状态改为禁用或启用并确定后,高级筛选按新状态能查到该用户|关联需求:UI-SPEC-SPD-USER-001 + +#### 边界验证点 + +- **TP_USER_USR_060**(低)|不改字段直接确定|验证要点:打开编辑不改内容点确定,记录保持原值且不报错|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_061**(中)|编辑时账号只读|验证要点:编辑弹窗中登录账号不可改|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_062**(低)|不传签名可保存|验证要点:签名非必填时不上传仍可确定成功|关联需求:UI-SPEC-SPD-USER-001 + +#### 异常验证点 + +- **TP_USER_USR_063**(高)|编辑清空必填拦截|验证要点:清空姓名后确定被拦截,原记录不被写成空|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_064**(中)|编辑已删除用户|验证要点:记录已被删除后再确定,提示不存在,不产生幽灵更新|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_065**(中)|签名非图片格式拦截|验证要点:上传非图片或超限文件时拒绝保存并提示|关联需求:UI-SPEC-SPD-USER-001 + +#### 集成验证点 + +- **TP_USER_USR_066**(高)|禁用后登录受限|验证要点:用户禁用后使用该账号登录被拒绝或权限收回|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_067**(中)|再启用保持原授权|验证要点:禁用后再启用,菜单授权仍为禁用前已保存的勾选,不额外膨胀、不自动清空|关联需求:UI-SPEC-SPD-USER-001 + +--- + +### 授权 + +#### 功能验证点 + +- **TP_USER_USR_068**(高)|打开授权|验证要点:行内「授权」打开授权层(弹窗或权限树),展示可勾选菜单/功能|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_069**(高)|保存后勾选持久化|验证要点:勾选后保存,关闭再打开同一用户,勾选与保存时一致|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_070**(中)|取消不生效|验证要点:勾选后点取消/关闭,再次打开仍为操作前勾选|关联需求:UI-SPEC-SPD-USER-001 + +#### 边界验证点 + +- **TP_USER_USR_071**(中)|未勾选任何权限可保存|验证要点:全部取消勾选后仍可保存成功,再次打开为全不勾选|关联需求:UI-SPEC-SPD-USER-001 + +#### 异常验证点 + +- **TP_USER_USR_072**(中)|授权保存失败|验证要点:接口失败有提示,再次打开不是半成功勾选|关联需求:UI-SPEC-SPD-USER-001 + +#### 集成验证点 + +- **TP_USER_USR_073**(高)|授权后仅可见已授权菜单|验证要点:保存后该用户登录仅能看到已授权菜单|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_074**(中)|授权节点与角色权限树语义一致|验证要点:用户授权层菜单节点名称与角色管理权限树对应节点一致|关联需求:UI-SPEC-SPD-USER-001 + +--- + +### 重置密码 + +#### 功能验证点 + +- **TP_USER_USR_075**(高)|行内重置密码|验证要点:点「重置密码」出现确认或密码表单,确认后该用户密码被更新|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_076**(中)|编辑弹窗内重置密码|验证要点:「编辑用户」内「重置密码」与行内重置效果一致|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_077**(中)|取消不改密|验证要点:确认框点取消后原密码仍有效|关联需求:UI-SPEC-SPD-USER-001 + +#### 边界验证点 + +- **TP_USER_USR_078**(低)|对已禁用用户可重置|验证要点:禁用用户允许重置密码,重置后账号仍为禁用、不误启用|关联需求:UI-SPEC-SPD-USER-001 + +#### 异常验证点 + +- **TP_USER_USR_079**(中)|重置接口失败|验证要点:失败有提示,原密码仍可登录|关联需求:UI-SPEC-SPD-USER-001 +- **TP_USER_USR_080**(中)|无二次确认不得改密|验证要点:重置须经确认框;直接关闭或点取消不得改密|关联需求:UI-SPEC-SPD-USER-001 + +#### 集成验证点 + +- **TP_USER_USR_081**(高)|重置后原密码不可登录|验证要点:重置成功后原密码登录失败,新密码(或初始密码规则)可登录|关联需求:UI-SPEC-SPD-USER-001 + + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航、筛选查询、列表分页、新增/编辑(含签名与启用禁用)、授权、重置密码、权限与登录异常、同模块衔接;共 **81** 条原子测试点(未声称全覆盖,断网类与仓库其它基础管理页同样省略) | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分;无某类时保留标题并说明 | +| 去重 | 登录拦截、会话过期与同模块其它用户管理页规则相同,本页仅保留菜单/URL/字段差异;编号前缀 `TP_USER_USR` | +| 待评审项 | ① 新增弹窗扫描未稳定打开,字段以编辑弹窗与 UI-SPEC 为准 ② 授权层扫描未采集到,勾选控件以现网为准 ③ 编辑时登录账号按只读写点,若现网可改需改判定 ④ 空权限按允许保存写点,联系方式非法/签名非图片按拦截写点,禁用用户重置按允许且不启用写点 ⑤ UI-SPEC 截图未见「查询」,现网扫描有「查询」,测试点以扫描为准 ⑥ 列表序号可勾选但无批量工具栏,按无批量处理 | +| 下游 | 可转结构化 MD/CSV;扫描 `npm run discover:scan-user`;自动化建议 `pages/spd/basic-user.page.ts` | + diff --git "a/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" new file mode 100644 index 0000000..1935fc3 --- /dev/null +++ "b/testpoint/\345\237\272\347\241\200\347\256\241\347\220\206-\347\224\250\346\210\267\347\256\241\347\220\206-\350\247\222\350\211\262\347\256\241\347\220\206-\346\265\213\350\257\225\347\202\271\345\210\206\346\236\220-01.md" @@ -0,0 +1,132 @@ +# 基础管理-用户管理-角色管理 — 测试点分析 + +**关联需求**:`knowledge-base/08-UI专项规范库/UI-SPEC-SPD-USER-003.md` +**页面 URL**:`http://spdtest.cmic.com.cn:8080/spd/basic/user/platform/role` +**版本 / 定稿日期**:2026-10-20 +**元素扫描**:`data/scan/basic-role-elements.json`(1170 项) +**定位器**:`locators/spd/basic-role.locator.yaml` + +--- + +## 基础设置 · 用户管理 · 角色管理 + +### 页面访问与导航 + +#### 功能验证点 + +- **TP_USER_ROL_001**(高)|菜单路径可达角色管理|验证要点:登录后通过「基础设置 → 用户管理 → 角色管理」进入目标页,左侧菜单与顶栏 Tab 高亮为角色管理,不串到同组其它页|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_002**(高)|页面主区加载完成|验证要点:左右分栏渲染:左侧角色列表与右侧菜单权限树可见,「保存」「刷新」可见,无独立分页,无报错遮罩|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_003**(中)|同组用户管理菜单可切换|验证要点:在 用户管理 / 医共体用户管理 / 角色管理 间切换后 URL 与页面内容对应,再返回角色管理状态正确|关联需求:UI-SPEC-SPD-USER-003 + +#### 边界验证点 + +- **TP_USER_ROL_004**(低)|已登录直接打开 URL|验证要点:携带有效登录态打开 `/spd/basic/user/platform/role`,页面正常渲染|关联需求:UI-SPEC-SPD-USER-003 + +#### 异常验证点 + +- **TP_USER_ROL_005**(高)|未登录访问拦截|验证要点:无登录态访问 URL 跳转登录或不展示用户/角色业务数据|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_006**(中)|登录态过期|验证要点:会话失效后查询/写操作被拦截并提示重新登录,不误写|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_007**(中)|无菜单权限|验证要点:无角色管理权限的账号不可见菜单或 URL 被拒绝|关联需求:UI-SPEC-SPD-USER-003 + +#### 集成验证点 + +- **TP_USER_ROL_008**(中)|顶栏身份与可管理范围一致|验证要点:当前登录人可维护角色与权限树,与顶栏登录身份不矛盾;本页无所属医院维度|关联需求:UI-SPEC-SPD-USER-003 + +--- + +### 左侧角色列表 + +#### 功能验证点 + +- **TP_USER_ROL_009**(高)|角色列表加载|验证要点:左侧列出角色名称(如医院管理员、中心库库管、护士等),可点选|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_010**(高)|角色名称搜索过滤|验证要点:在角色名称框输入关键字后列表收敛为匹配项(即时或回车,以现网为准)|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_011**(高)|选中角色刷新右侧权限树|验证要点:点击某角色后右侧菜单权限勾选刷新为该角色配置,无上一角色残留|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_012**(中)|清空搜索恢复全量|验证要点:清空角色搜索后列表恢复未过滤的完整角色|关联需求:UI-SPEC-SPD-USER-003 + +#### 边界验证点 + +- **TP_USER_ROL_013**(低)|搜索超长关键字|验证要点:超长不崩溃,无匹配时左栏空态明确|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_014**(低)|仅空格搜索|验证要点:按空处理或提示,不报错|关联需求:UI-SPEC-SPD-USER-003 + +#### 异常验证点 + +- **TP_USER_ROL_015**(中)|角色列表加载失败|验证要点:失败有提示,不白屏|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_016**(低)|无匹配角色空态|验证要点:搜索无结果时空态明确|关联需求:UI-SPEC-SPD-USER-003 + +#### 集成验证点 + +- **TP_USER_ROL_017**(高)|角色名与用户管理角色下拉同源|验证要点:左栏角色名称集合与用户管理/医共体用户管理「角色」筛选项一致(允许按权限裁剪)|关联需求:UI-SPEC-SPD-USER-003 + +--- + +### 右侧菜单权限树 + +#### 功能验证点 + +- **TP_USER_ROL_018**(高)|权限树按应用与模块展开|验证要点:右侧为「菜单权限」树,根为「SPD内网系统PC端」,下含基础设置/运维管理/厂商管理/产品管理等模块;节点可展开/折叠,展开后可见子菜单|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_019**(高)|勾选父节点联动子节点|验证要点:勾选某模块父节点后其子菜单/功能全部勾选|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_020**(高)|取消父节点联动取消子节点|验证要点:取消父节点后子节点全部取消|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_021**(中)|部分勾选呈半选态|验证要点:只勾选部分子节点时父节点为半选|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_022**(高)|保存授权持久化|验证要点:点「保存」成功后刷新或重新进入,该角色勾选与保存时一致|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_023**(中)|刷新恢复未保存勾选|验证要点:改勾选后点「刷新」且未保存,树恢复为上次已保存状态|关联需求:UI-SPEC-SPD-USER-003 + +#### 边界验证点 + +- **TP_USER_ROL_024**(中)|同名菜单按层级定位|验证要点:不同模块下同名菜单(若有)勾选互不影响,须按「模块名 + 菜单名」判定|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_025**(低)|树节点很多时可滚动勾选|验证要点:长树滚动后勾选仍对应正确节点,不点错行|关联需求:UI-SPEC-SPD-USER-003 + +#### 异常验证点 + +- **TP_USER_ROL_026**(中)|未选角色直接保存|验证要点:未选中角色时保存被拦截或提示,不把勾选写到错误角色|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_027**(高)|未保存勾选时切换角色|验证要点:改勾选未保存即点另一角色:须提示未保存;确认后丢弃未保存勾选并加载新角色,不把未保存勾选静默写到原角色或新角色|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_028**(中)|保存接口失败|验证要点:失败有提示,再次进入不是半成功勾选|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_029**(中)|权限树加载失败|验证要点:失败有提示,左栏角色仍可选|关联需求:UI-SPEC-SPD-USER-003 + +#### 集成验证点 + +- **TP_USER_ROL_030**(高)|角色授权后用户可见对应菜单|验证要点:为角色勾选「厂商查询」等菜单并保存后,仅绑定该角色的用户登录后能看到对应菜单|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_031**(高)|取消角色菜单后用户收回|验证要点:取消该角色已勾选菜单并保存后,仅绑定该角色的用户登录不再看到该菜单|关联需求:UI-SPEC-SPD-USER-003 + +--- + +### 角色增删改 + +#### 功能验证点 + +- **TP_USER_ROL_032**(中)|新增角色入口可达|验证要点:通过新增/「角色」工具按钮打开新增表单,可填写角色名称并保存,左栏出现新角色|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_033**(高)|编辑角色名称|验证要点:行内编辑可改角色名称,保存后左栏显示新名|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_034**(高)|删除需二次确认|验证要点:点删除出现确认框,含取消/确定|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_035**(高)|确认删除后左栏移除|验证要点:确认后该角色从左栏消失,再搜索名称无该角色|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_036**(中)|取消删除保留角色|验证要点:确认框点取消后角色仍在,权限树勾选不变|关联需求:UI-SPEC-SPD-USER-003 + +#### 边界验证点 + +- **TP_USER_ROL_037**(中)|角色名最大长度|验证要点:达到允许最大长度可保存|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_038**(低)|删除列表中最后一个自定义角色|验证要点:删除后左栏不报错,权限树进入空态或不可保存|关联需求:UI-SPEC-SPD-USER-003 + +#### 异常验证点 + +- **TP_USER_ROL_039**(高)|角色名称为空拦截|验证要点:空名称保存被拦截并提示|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_040**(高)|角色名称重复拦截|验证要点:与已有角色重名保存失败,不覆盖|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_041**(高)|预置角色不可删除|验证要点:医院管理员等预置角色删除入口不可见或确定后拒绝,角色仍在左栏|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_042**(高)|删除仍被用户引用的角色|验证要点:若有用户绑定该自定义角色则拒绝删除或提示先解绑,不出现孤儿授权|关联需求:UI-SPEC-SPD-USER-003 + +#### 集成验证点 + +- **TP_USER_ROL_043**(高)|新增后用户页角色下拉出现|验证要点:新增角色保存后,用户管理/医共体用户管理「角色」筛选项出现该名称|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_044**(高)|改名后用户页角色文案同步|验证要点:改名保存后,用户页下拉与已绑用户行「角色」列为新名|关联需求:UI-SPEC-SPD-USER-003 +- **TP_USER_ROL_045**(高)|删除后用户页无已删角色残留|验证要点:删除自定义角色后,用户页下拉与行「角色」列不再出现该名称|关联需求:UI-SPEC-SPD-USER-003 + + +--- + +## 质量自检摘要 + +| 项 | 结论 | +|----|------| +| 覆盖 | 已覆盖:导航(左右分栏无分页)、左栏角色搜索、右栏权限树勾选联动/保存/未保存切角色、角色增删改与用户页下拉同步;共 **45** 条原子测试点(未声称全覆盖,断网类与仓库其它基础管理页同样省略) | +| 分类 | 功能 / 边界 / 异常 / 集成四类均已按功能点拆分;无某类时保留标题并说明 | +| 去重 | 登录拦截、会话过期与同模块其它用户管理页规则相同,本页仅保留菜单/URL/字段差异;编号前缀 `TP_USER_ROL` | +| 待评审项 | ① 新增角色弹窗扫描未打开,入口以现网工具栏/行内图标为准,对应点优先级为中 ② 预置角色按不可删除写点,以现网预置名单为准 ③ 未保存切角色按「提示后丢弃、不静默写入」写点,若自动保存需改判定 ④ 本页无授权弹窗、无分页、无所属医院 | +| 下游 | 可转结构化 MD/CSV;扫描 `npm run discover:scan-user`;自动化建议 `pages/spd/basic-role.page.ts` | + diff --git a/tests/setup/auth.setup.ts b/tests/setup/auth.setup.ts index a185a69..fd5e033 100644 --- a/tests/setup/auth.setup.ts +++ b/tests/setup/auth.setup.ts @@ -2,6 +2,11 @@ import { test as setup } from '@playwright/test'; import { ensureAuthStorageFresh } from '../../utils/auth-session'; +/** + * 会话级登录态(只跑一次):登录成功后写入 auth/user.json。 + * 业务 project(chromium)通过 use.storageState 加载该文件; + * Playwright 仍为每条业务用例创建独立 BrowserContext,互不共享。 + */ setup.describe('auth setup', () => { setup('保存登录态', async ({ browser }) => { const result = await ensureAuthStorageFresh(browser, { honorForce: true }); diff --git a/tests/spd/admit-material-list.spec.ts b/tests/spd/admit-material-list.spec.ts new file mode 100644 index 0000000..70779a0 --- /dev/null +++ b/tests/spd/admit-material-list.spec.ts @@ -0,0 +1,82 @@ +import { test } from '../../fixtures'; + +/** + * 全量用例:映射 testcase/基础管理-准入管理-物料准入单列表-UI自动化测试用例-01.csv + */ +test.describe('基础设置 · 准入管理', () => { + test.describe('物料准入单列表 · 页面进入', () => { + test('ADMIT-MLS-001 菜单进入物料准入单列表', { tag: '@smoke' }, async ({ admitMaterialListPage }) => { + await admitMaterialListPage.openFromMenu(); + await admitMaterialListPage.expectEntered(); + }); + }); + + test.describe('物料准入单列表 · 筛选与状态', () => { + test('ADMIT-MLS-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.query(); + await admitMaterialListPage.expectListOrEmpty(); + }); + + test('ADMIT-MLS-003 按准入单号筛选', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.filterByOrderOrFallback(); + }); + + test('ADMIT-MLS-004 重置恢复默认', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.resetFilters(); + }); + + test('ADMIT-MLS-005 无匹配结果空态(mock反例)', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.mockEmpty(); + }); + + test('ADMIT-MLS-006 列表核心列完整', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.expectCoreColumns(); + }); + + test('ADMIT-MLS-007 切换已通过 Tab', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.switchPassed(); + }); + test('ADMIT-MLS-014 多条件组合查询(制单日期等6项)', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.filterCombination(); + }); + }); + + test.describe('物料准入单列表 · 单据详情', () => { + test('ADMIT-MLS-008 打开详情后返回', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.detailAndBack(); + }); + + test('ADMIT-MLS-009 详情产品条数与列表一致', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.detailCountMatches(); + }); + + test('ADMIT-MLS-010 审批进程可打开', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.openApproval(); + }); + + test('ADMIT-MLS-011 详情导出不白屏', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.exportDetail(); + }); + + test('ADMIT-MLS-012 详情搜索无匹配(mock反例)', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.detailSearchMock(); + }); + + test('ADMIT-MLS-013 已通过单据不确认通过', async ({ admitMaterialListPage }) => { + await admitMaterialListPage.open(); + await admitMaterialListPage.passedNotConfirm(); + }); + }); +}); diff --git a/tests/spd/admit-material.spec.ts b/tests/spd/admit-material.spec.ts new file mode 100644 index 0000000..89e2cc8 --- /dev/null +++ b/tests/spd/admit-material.spec.ts @@ -0,0 +1,122 @@ +import { test } from '../../fixtures'; + +/** + * 全量用例:映射 testcase/基础管理-准入管理-物料准入管理-UI自动化测试用例-01.csv + */ +test.describe('基础设置 · 准入管理', () => { + test.describe('物料准入管理 · 页面进入', () => { + test('ADMIT-MAT-001 菜单进入物料准入管理页', { tag: '@smoke' }, async ({ admitMaterialPage }) => { + await admitMaterialPage.openFromMenu(); + await admitMaterialPage.expectEntered(); + }); + }); + + test.describe('物料准入管理 · 筛选查询', () => { + test('ADMIT-MAT-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.query(); + await admitMaterialPage.expectListOrEmpty(); + }); + + test('ADMIT-MAT-003 产品名称筛选', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.filterByProductName(); + }); + + test('ADMIT-MAT-004 耗材类型筛选', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.filterByGoodsType(); + }); + + test('ADMIT-MAT-005 名称与耗材类型组合查询', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.filterByNameAndType(); + }); + + test('ADMIT-MAT-006 重置恢复默认', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.resetFilters(); + }); + + test('ADMIT-MAT-007 收起后再展开保留条件', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.collapseKeepsName(); + }); + + test('ADMIT-MAT-008 无匹配结果空态(mock反例)', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.mockEmptyName(); + }); + }); + + test.describe('物料准入管理 · 状态与列表', () => { + test('ADMIT-MAT-009 列表核心列完整', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.expectCoreColumns(); + }); + + test('ADMIT-MAT-010 切换待申请 Tab', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.switchPending(); + }); + + test('ADMIT-MAT-011 待申请 Tab 计数与分页总数一致', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.pendingCountMatches(); + }); + + test('ADMIT-MAT-012 未勾选时生成申请单不可用或提示', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.generateNeedsSelection(); + }); + }); + + test.describe('物料准入管理 · 详情与编辑', () => { + test('ADMIT-MAT-013 打开详情后返回', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.detailAndBack(); + }); + + test('ADMIT-MAT-014 详情关键字段不可直接改写', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.detailReadonly(); + }); + + test('ADMIT-MAT-015 待申请行编辑回填', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.editBackfill(); + }); + + test('ADMIT-MAT-016 编辑取消不保存', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.editCancel(); + }); + + test('ADMIT-MAT-017 编辑清空产品名称拦截(mock反例)', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.editClearName(); + }); + + test('ADMIT-MAT-018 已生效行不提供编辑', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.effectiveNoEdit(); + }); + }); + + test.describe('物料准入管理 · 关联与作废', () => { + test('ADMIT-MAT-019 关联产品取消不落库', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.relateCancel(); + }); + + test('ADMIT-MAT-020 作废确认后取消', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.voidCancel(); + }); + + test('ADMIT-MAT-021 打开操作日志后关闭', async ({ admitMaterialPage }) => { + await admitMaterialPage.open(); + await admitMaterialPage.openLog(); + }); + }); +}); diff --git a/tests/spd/admit-product-apply.spec.ts b/tests/spd/admit-product-apply.spec.ts new file mode 100644 index 0000000..14366f1 --- /dev/null +++ b/tests/spd/admit-product-apply.spec.ts @@ -0,0 +1,112 @@ +import { test } from '../../fixtures'; + +/** + * 全量用例:映射 testcase/基础管理-准入管理-产品准入申请-UI自动化测试用例-01.csv + */ +test.describe('基础设置 · 准入管理', () => { + test.describe('产品准入申请 · 页面进入', () => { + test('ADMIT-PAP-001 菜单进入产品准入申请页', { tag: '@smoke' }, async ({ admitProductApplyPage }) => { + await admitProductApplyPage.openFromMenu(); + await admitProductApplyPage.expectEntered(); + }); + }); + + test.describe('产品准入申请 · 筛选查询', () => { + test('ADMIT-PAP-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.query(); + await admitProductApplyPage.expectListOrEmpty(); + }); + + test('ADMIT-PAP-003 产品名称筛选', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.filterByProductName(); + }); + + test('ADMIT-PAP-004 名称与耗材类型组合查询', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.filterByNameAndType(); + }); + + test('ADMIT-PAP-005 重置恢复默认', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.resetFilters(); + }); + + test('ADMIT-PAP-006 无匹配结果空态(mock反例)', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.mockEmpty(); + }); + }); + + test.describe('产品准入申请 · 状态与列表', () => { + test('ADMIT-PAP-007 列表核心列完整', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.expectCoreColumns(); + }); + + test('ADMIT-PAP-008 切换待申请 Tab', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.switchPending(); + }); + + test('ADMIT-PAP-009 翻至第 2 页', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.goPage2(); + }); + + test('ADMIT-PAP-010 跳至指定页', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.jumpPage2(); + }); + }); + + test.describe('产品准入申请 · 新增与导入', () => { + test('ADMIT-PAP-011 打开新增产品后返回', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.openAddAndBack(); + }); + + test('ADMIT-PAP-012 新增取消不落库', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.addCancel(); + }); + + test('ADMIT-PAP-013 Excel 导入打开后取消', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.excelCancel(); + }); + + test('ADMIT-PAP-014 产品名称为空保存拦截(mock反例)', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.emptyNameBlocked(); + }); + }); + + test.describe('产品准入申请 · 详情、编辑与作废', () => { + test('ADMIT-PAP-015 打开详情后返回', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.detailAndBack(); + }); + + test('ADMIT-PAP-016 待申请编辑回填', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.editBackfill(); + }); + + test('ADMIT-PAP-017 编辑取消不保存', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.editCancel(); + }); + + test('ADMIT-PAP-018 作废确认后取消', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.voidCancel(); + }); + + test('ADMIT-PAP-019 已通过行不提供编辑', async ({ admitProductApplyPage }) => { + await admitProductApplyPage.open(); + await admitProductApplyPage.passedNoEdit(); + }); + }); +}); diff --git a/tests/spd/admit-product-list.spec.ts b/tests/spd/admit-product-list.spec.ts new file mode 100644 index 0000000..4209fa6 --- /dev/null +++ b/tests/spd/admit-product-list.spec.ts @@ -0,0 +1,82 @@ +import { test } from '../../fixtures'; + +/** + * 全量用例:映射 testcase/基础管理-准入管理-产品准入单列表-UI自动化测试用例-01.csv + */ +test.describe('基础设置 · 准入管理', () => { + test.describe('产品准入单列表 · 页面进入', () => { + test('ADMIT-PLS-001 菜单进入产品准入单列表', { tag: '@smoke' }, async ({ admitProductListPage }) => { + await admitProductListPage.openFromMenu(); + await admitProductListPage.expectEntered(); + }); + }); + + test.describe('产品准入单列表 · 筛选与状态', () => { + test('ADMIT-PLS-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.query(); + await admitProductListPage.expectListOrEmpty(); + }); + + test('ADMIT-PLS-003 按产品名称筛选', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.filterByProductOrFallback(); + }); + + test('ADMIT-PLS-004 重置恢复默认', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.resetFilters(); + }); + + test('ADMIT-PLS-005 无匹配结果空态(mock反例)', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.mockEmpty(); + }); + + test('ADMIT-PLS-006 列表核心列与附件计数', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.expectCoreColumns(); + }); + + test('ADMIT-PLS-007 切换已通过 Tab', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.switchPassed(); + }); + + test('ADMIT-PLS-008 与物料准入单列表不串 URL', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.expectNotMaterialUrl(); + }); + test('ADMIT-PLS-014 多条件组合查询(制单日期等6项)', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.filterCombination(); + }); + }); + + test.describe('产品准入单列表 · 单据详情', () => { + test('ADMIT-PLS-009 打开详情后返回', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.detailAndBack(); + }); + + test('ADMIT-PLS-010 详情产品条数与列表一致', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.detailCountMatches(); + }); + + test('ADMIT-PLS-011 单据日志可打开', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.openBillLog(); + }); + + test('ADMIT-PLS-012 详情搜索无匹配(mock反例)', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.detailSearchMock(); + }); + + test('ADMIT-PLS-013 已通过单据不确认通过', async ({ admitProductListPage }) => { + await admitProductListPage.open(); + await admitProductListPage.passedNotConfirm(); + }); + }); +}); diff --git a/tests/spd/admit-supply.spec.ts b/tests/spd/admit-supply.spec.ts new file mode 100644 index 0000000..aa3116a --- /dev/null +++ b/tests/spd/admit-supply.spec.ts @@ -0,0 +1,104 @@ +import { test } from '../../fixtures'; + +/** + * 全量用例:映射 testcase/基础管理-准入管理-供货目录申请-UI自动化测试用例-01.csv + */ +test.describe('基础设置 · 准入管理', () => { + test.describe('供货目录申请 · 页面进入', () => { + test('ADMIT-SUP-001 菜单进入供货目录申请页', { tag: '@smoke' }, async ({ admitSupplyPage }) => { + await admitSupplyPage.openFromMenu(); + await admitSupplyPage.expectEntered(); + }); + }); + + test.describe('供货目录申请 · 筛选与状态', () => { + test('ADMIT-SUP-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.query(); + await admitSupplyPage.expectListOrEmpty(); + }); + + test('ADMIT-SUP-003 按进院供应商筛选', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.filterBySupplier(); + }); + + test('ADMIT-SUP-004 重置恢复默认', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.resetFilters(); + }); + + test('ADMIT-SUP-005 变更单号无匹配空态(mock反例)', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.mockEmpty(); + }); + + test('ADMIT-SUP-006 列表核心列完整', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.expectCoreColumns(); + }); + + test('ADMIT-SUP-007 切换待提交 Tab 且计数一致', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.switchPending(); + }); + test('ADMIT-SUP-018 多条件组合查询(制单日期等6项)', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.filterCombination(); + }); + }); + + test.describe('供货目录申请 · 新增与编辑', () => { + test('ADMIT-SUP-008 打开新增供货目录', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.openAddFields(); + }); + + test('ADMIT-SUP-009 新增放弃不落库', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.addCancel(); + }); + + test('ADMIT-SUP-010 业务模式与是否默认只切换不提交', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.switchModeWithoutSubmit(); + }); + + test('ADMIT-SUP-011 待提交编辑回填', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.editBackfill(); + }); + + test('ADMIT-SUP-012 编辑取消不保存', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.editCancel(); + }); + + test('ADMIT-SUP-013 进院供应商为空不可提交(mock反例)', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.emptySupplierBlocked(); + }); + + test('ADMIT-SUP-014 未添加产品不可提交', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.noProductBlocked(); + }); + }); + + test.describe('供货目录申请 · 批量提交与详情', () => { + test('ADMIT-SUP-015 未勾选时批量提交不可用', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.batchNeedsSelection(); + }); + + test('ADMIT-SUP-016 勾选后批量提交取消', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.batchCancel(); + }); + + test('ADMIT-SUP-017 打开详情后返回', async ({ admitSupplyPage }) => { + await admitSupplyPage.open(); + await admitSupplyPage.detailAndBack(); + }); + }); +}); diff --git a/tests/spd/admit-task.spec.ts b/tests/spd/admit-task.spec.ts new file mode 100644 index 0000000..c2cc332 --- /dev/null +++ b/tests/spd/admit-task.spec.ts @@ -0,0 +1,89 @@ +import { test } from '../../fixtures'; + +/** + * 全量用例:映射 testcase/基础管理-准入管理-准入任务分配-UI自动化测试用例-01.csv + */ +test.describe('基础设置 · 准入管理', () => { + test.describe('准入任务分配 · 页面进入', () => { + test('ADMIT-TSK-001 菜单进入准入任务分配页', { tag: '@smoke' }, async ({ admitTaskPage }) => { + await admitTaskPage.openFromMenu(); + await admitTaskPage.expectEntered(); + }); + }); + + test.describe('准入任务分配 · 筛选与分类', () => { + test('ADMIT-TSK-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.query(); + await admitTaskPage.expectListOrEmpty(); + }); + + test('ADMIT-TSK-003 产品名称筛选', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.filterByProductName(); + }); + + test('ADMIT-TSK-004 待分类与已分类切换', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.switchTabs(); + }); + + test('ADMIT-TSK-005 重置恢复默认', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.resetFilters(); + }); + + test('ADMIT-TSK-006 无匹配结果空态(mock反例)', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.mockEmpty(); + }); + test('ADMIT-TSK-015 多条件组合查询(新增日期等11项)', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.filterCombination(); + }); + }); + + test.describe('准入任务分配 · 列表分页', () => { + test('ADMIT-TSK-007 列表核心列完整', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.expectCoreColumns(); + }); + + test('ADMIT-TSK-008 翻至第 2 页', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.goPage2(); + }); + + test('ADMIT-TSK-009 跳至指定页', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.jumpPage2(); + }); + + test('ADMIT-TSK-010 第 1 页上一页不可用', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.prevDisabled(); + }); + }); + + test.describe('准入任务分配 · 分配操作', () => { + test('ADMIT-TSK-011 单行分配类型取消', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.assignCancel(); + }); + + test('ADMIT-TSK-012 未勾选时批量分配不可用', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.batchNeedsSelection(); + }); + + test('ADMIT-TSK-013 勾选后批量分配取消', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.batchCancel(); + }); + + test('ADMIT-TSK-014 已分类行不再按待分类分配', async ({ admitTaskPage }) => { + await admitTaskPage.open(); + await admitTaskPage.classifiedNoAssign(); + }); + }); +}); diff --git a/tests/spd/basic-branch.spec.ts b/tests/spd/basic-branch.spec.ts new file mode 100644 index 0000000..fdfb868 --- /dev/null +++ b/tests/spd/basic-branch.spec.ts @@ -0,0 +1,113 @@ +import { test } from '../../fixtures'; +import { basicHospitalData, isBasicWriteAllowed } from '../../data/basic-hospital.data'; + +/** + * 全量用例:映射 testcase/基础管理-医院资料-院区管理-UI自动化测试用例-01.csv + * 运行:npm run test:basic-branch + */ +test.describe('基础设置 · 医院资料', () => { + test.describe('院区管理 · 页面进入', () => { + test('BASIC-BRN-001 菜单进入院区管理页', { tag: '@smoke' }, async ({ basicBranchPage }) => { + await basicBranchPage.openViaMenu(basicHospitalData.menuBranch, '/paltform/area'); + await basicBranchPage.expectEntered(); + }); + }); + + test.describe('院区管理 · 列表搜索', () => { + test('BASIC-BRN-002 默认列出院区或空态明确', { tag: '@smoke' }, async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.expectListOrEmpty(); + }); + + test('BASIC-BRN-003 院区名称搜索过滤', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + const sample = await basicBranchPage.extractSample(); + await basicBranchPage.searchByName(sample.sampleCampusName); + await basicBranchPage.expectNameResults(sample.sampleCampusName); + }); + + test('BASIC-BRN-004 列表核心列完整', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.expectCoreColumns(); + }); + + test('BASIC-BRN-005 清空搜索恢复全量', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + const sample = await basicBranchPage.extractSample(); + await basicBranchPage.searchByName(sample.sampleCampusName); + await basicBranchPage.clearSearch(); + }); + + test('BASIC-BRN-006 搜索无匹配空态(mock反例)', { tag: '@smoke' }, async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.searchByName(basicHospitalData.mockCampusName); + await basicBranchPage.expectMockEmpty(); + }); + }); + + test.describe('院区管理 · 新增院区', () => { + test('BASIC-BRN-007 打开新增院区弹窗', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.openAddDialog(); + await basicBranchPage.closeDialogIfOpen(); + }); + + test('BASIC-BRN-008 新增取消不落库', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.addAndCancel(); + }); + + test('BASIC-BRN-009 合法必填新增成功', async ({ basicBranchPage }) => { + test.skip(!isBasicWriteAllowed(), basicHospitalData.writeDisabledReason); + await basicBranchPage.open(); + await basicBranchPage.addSuccessAndCleanup(); + }); + + test('BASIC-BRN-010 院区名称为空拦截(mock反例)', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.addEmptyName(); + }); + + test('BASIC-BRN-011 所在地区为空拦截(mock反例)', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.addEmptyRegion(); + }); + + test('BASIC-BRN-012 详细地址为空拦截(mock反例)', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.addEmptyAddress(); + }); + }); + + test.describe('院区管理 · 编辑院区', () => { + test('BASIC-BRN-013 打开编辑弹窗并回填', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.openEditAndExpectBackfill(); + }); + + test('BASIC-BRN-014 编辑态院区编号不可换号', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.expectIdNotChangeable(); + }); + + test('BASIC-BRN-015 编辑取消不保存', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.editAndCancel(); + }); + + test('BASIC-BRN-016 编辑状态为禁用启用', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.expectEditStatusLabels(); + }); + + test('BASIC-BRN-017 编辑可见启用云仓', async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.expectCloudWarehouse(); + }); + + test('BASIC-BRN-018 编辑清空必填拦截(mock反例)', { tag: '@smoke' }, async ({ basicBranchPage }) => { + await basicBranchPage.open(); + await basicBranchPage.editClearRequired(); + }); + }); +}); diff --git a/tests/spd/basic-finance-month.spec.ts b/tests/spd/basic-finance-month.spec.ts new file mode 100644 index 0000000..3bc1a85 --- /dev/null +++ b/tests/spd/basic-finance-month.spec.ts @@ -0,0 +1,99 @@ +import { test } from '../../fixtures'; +import { basicHospitalData, isBasicWriteAllowed } from '../../data/basic-hospital.data'; + +/** + * 全量用例:映射 testcase/基础管理-医院资料-财务月份设置-UI自动化测试用例-01.csv + * 运行:npm run test:basic-finance-month + */ +test.describe('基础设置 · 医院资料', () => { + test.describe('财务月份设置 · 页面进入', () => { + test('BASIC-FMN-001 菜单进入财务月份设置页', { tag: '@smoke' }, async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.openViaMenu(basicHospitalData.menuFinanceMonth, '/balance/month'); + await basicFinanceMonthPage.expectEntered(); + }); + }); + + test.describe('财务月份设置 · 筛选查询', () => { + test('BASIC-FMN-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + await basicFinanceMonthPage.search(); + await basicFinanceMonthPage.expectListOrEmpty(); + }); + + test('BASIC-FMN-003 按财务月份筛选', async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + const sample = await basicFinanceMonthPage.extractSample(); + await basicFinanceMonthPage.queryByMonth(sample.sampleFinanceMonth); + await basicFinanceMonthPage.expectMonthResults(sample.sampleFinanceMonth); + }); + + test('BASIC-FMN-004 重置恢复默认', async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + const sample = await basicFinanceMonthPage.extractSample(); + await basicFinanceMonthPage.queryByMonth(sample.sampleFinanceMonth); + await basicFinanceMonthPage.resetFilters(); + }); + }); + + test.describe('财务月份设置 · 列表与状态', () => { + test('BASIC-FMN-005 列表核心列完整', async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + await basicFinanceMonthPage.search(); + await basicFinanceMonthPage.expectCoreColumns(); + }); + + test('BASIC-FMN-006 仅当前月份行可修改', async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + await basicFinanceMonthPage.search(); + await basicFinanceMonthPage.expectOnlyCurrentEditable(); + }); + + test('BASIC-FMN-007 当前月份至多一行', async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + await basicFinanceMonthPage.search(); + await basicFinanceMonthPage.expectAtMostOneCurrent(); + }); + + test('BASIC-FMN-008 历史月份无法进入编辑', async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + await basicFinanceMonthPage.search(); + await basicFinanceMonthPage.expectHistoryNotEditable(); + }); + }); + + test.describe('财务月份设置 · 当前月份修改', () => { + test('BASIC-FMN-009 修改进入行内编辑', async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + await basicFinanceMonthPage.search(); + await basicFinanceMonthPage.enterInlineEdit(); + await basicFinanceMonthPage.locators.cancelBtn.click(); + }); + + test('BASIC-FMN-010 编辑态开始结束日期只读', async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + await basicFinanceMonthPage.search(); + await basicFinanceMonthPage.expectStartEndReadonly(); + }); + + test('BASIC-FMN-011 取消不保存自动结算日', async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + await basicFinanceMonthPage.cancelWithoutSave(); + }); + + test('BASIC-FMN-012 保存自动结算日期持久化', async ({ basicFinanceMonthPage }) => { + test.skip(!isBasicWriteAllowed(), basicHospitalData.writeDisabledReason); + await basicFinanceMonthPage.open(); + await basicFinanceMonthPage.saveAndRestore(); + }); + + test('BASIC-FMN-013 自动结算日为空拦截(mock反例)', { tag: '@smoke' }, async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + await basicFinanceMonthPage.saveEmptyDate(); + }); + + test('BASIC-FMN-014 自动结算日超出当月区间拦截(mock反例)', async ({ basicFinanceMonthPage }) => { + await basicFinanceMonthPage.open(); + await basicFinanceMonthPage.saveOutOfRange(); + }); + }); +}); diff --git a/tests/spd/basic-hospital-info.spec.ts b/tests/spd/basic-hospital-info.spec.ts new file mode 100644 index 0000000..9b5deb9 --- /dev/null +++ b/tests/spd/basic-hospital-info.spec.ts @@ -0,0 +1,74 @@ +import { test } from '../../fixtures'; +import { basicHospitalData, isBasicWriteAllowed } from '../../data/basic-hospital.data'; + +/** + * 全量用例:映射 testcase/基础管理-医院资料-医院信息登记-UI自动化测试用例-01.csv + * 运行:npm run test:basic-hospital-info + */ +test.describe('基础设置 · 医院资料', () => { + test.describe('医院信息登记 · 页面进入与查看', () => { + test('BASIC-HOS-001 菜单进入医院信息登记页', { tag: '@smoke' }, async ({ basicHospitalInfoPage }) => { + await basicHospitalInfoPage.openViaMenu(basicHospitalData.menuHospitalInfo, '/paltform/info'); + await basicHospitalInfoPage.expectEntered(); + }); + + test('BASIC-HOS-002 查看态只读字段不可改', { tag: '@smoke' }, async ({ basicHospitalInfoPage }) => { + await basicHospitalInfoPage.open(); + await basicHospitalInfoPage.expectViewReadonly(); + }); + + test('BASIC-HOS-003 已上传图片可预览', async ({ basicHospitalInfoPage }) => { + await basicHospitalInfoPage.open(); + await basicHospitalInfoPage.previewIfPresent(); + }); + }); + + test.describe('医院信息登记 · 编辑保存', () => { + test('BASIC-HOS-004 进入编辑模式', async ({ basicHospitalInfoPage }) => { + await basicHospitalInfoPage.open(); + await basicHospitalInfoPage.enterEdit(); + await basicHospitalInfoPage.expectSystemFieldsReadonly(); + await basicHospitalInfoPage.locators.cancelBtn.click(); + }); + + test('BASIC-HOS-005 编辑态只读字段仍不可改', async ({ basicHospitalInfoPage }) => { + await basicHospitalInfoPage.open(); + await basicHospitalInfoPage.enterEdit(); + await basicHospitalInfoPage.expectSystemFieldsReadonly(); + await basicHospitalInfoPage.locators.cancelBtn.click(); + }); + + test('BASIC-HOS-006 取消不保存', async ({ basicHospitalInfoPage }) => { + await basicHospitalInfoPage.open(); + await basicHospitalInfoPage.cancelWithoutSave(); + }); + + test('BASIC-HOS-007 保存后字段持久化', async ({ basicHospitalInfoPage }) => { + test.skip(!isBasicWriteAllowed(), basicHospitalData.writeDisabledReason); + await basicHospitalInfoPage.open(); + await basicHospitalInfoPage.saveAndRestore(); + }); + + test('BASIC-HOS-008 联系人为空保存拦截(mock反例)', { tag: '@smoke' }, async ({ basicHospitalInfoPage }) => { + await basicHospitalInfoPage.open(); + await basicHospitalInfoPage.saveEmptyField('linkman'); + }); + + test('BASIC-HOS-009 联系方式为空保存拦截(mock反例)', async ({ basicHospitalInfoPage }) => { + await basicHospitalInfoPage.open(); + await basicHospitalInfoPage.saveEmptyField('contactWay'); + }); + + test('BASIC-HOS-010 医院简称为空保存拦截(mock反例)', async ({ basicHospitalInfoPage }) => { + await basicHospitalInfoPage.open(); + await basicHospitalInfoPage.saveEmptyField('shortName'); + }); + }); + + test.describe('医院信息登记 · 图片上传', () => { + test('BASIC-HOS-011 编辑态存在背景与 logo 上传控件', async ({ basicHospitalInfoPage }) => { + await basicHospitalInfoPage.open(); + await basicHospitalInfoPage.expectUploadInputs(); + }); + }); +}); diff --git a/tests/spd/basic-notice.spec.ts b/tests/spd/basic-notice.spec.ts new file mode 100644 index 0000000..27ff733 --- /dev/null +++ b/tests/spd/basic-notice.spec.ts @@ -0,0 +1,124 @@ +import { test } from '../../fixtures'; +import { basicHospitalData, isBasicWriteAllowed } from '../../data/basic-hospital.data'; + +/** + * 全量用例:映射 testcase/基础管理-医院资料-公告管理-UI自动化测试用例-01.csv + * 运行:npm run test:basic-notice + */ +test.describe('基础设置 · 医院资料', () => { + test.describe('公告管理 · 页面进入', () => { + test('BASIC-NTC-001 菜单进入公告管理页', { tag: '@smoke' }, async ({ basicNoticePage }) => { + await basicNoticePage.openViaMenu(basicHospitalData.menuNotice, '/paltform/notice'); + await basicNoticePage.expectEntered(); + }); + }); + + test.describe('公告管理 · 筛选查询', () => { + test('BASIC-NTC-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.search(); + await basicNoticePage.expectListOrEmpty(); + }); + + test('BASIC-NTC-003 公告标题模糊查询', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + const sample = await basicNoticePage.extractSample(); + await basicNoticePage.queryByTitle(sample.sampleNoticeTitle); + await basicNoticePage.expectTitleResultsMatch(sample.sampleNoticeTitle); + }); + + test('BASIC-NTC-004 发布范围下拉查询', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + const sample = await basicNoticePage.extractSample(); + await basicNoticePage.queryByScope(sample.samplePublishScope); + await basicNoticePage.expectScopeResultsMatch(sample.samplePublishScope); + }); + + test('BASIC-NTC-005 标题与范围组合查询', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + const sample = await basicNoticePage.extractSample(); + await basicNoticePage.queryByTitleAndScope(sample.sampleNoticeTitle, sample.samplePublishScope); + await basicNoticePage.expectTitleResultsMatch(sample.sampleNoticeTitle); + await basicNoticePage.expectScopeResultsMatch(sample.samplePublishScope); + }); + + test('BASIC-NTC-006 快捷区重置恢复默认', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + const sample = await basicNoticePage.extractSample(); + await basicNoticePage.queryByTitle(sample.sampleNoticeTitle); + await basicNoticePage.resetFilters(); + }); + + test('BASIC-NTC-007 无匹配结果空态(mock反例)', { tag: '@smoke' }, async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.queryByTitle(basicHospitalData.mockNoticeTitle); + await basicNoticePage.expectMockEmpty(); + }); + }); + + test.describe('公告管理 · 列表分页', () => { + test('BASIC-NTC-008 查询后核心列展示', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.search(); + await basicNoticePage.expectCoreColumns(); + }); + + test('BASIC-NTC-009 分页翻至第 2 页', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.search(); + await basicNoticePage.goToPage2(); + }); + + test('BASIC-NTC-010 跳至指定页', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.search(); + await basicNoticePage.jumpToPage(2); + }); + }); + + test.describe('公告管理 · 新增公告', () => { + test('BASIC-NTC-011 打开新增公告弹窗', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.openAddDialog(); + await basicNoticePage.closeDialogIfOpen(); + }); + + test('BASIC-NTC-012 新增取消不落库', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.addAndCancel(`AUTO-NTC-${Date.now()}`); + }); + + test('BASIC-NTC-013 合法必填发布成功', async ({ basicNoticePage }) => { + test.skip(!isBasicWriteAllowed(), basicHospitalData.writeDisabledReason); + await basicNoticePage.open(); + await basicNoticePage.publishAndCleanup(); + }); + + test('BASIC-NTC-014 标题为空发布拦截(mock反例)', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.publishWithEmptyTitle(); + }); + + test('BASIC-NTC-015 内容为空发布拦截(mock反例)', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.publishWithEmptyContent(); + }); + }); + + test.describe('公告管理 · 编辑与删除', () => { + test('BASIC-NTC-016 打开编辑公告并回填', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.openEditAndExpectBackfill(); + }); + + test('BASIC-NTC-017 编辑取消不保存', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.editAndCancel(); + }); + + test('BASIC-NTC-018 删除弹出确认框', async ({ basicNoticePage }) => { + await basicNoticePage.open(); + await basicNoticePage.deleteConfirmAndCancel(); + }); + }); +}); diff --git a/tests/spd/basic-role.spec.ts b/tests/spd/basic-role.spec.ts new file mode 100644 index 0000000..01e87cc --- /dev/null +++ b/tests/spd/basic-role.spec.ts @@ -0,0 +1,193 @@ +import { expect, test } from '../../fixtures'; +import { userData } from '../../data/user.data'; + +/** + * 全量用例:与 testcase/基础管理-用户管理-角色管理-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:basic-role:all + */ +test.describe('基础设置 · 用户管理', () => { + test.describe('角色管理 · 页面进入', () => { + test('USER-ROL-001 菜单进入角色管理页', { tag: '@smoke' }, async ({ basicRolePage }) => { + await basicRolePage.openFromMenu(); + expect(basicRolePage.browserPage.url(), 'URL 应含角色管理 path').toContain('/basic/user/platform/role'); + expect(basicRolePage.browserPage.url(), '不应进入用户管理').not.toContain('/platform/person'); + await expect(basicRolePage.locators.saveBtn, '保 存').toBeVisible(); + await expect(basicRolePage.locators.refreshBtn, '刷 新').toBeVisible(); + await expect(basicRolePage.locators.roleTree, '右侧权限树').toBeVisible(); + expect((await basicRolePage.leftRoleNames()).length, '左侧角色列表').toBeGreaterThan(0); + await expect(basicRolePage.locators.paginationTotal, '无独立分页').toBeHidden(); + }); + + test.describe('未登录', () => { + test.use({ storageState: { cookies: [], origins: [] } }); + + test('USER-ROL-002 未登录访问拦截', async ({ basicRolePage }) => { + const probe = await basicRolePage.probeUnauthAccess(); + expect(probe.onLogin || !probe.hasBusinessRow, '未登录不可见角色配置').toBeTruthy(); + }); + }); + }); + + test.describe('角色管理 · 左侧角色列表', () => { + test('USER-ROL-003 角色列表加载并可点选', { tag: '@smoke' }, async ({ basicRolePage }) => { + await basicRolePage.open(); + const name = await basicRolePage.sampleRoleName(); + await basicRolePage.selectRole(name); + expect(await basicRolePage.isRoleSelected(name), '该行应处于选中态').toBeTruthy(); + }); + + test('USER-ROL-004 角色名称搜索过滤', async ({ basicRolePage }) => { + await basicRolePage.open(); + const name = await basicRolePage.sampleRoleName(); + const keyword = name.slice(0, Math.min(2, name.length)); + await basicRolePage.searchRole(keyword); + const names = await basicRolePage.leftRoleNames(); + expect(names.length, '搜索后应有匹配项').toBeGreaterThan(0); + expect( + names.every((item) => item.includes(keyword)), + '搜索结果角色名包含关键字', + ).toBeTruthy(); + }); + + test('USER-ROL-005 选中角色刷新右侧权限树', async ({ basicRolePage }) => { + await basicRolePage.open(); + const names = await basicRolePage.leftRoleNames(); + if (names.length < 2) { + await basicRolePage.ensureHealthyBeforeSkip('数据不足:左栏不足 2 个角色'); + test.skip(true, '数据不足:左栏不足 2 个角色'); + } + await basicRolePage.selectRole(names[0]); + await expect(basicRolePage.locators.roleTree, '权限树').toBeVisible(); + await basicRolePage.checkedSignature(); + await basicRolePage.selectRole(names[1]); + await expect(basicRolePage.locators.roleTree, '切换后权限树仍可见').toBeVisible(); + expect(await basicRolePage.isRoleSelected(names[1]), '当前角色应选中').toBeTruthy(); + }); + + test('USER-ROL-006 清空搜索恢复全量', async ({ basicRolePage }) => { + await basicRolePage.open(); + const before = (await basicRolePage.leftRoleNames()).length; + const name = await basicRolePage.sampleRoleName(); + await basicRolePage.searchRole(name.slice(0, Math.min(2, name.length))); + await basicRolePage.clearRoleSearch(); + expect((await basicRolePage.leftRoleNames()).length, '清空后应恢复全量').toBeGreaterThanOrEqual(before); + }); + + test('USER-ROL-007 搜索无匹配空态(mock反例)', { tag: '@smoke' }, async ({ basicRolePage }) => { + await basicRolePage.open(); + await basicRolePage.searchRole(userData.mockRole); + const names = await basicRolePage.leftRoleNames(); + const empty = await basicRolePage.locators.empty.isVisible().catch(() => false); + expect(names.length === 0 || empty, '无匹配时空态明确').toBeTruthy(); + }); + + test('USER-ROL-014 角色名与用户管理角色下拉同源', async ({ basicRolePage, basicUserPage }) => { + await basicRolePage.open(); + const roleNames = (await basicRolePage.leftRoleNames()).filter((item) => item && item !== '角色'); + expect(roleNames.length, '左栏应有角色').toBeGreaterThan(0); + await basicUserPage.open(); + const options = await basicUserPage.collectRoleFilterOptions(); + if (options.length === 0) { + await basicUserPage.ensureHealthyBeforeSkip('数据不足:用户管理角色下拉为空'); + test.skip(true, '数据不足:用户管理角色下拉为空'); + } + const sample = roleNames.slice(0, 8); + for (const name of sample) { + const compact = name.replace(/\s/g, '').replace(/[((].*$/, ''); + const hit = options.some((item) => item.includes(compact) || compact.includes(item)); + expect(hit, `角色「${name}」应能在用户页下拉对应到`).toBeTruthy(); + } + }); + }); + + test.describe('角色管理 · 右侧菜单权限树', () => { + test('USER-ROL-008 权限树按应用与模块展开', async ({ basicRolePage }) => { + await basicRolePage.open(); + const name = await basicRolePage.sampleRoleName(); + await basicRolePage.selectRole(name); + await expect(basicRolePage.browserPage.getByText('SPD内网系统PC端').first(), '树根').toBeVisible(); + await expect(basicRolePage.locators.treeNode('基础设置').or(basicRolePage.browserPage.getByText('基础设置').first()), '基础设置').toBeVisible(); + await expect(basicRolePage.locators.treeNode('厂商管理').or(basicRolePage.browserPage.getByText('厂商管理').first()), '厂商管理').toBeVisible(); + await basicRolePage.expandTreeNode('基础设置'); + await expect( + basicRolePage.locators.treeNode('医院资料').or(basicRolePage.browserPage.getByText('医院资料').first()), + '基础设置子菜单', + ).toBeVisible(); + }); + + test('USER-ROL-009 勾选父节点联动子节点', { tag: '@smoke' }, async ({ basicRolePage }) => { + await basicRolePage.open(); + const name = await basicRolePage.sampleRoleName(); + await basicRolePage.selectRole(name); + await basicRolePage.expandTreeNode('厂商管理'); + await basicRolePage.checkModule('厂商管理'); + expect(await basicRolePage.isTreeChecked('厂商管理'), '父节点应勾选').toBeTruthy(); + const child = basicRolePage.locators.treeNode('厂商查询'); + if (await child.isVisible().catch(() => false)) { + expect(await basicRolePage.isTreeChecked('厂商查询'), '子节点应联动勾选').toBeTruthy(); + } + await basicRolePage.refreshTree(); + }); + + test('USER-ROL-010 刷新恢复未保存勾选', async ({ basicRolePage }) => { + await basicRolePage.open(); + const name = await basicRolePage.sampleRoleName(); + await basicRolePage.selectRole(name); + const before = await basicRolePage.checkedSignature(); + await basicRolePage.checkModule('厂商管理'); + await basicRolePage.refreshTree(); + const after = await basicRolePage.checkedSignature(); + expect(after, '刷新应丢弃未保存勾选').toBe(before); + }); + }); + + test.describe('角色管理 · 角色增删改', () => { + test('USER-ROL-011 删除需确认且取消保留', async ({ basicRolePage }) => { + await basicRolePage.open(); + const opened = await basicRolePage.openDeleteConfirm(); + expect(opened, '应出现删除确认').toBeTruthy(); + await expect(opened!.confirm.getByRole('button', { name: /取\s*消|关\s*闭/ }).first(), '取消').toBeVisible(); + await expect(opened!.confirm.getByRole('button', { name: /确\s*定|删/ }).first(), '确定').toBeVisible(); + const cancel = opened!.confirm.getByRole('button', { name: /取\s*消|关\s*闭/ }).first(); + await cancel.click(); + const names = await basicRolePage.leftRoleNames(); + expect(names.some((item) => item.includes(opened!.roleName)), '取消后角色仍在').toBeTruthy(); + }); + + test('USER-ROL-012 新增角色入口打开后取消', async ({ basicRolePage }) => { + await basicRolePage.open(); + const { before, opened } = await basicRolePage.openAddRoleForm(); + expect(opened, '新增表单应打开').toBeTruthy(); + await basicRolePage.cancelAddRole(); + expect((await basicRolePage.leftRoleNames()).length, '取消后角色数不增加').toBe(before); + }); + + test('USER-ROL-013 预置角色不可删除', async ({ basicRolePage }) => { + await basicRolePage.open(); + const names = await basicRolePage.leftRoleNames(); + const preset = names.find((item) => /医院管理员/.test(item)); + if (!preset) { + await basicRolePage.ensureHealthyBeforeSkip('数据不足:左栏无医院管理员等预置角色'); + test.skip(true, '数据不足:左栏无医院管理员等预置角色'); + } + await basicRolePage.selectRole(preset!); + const del = await basicRolePage.deleteEntry(preset!); + const visible = await del.isVisible().catch(() => false); + if (!visible) { + expect(visible, '预置角色删除入口不可见').toBeFalsy(); + return; + } + await del.click(); + const confirm = basicRolePage.browserPage + .locator('.ant-modal-confirm, .ant-popconfirm, .ant-modal') + .filter({ hasText: /提示|删除|确认/ }) + .last(); + if (await confirm.isVisible().catch(() => false)) { + const cancel = confirm.getByRole('button', { name: /取\s*消|关\s*闭/ }).first(); + if (await cancel.isVisible().catch(() => false)) await cancel.click(); + else await basicRolePage.browserPage.keyboard.press('Escape'); + } + expect((await basicRolePage.leftRoleNames()).some((item) => item.includes(preset!)), '预置角色仍在').toBeTruthy(); + }); + }); +}); diff --git a/tests/spd/basic-user-union.spec.ts b/tests/spd/basic-user-union.spec.ts new file mode 100644 index 0000000..cefd3eb --- /dev/null +++ b/tests/spd/basic-user-union.spec.ts @@ -0,0 +1,189 @@ +import { expect, test } from '../../fixtures'; +import { assertHeaderOrder, assertListOrEmpty, assertMockEmpty } from './helpers/user-assert.helper'; + +/** + * 全量用例:与 testcase/基础管理-用户管理-医共体用户管理-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:basic-user-union:all + */ +test.describe('基础设置 · 用户管理', () => { + test.describe('医共体用户管理 · 页面进入', () => { + test('USER-UNI-001 菜单进入医共体用户管理页', { tag: '@smoke' }, async ({ basicUserUnionPage }) => { + await basicUserUnionPage.openFromMenu(); + expect(basicUserUnionPage.browserPage.url(), 'URL 应含医共体 path').toContain('/basic/user/platform/personCom'); + expect(basicUserUnionPage.browserPage.url(), '不应进入角色管理').not.toContain('/platform/role'); + await expect(basicUserUnionPage.locators.formSelect('所属医院'), '所属医院筛选').toBeVisible(); + await expect(basicUserUnionPage.button('新增').first(), '工具栏新增').toBeVisible(); + expect((await basicUserUnionPage.headerTexts()).length, '列表列头').toBeGreaterThan(0); + await expect( + basicUserUnionPage.locators.paginationTotal.or(basicUserUnionPage.browserPage.locator('.ant-pagination')).first(), + '分页', + ).toBeVisible(); + }); + + test.describe('未登录', () => { + test.use({ storageState: { cookies: [], origins: [] } }); + + test('USER-UNI-002 未登录访问拦截', async ({ basicUserUnionPage }) => { + const probe = await basicUserUnionPage.probeUnauthAccess(); + expect(probe.onLogin || !probe.hasBusinessRow, '未登录不可见医共体用户数据').toBeTruthy(); + }); + }); + }); + + test.describe('医共体用户管理 · 筛选与查询', () => { + test('USER-UNI-003 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + await basicUserUnionPage.query(); + await assertListOrEmpty(await basicUserUnionPage.snapshotList(), '默认查询'); + }); + + test('USER-UNI-004 姓名或登录账号关键字查询', async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + const sample = await basicUserUnionPage.extractUnionSample(); + const keyword = sample.sampleUserName || sample.sampleLoginName; + await basicUserUnionPage.queryByName(keyword); + const nameOk = await basicUserUnionPage.rowsMatchKeyword('姓名', keyword); + const loginOk = await basicUserUnionPage.rowsMatchKeyword('登录账号', keyword); + expect(nameOk || loginOk, '结果行姓名或登录账号应匹配关键字').toBeTruthy(); + }); + + test('USER-UNI-005 所属医院筛选', { tag: '@smoke' }, async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + await basicUserUnionPage.query(); + const switched = await basicUserUnionPage.switchHospitalIfPossible(); + expect(switched, '应切换到另一医院').toBeTruthy(); + expect( + await basicUserUnionPage.rowsMatchKeyword('所属医院', switched!.to), + '结果行所属医院应与所选一致', + ).toBeTruthy(); + }); + + test('USER-UNI-006 姓名+医院+联系方式组合查询', async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + const sample = await basicUserUnionPage.extractUnionSample(); + await basicUserUnionPage.queryComboUnion(sample); + await assertListOrEmpty(await basicUserUnionPage.snapshotList(), '组合查询'); + if (sample.sampleUserName) { + const nameOk = await basicUserUnionPage.rowsMatchKeyword('姓名', sample.sampleUserName); + const loginOk = await basicUserUnionPage.rowsMatchKeyword('登录账号', sample.sampleLoginName); + expect(nameOk || loginOk || (await basicUserUnionPage.getDataRowCount()) === 0, '组合条件应生效').toBeTruthy(); + } + }); + + test('USER-UNI-007 重置恢复默认医院', async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + const defHospital = await basicUserUnionPage.currentHospitalLabel(); + await basicUserUnionPage.fillName('x'); + await basicUserUnionPage.reset(); + expect(await basicUserUnionPage.currentHospitalLabel(), '所属医院应恢复默认').toBe(defHospital); + await expect(basicUserUnionPage.locators.nameInput).toHaveValue(''); + }); + + test('USER-UNI-008 展开高级筛选', async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + await basicUserUnionPage.expandAdvanced(); + await expect( + basicUserUnionPage.locators.formSelect('所属医院').or(basicUserUnionPage.browserPage.getByText('所属医院').first()), + '所属医院', + ).toBeVisible(); + await expect(basicUserUnionPage.browserPage.getByText('联系方式').first(), '联系方式').toBeVisible(); + await expect(basicUserUnionPage.browserPage.getByText('科室').first(), '科室').toBeVisible(); + await expect(basicUserUnionPage.browserPage.getByText('角色').first(), '角色').toBeVisible(); + await expect(basicUserUnionPage.button('筛选').first(), '筛 选').toBeVisible(); + }); + + test('USER-UNI-009 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + await basicUserUnionPage.queryMockEmpty(); + await assertMockEmpty(await basicUserUnionPage.snapshotList()); + }); + }); + + test.describe('医共体用户管理 · 列表与分页', () => { + test('USER-UNI-010 列表列顺序含所属医院', async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + await basicUserUnionPage.query(); + assertHeaderOrder(await basicUserUnionPage.headerTexts(), [ + '登录账号', + '姓名', + '联系方式', + '所属医院', + '所属科室', + '医院员工编号', + '角色', + '科室任职数', + '操作', + ]); + }); + + test('USER-UNI-011 分页翻至第 2 页', async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + await basicUserUnionPage.query(); + const hospital = await basicUserUnionPage.currentHospitalLabel(); + const { firstLogin, secondLogin, active } = await basicUserUnionPage.goToPage2(); + expect(active, '页码高亮为 2').toBe('2'); + expect(secondLogin, '第 2 页首行应更换').not.toBe(firstLogin); + expect(await basicUserUnionPage.currentHospitalLabel(), '翻页后医院筛选应保持').toBe(hospital); + }); + + test('USER-UNI-012 与用户管理页同一用户字段一致', async ({ basicUserUnionPage, basicUserPage }) => { + await basicUserUnionPage.open(); + const sample = await basicUserUnionPage.extractUnionSample(); + expect(sample.sampleHospital, '本页应有所属医院').toBeTruthy(); + await basicUserPage.open(); + await basicUserPage.queryByName(sample.sampleLoginName); + const snap = await basicUserPage.snapshotList(); + if (snap.rows === 0) { + await basicUserPage.ensureHealthyBeforeSkip('数据不足:用户管理页查不到同一账号'); + test.skip(true, '数据不足:用户管理页查不到同一账号'); + } + const row = await basicUserPage.firstRow(); + expect(await basicUserPage.cellText(row, '姓名'), '姓名跨页一致').toContain(sample.sampleUserName.slice(0, 2)); + if (sample.sampleDept) { + const dept = await basicUserPage.tryCell(row, '所属科室'); + expect(dept.replace(/\s/g, ''), '所属科室跨页一致').toContain(sample.sampleDept.replace(/\s/g, '').slice(0, 4)); + } + const headers = await basicUserPage.headerTexts(); + expect(headers.includes('所属医院'), '用户管理页仅无所属医院').toBeFalsy(); + }); + }); + + test.describe('医共体用户管理 · 新增与编辑', () => { + test('USER-UNI-013 打开新增弹窗', async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + const dialog = await basicUserUnionPage.openAddDialog(); + await expect(dialog.getByText(/账号|姓名|联系方式/).first()).toBeVisible(); + await expect(dialog.getByRole('button', { name: /取\s*消/ })).toBeVisible(); + await expect(dialog.getByRole('button', { name: /确\s*定/ })).toBeVisible(); + await basicUserUnionPage.closeDialog(); + }); + + test('USER-UNI-014 新增取消不落库', async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + const login = await basicUserUnionPage.addAndCancelOnHospital(); + await assertMockEmpty(await basicUserUnionPage.snapshotList(), login); + }); + + test('USER-UNI-015 必填为空拦截', async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + await basicUserUnionPage.submitEmptyRequired(); + await basicUserUnionPage.expectBlockedAndStay(); + await basicUserUnionPage.closeDialog(); + }); + + test('USER-UNI-016 打开编辑并回填', async ({ basicUserUnionPage }) => { + await basicUserUnionPage.open(); + await basicUserUnionPage.query(); + const { login, name } = await basicUserUnionPage.openEdit(); + const dialog = basicUserUnionPage.dialog(); + const codeVal = (await basicUserUnionPage.locators.dialogUserCode().inputValue().catch(() => '')) || ''; + const nameVal = (await basicUserUnionPage.locators.dialogName().inputValue().catch(() => '')) || ''; + expect( + codeVal.replace(/\s/g, '') === login.replace(/\s/g, '') || nameVal.includes(name.slice(0, 2)), + '回填应与列表行一致', + ).toBeTruthy(); + await expect(dialog.getByText(/所属医院/).first().or(dialog), '弹窗可见').toBeVisible(); + await basicUserUnionPage.closeDialog(); + }); + }); +}); diff --git a/tests/spd/basic-user.spec.ts b/tests/spd/basic-user.spec.ts new file mode 100644 index 0000000..8f80383 --- /dev/null +++ b/tests/spd/basic-user.spec.ts @@ -0,0 +1,251 @@ +import { expect, test } from '../../fixtures'; +import { assertHeadersContain, assertListOrEmpty, assertMockEmpty } from './helpers/user-assert.helper'; + +/** + * 全量用例:与 testcase/基础管理-用户管理-用户管理-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:basic-user:all + */ +test.describe('基础设置 · 用户管理', () => { + test.describe('用户管理 · 页面进入', () => { + test('USER-USR-001 菜单进入用户管理页', { tag: '@smoke' }, async ({ basicUserPage }) => { + await basicUserPage.openFromMenu(); + expect(basicUserPage.browserPage.url(), 'URL 应含用户管理 path').toContain('/basic/user/platform/person'); + expect(basicUserPage.browserPage.url(), '不应进入医共体').not.toContain('personCom'); + expect(basicUserPage.browserPage.url(), '不应进入角色管理').not.toContain('/platform/role'); + await expect(basicUserPage.locators.nameInput, '姓名筛选').toBeVisible(); + await expect(basicUserPage.button('新增').first(), '新 增').toBeVisible(); + await expect(basicUserPage.locators.excelImport, 'Excel导入').toBeVisible(); + expect((await basicUserPage.headerTexts()).length, '列表列头').toBeGreaterThan(0); + await expect(basicUserPage.locators.paginationTotal.or(basicUserPage.browserPage.locator('.ant-pagination')).first(), '分页').toBeVisible(); + }); + + test.describe('未登录', () => { + test.use({ storageState: { cookies: [], origins: [] } }); + + test('USER-USR-002 未登录访问拦截', async ({ basicUserPage }) => { + const probe = await basicUserPage.probeUnauthAccess(); + expect(probe.onLogin || !probe.hasBusinessRow, '未登录不可见用户数据').toBeTruthy(); + }); + }); + }); + + test.describe('用户管理 · 筛选与查询', () => { + test('USER-USR-003 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.query(); + await assertListOrEmpty(await basicUserPage.snapshotList(), '默认查询'); + }); + + test('USER-USR-004 姓名或登录账号关键字查询', async ({ basicUserPage }) => { + await basicUserPage.open(); + const sample = await basicUserPage.extractSample(); + const keyword = sample.sampleUserName || sample.sampleLoginName; + await basicUserPage.queryByName(keyword); + const nameOk = await basicUserPage.rowsMatchKeyword('姓名', keyword); + const loginOk = await basicUserPage.rowsMatchKeyword('登录账号', keyword); + expect(nameOk || loginOk, '结果行姓名或登录账号应匹配关键字').toBeTruthy(); + }); + + test('USER-USR-005 科室下拉筛选', async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.query(); + const dept = await basicUserPage.sampleCell('所属科室'); + await basicUserPage.queryByDept(dept); + expect(await basicUserPage.rowsMatchKeyword('所属科室', dept), '所属科室应与筛选一致').toBeTruthy(); + const rows = await basicUserPage.listRows(); + const limit = Math.min(await rows.count(), 5); + for (let i = 0; i < limit; i += 1) { + const text = (await basicUserPage.cellText(rows.nth(i), '所属科室')).replace(/\s/g, ''); + expect(text === '--' || text === '-', '筛选后不应含未分配科室').toBeFalsy(); + } + }); + + test('USER-USR-006 姓名+科室+角色组合查询', async ({ basicUserPage }) => { + await basicUserPage.open(); + const sample = await basicUserPage.extractSample(); + await basicUserPage.queryCombo(sample); + await assertListOrEmpty(await basicUserPage.snapshotList(), '组合查询'); + }); + + test('USER-USR-007 重置恢复默认', async ({ basicUserPage }) => { + await basicUserPage.open(); + const sample = await basicUserPage.extractSample(); + await basicUserPage.queryByName(sample.sampleUserName); + await basicUserPage.reset(); + await expect(basicUserPage.locators.nameInput).toHaveValue(''); + }); + + test('USER-USR-008 展开高级筛选', async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.expandAdvanced(); + await expect(basicUserPage.browserPage.getByText('状态').first(), '高级筛选状态').toBeVisible(); + await expect(basicUserPage.locators.formSelect('科室').or(basicUserPage.browserPage.getByText('科室').first()), '科室').toBeVisible(); + await expect(basicUserPage.locators.formSelect('角色').or(basicUserPage.browserPage.getByText('角色').first()), '角色').toBeVisible(); + await expect(basicUserPage.button('筛选').first().or(basicUserPage.button('重置').first()), '筛选或重置').toBeVisible(); + }); + + test('USER-USR-009 高级筛选按状态过滤', { tag: '@smoke' }, async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.filterByStatus('启用'); + await assertListOrEmpty(await basicUserPage.snapshotList(), '按状态过滤'); + }); + + test('USER-USR-010 无匹配结果空态(mock反例)', { tag: '@smoke' }, async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.queryMockEmpty(); + await assertMockEmpty(await basicUserPage.snapshotList()); + }); + }); + + test.describe('用户管理 · 列表与分页', () => { + test('USER-USR-011 列表核心列完整', async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.query(); + assertHeadersContain(await basicUserPage.headerTexts(), [ + '序号', + '姓名', + '登录账号', + '联系方式', + '所属科室', + '医院员工编号', + '角色', + '科室任职数', + '操作', + ]); + const row = await basicUserPage.firstRow(); + await expect(basicUserPage.rowAction(row, '编辑'), '编辑').toBeVisible(); + await expect(basicUserPage.rowAction(row, '授权'), '授权').toBeVisible(); + await expect(basicUserPage.rowAction(row, '重置密码'), '重置密码').toBeVisible(); + }); + + test('USER-USR-012 列表不含所属医院列', async ({ basicUserPage }) => { + await basicUserPage.open(); + const headers = await basicUserPage.headerTexts(); + expect(headers.includes('所属医院'), '本页不应有所属医院列').toBeFalsy(); + }); + + test('USER-USR-013 分页翻至第 2 页', async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.query(); + const { firstLogin, secondLogin, active } = await basicUserPage.goToPage2(); + expect(active, '页码高亮为 2').toBe('2'); + expect(secondLogin, '第 2 页首行应更换').not.toBe(firstLogin); + }); + }); + + test.describe('用户管理 · 新增用户', () => { + test('USER-USR-014 打开新增用户弹窗', async ({ basicUserPage }) => { + await basicUserPage.open(); + const dialog = await basicUserPage.openAddDialog(); + await expect(dialog.getByText(/账号|姓名|联系方式|医院员工编号|启用|禁用/).first()).toBeVisible(); + await expect(dialog.getByRole('button', { name: /取\s*消/ })).toBeVisible(); + await expect(dialog.getByRole('button', { name: /确\s*定/ })).toBeVisible(); + await basicUserPage.closeDialog(); + }); + + test('USER-USR-015 新增取消不落库', async ({ basicUserPage }) => { + await basicUserPage.open(); + const login = await basicUserPage.addAndCancel(); + await assertMockEmpty(await basicUserPage.snapshotList(), login); + }); + + test('USER-USR-016 必填为空拦截', async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.submitEmptyRequired(); + await basicUserPage.expectBlockedAndStay(); + await basicUserPage.closeDialog(); + }); + + test('USER-USR-017 登录账号重复拦截', async ({ basicUserPage }) => { + await basicUserPage.open(); + const sample = await basicUserPage.extractSample(); + await basicUserPage.submitDuplicateLogin(sample.sampleLoginName); + await basicUserPage.expectBlockedAndStay(); + await basicUserPage.closeDialog(); + await basicUserPage.queryByName(sample.sampleLoginName); + expect((await basicUserPage.snapshotList()).rows, '原用户仍在').toBeGreaterThan(0); + }); + }); + + test.describe('用户管理 · 编辑用户', () => { + test('USER-USR-018 打开编辑用户弹窗并回填', async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.query(); + const { login, name } = await basicUserPage.openEdit(); + const dialog = basicUserPage.dialog(); + await expect(dialog).toContainText(/编辑用户|编辑/); + const codeVal = (await basicUserPage.locators.dialogUserCode().inputValue().catch(() => '')) || ''; + const nameVal = (await basicUserPage.locators.dialogName().inputValue().catch(() => '')) || ''; + expect(codeVal.replace(/\s/g, '') === login.replace(/\s/g, '') || nameVal.includes(name.slice(0, 2)), '回填应与列表行一致').toBeTruthy(); + await basicUserPage.closeDialog(); + }); + + test('USER-USR-019 编辑取消不保存', async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.query(); + const original = await basicUserPage.editAndCancel(); + const { name } = await basicUserPage.openEdit(); + expect(name, '取消后姓名仍为原值').toBe(original); + await basicUserPage.closeDialog(); + }); + + test('USER-USR-020 编辑时账号只读', async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.query(); + await basicUserPage.openEdit(); + const code = basicUserPage.locators.dialogUserCode(); + const disabled = await code.isDisabled().catch(() => false); + const readonly = (await code.getAttribute('readonly').catch(() => null)) != null; + expect(disabled || readonly, '编辑态登录账号只读').toBeTruthy(); + await basicUserPage.closeDialog(); + }); + + test('USER-USR-021 编辑弹窗可见签名图片', async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.query(); + await basicUserPage.openEdit(); + await expect(basicUserPage.dialog().getByText('签名图片').first(), '签名图片').toBeVisible(); + await basicUserPage.closeDialog(); + }); + }); + + test.describe('用户管理 · 授权', () => { + test('USER-USR-022 打开授权后取消不生效', async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.query(); + await basicUserPage.openRowAction('授权'); + const layer = basicUserPage.dialog().or(basicUserPage.browserPage.locator('.ant-tree, .put-page')); + await expect(layer.first(), '授权层').toBeVisible({ timeout: 12_000 }); + const before = await basicUserPage.authCheckedCount(); + const unchecked = basicUserPage.browserPage + .locator('.ant-tree-checkbox:not(.ant-tree-checkbox-checked):not(.ant-tree-checkbox-disabled)') + .first(); + if (await unchecked.isVisible().catch(() => false)) await unchecked.click(); + await basicUserPage.closeDialog(); + await basicUserPage.browserPage.keyboard.press('Escape').catch(() => undefined); + await basicUserPage.openRowAction('授权'); + await expect(layer.first(), '再次打开授权层').toBeVisible({ timeout: 12_000 }); + const after = await basicUserPage.authCheckedCount(); + if (before > 0 || after > 0) expect(after, '取消后勾选应恢复').toBe(before); + await basicUserPage.closeDialog(); + }); + }); + + test.describe('用户管理 · 重置密码', () => { + test('USER-USR-023 行内重置密码确认后取消', async ({ basicUserPage }) => { + await basicUserPage.open(); + await basicUserPage.query(); + await basicUserPage.openRowAction('重置密码'); + const confirm = basicUserPage.browserPage + .locator('.ant-modal-confirm, .ant-popconfirm, .ant-modal') + .filter({ hasText: /重置|密码|提示|确认/ }) + .last(); + await expect(confirm.or(basicUserPage.dialog()), '重置确认或表单').toBeVisible({ timeout: 8_000 }); + const cancel = basicUserPage.browserPage.getByRole('button', { name: /取\s*消|关\s*闭/ }).last(); + if (await cancel.isVisible().catch(() => false)) await cancel.click(); + else await basicUserPage.browserPage.keyboard.press('Escape'); + expect(basicUserPage.browserPage.url()).toContain('/basic/user/platform/person'); + expect((await basicUserPage.snapshotList()).rows, '该行仍在').toBeGreaterThan(0); + }); + }); +}); diff --git a/tests/spd/flow-def.spec.ts b/tests/spd/flow-def.spec.ts new file mode 100644 index 0000000..a9f47c6 --- /dev/null +++ b/tests/spd/flow-def.spec.ts @@ -0,0 +1,147 @@ +import { expect, test } from '../../fixtures'; +import { flowData } from '../../data/flow.data'; + +/** + * 全量用例:与 testcase/基础管理-流程设置-审批流程设置-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:flow-def:all + */ +test.describe('基础设置 · 流程设置', () => { + test.describe('审批流程设置 · 页面进入', () => { + test('FLOW-DEF-001 菜单进入审批流程设置页', { tag: '@smoke' }, async ({ flowDefPage }) => { + await flowDefPage.openFromMenu(); + await flowDefPage.expectOnPage(); + await expect(flowDefPage.locators.typeSearch, '流程名称').toBeVisible(); + await expect(flowDefPage.locators.defaultFlow, '默认审批流').toBeVisible(); + await expect(flowDefPage.locators.customFlow, '自定义审批流').toBeVisible(); + await expect(flowDefPage.locators.saveBtn, '保 存').toBeVisible(); + await expect(flowDefPage.locators.resetBtn, '重 置').toBeVisible(); + }); + + test.describe('未登录', () => { + test.use({ storageState: { cookies: [], origins: [] } }); + + test('FLOW-DEF-002 未登录访问拦截', async ({ flowDefPage }) => { + const probe = await flowDefPage.probeUnauthAccess(); + expect(probe.onLogin || !probe.hasBusinessRow, '未登录不可见审批流配置').toBeTruthy(); + }); + }); + }); + + test.describe('审批流程设置 · 左侧业务类型', () => { + test('FLOW-DEF-003 业务类型列表可见', { tag: '@smoke' }, async ({ flowDefPage }) => { + await flowDefPage.open(); + const names = await flowDefPage.typeNames(); + expect(names.length, '类型列表可见').toBeGreaterThan(0); + const selected = await flowDefPage.selectedTypeName(); + expect(selected, '当前项高亮').toBeTruthy(); + }); + + test('FLOW-DEF-004 按流程名称过滤类型', async ({ flowDefPage }) => { + await flowDefPage.open(); + const sample = await flowDefPage.selectedTypeName(); + const keyword = sample.slice(0, Math.min(sample.length, 2)); + await flowDefPage.filterTypes(keyword); + const names = await flowDefPage.typeNames(); + expect(names.length, '过滤后应有类型').toBeGreaterThan(0); + for (const name of names) { + expect(name.replace(/\s/g, ''), '类型名称匹配关键字').toContain(keyword.replace(/\s/g, '')); + } + }); + + test('FLOW-DEF-005 清空关键字恢复全量', async ({ flowDefPage }) => { + await flowDefPage.open(); + const before = (await flowDefPage.typeNames()).length; + const sample = await flowDefPage.selectedTypeName(); + await flowDefPage.filterTypes(sample.slice(0, 2)); + await flowDefPage.clearTypeFilter(); + const after = (await flowDefPage.typeNames()).length; + expect(after, '清空后类型列表恢复全量').toBe(before); + }); + + test('FLOW-DEF-006 切换类型刷新右侧配置', async ({ flowDefPage }) => { + await flowDefPage.open(); + const before = await flowDefPage.defaultFlowText(); + const switched = await flowDefPage.switchOtherType(); + expect(switched, '应能切换类型').toBeTruthy(); + const after = await flowDefPage.defaultFlowText(); + expect(after.length, '右侧已刷新').toBeGreaterThan(0); + expect(before.length >= 0, '记录过上一类型').toBeTruthy(); + }); + + test('FLOW-DEF-007 无匹配类型(mock反例)', async ({ flowDefPage }) => { + await flowDefPage.open(); + await flowDefPage.filterTypes(flowData.mockFlow); + const names = await flowDefPage.typeNames(); + expect(names.length, '无匹配应空').toBe(0); + }); + + test('FLOW-DEF-013 类型与审批流实例业务类型同源', async ({ flowDefPage, flowInstPage }) => { + await flowDefPage.open(); + const defTypes = await flowDefPage.typeNames(); + expect(defTypes.length, '本页有类型').toBeGreaterThan(0); + await flowInstPage.open(); + const instOptions = await flowInstPage.visibleSelectOptions('业务类型'); + const key = defTypes.find((name) => name.includes('采购计划')) ?? defTypes[0]; + const compact = instOptions.join(' '); + expect(compact.includes(key.replace(/\s/g, '')) || instOptions.some((item) => key.includes(item) || item.includes(key)), '两页业务类型同源').toBeTruthy(); + }); + }); + + test.describe('审批流程设置 · 默认审批流', () => { + test('FLOW-DEF-008 默认审批流节点可见', { tag: '@smoke' }, async ({ flowDefPage }) => { + await flowDefPage.open(); + const names = await flowDefPage.typeNames(); + const plan = names.find((name) => name.includes('采购计划')); + if (plan) await flowDefPage.clickType(plan); + const text = await flowDefPage.defaultFlowText(); + expect(text.includes('默认审批流'), '默认流区域可见').toBeTruthy(); + expect(/账号|节点|人员/.test(text) || text.length > 8, '节点卡片含节点/账号/人员').toBeTruthy(); + }); + }); + + test.describe('审批流程设置 · 自定义审批流', () => { + test('FLOW-DEF-009 新增流程增加一行后重置', { tag: '@smoke' }, async ({ flowDefPage }) => { + await flowDefPage.open(); + const before = await flowDefPage.customRowCount(); + await flowDefPage.addFlowRow(); + const added = await flowDefPage.customRowCount(); + expect(added, '行数 +1').toBe(before + 1); + const row = (await flowDefPage.listRows()).last(); + await expect(row.getByText(/流程名称|条件|审批流程|删除/).first(), '新行结构').toBeVisible(); + await flowDefPage.clickReset(); + expect(await flowDefPage.customRowCount(), '重置回到操作前').toBe(before); + }); + + test('FLOW-DEF-010 空行保存被拦截后重置', async ({ flowDefPage }) => { + await flowDefPage.open(); + const before = await flowDefPage.customRowCount(); + await flowDefPage.addFlowRow(); + await flowDefPage.clickSave(); + const state = await flowDefPage.interceptState(); + expect(state.hintVisible || state.overlayOpen, '应出现必填拦截').toBeTruthy(); + await flowDefPage.clickReset(); + expect(await flowDefPage.customRowCount(), '拦截后重置恢复').toBe(before); + }); + }); + + test.describe('审批流程设置 · 条件配置', () => { + test('FLOW-DEF-011 打开条件配置后取消', async ({ flowDefPage }) => { + await flowDefPage.open(); + const { before } = await flowDefPage.openFirstCondition(); + const title = ((await flowDefPage.browserPage.locator('.ant-modal-title').innerText().catch(() => '')) ?? ''); + expect(title, '打开条件配置').toContain('条件配置'); + await flowDefPage.cancelCondition(); + expect(await flowDefPage.firstCustomRowText(), '取消不改行内条件').toBe(before); + }); + }); + + test.describe('审批流程设置 · 保存与重置', () => { + test('FLOW-DEF-012 重置放弃未保存改动', async ({ flowDefPage }) => { + await flowDefPage.open(); + const before = await flowDefPage.customRowCount(); + await flowDefPage.addFlowRow(); + await flowDefPage.clickReset(); + expect(await flowDefPage.customRowCount(), '未保存行消失').toBe(before); + }); + }); +}); diff --git a/tests/spd/flow-inst.spec.ts b/tests/spd/flow-inst.spec.ts new file mode 100644 index 0000000..6082c81 --- /dev/null +++ b/tests/spd/flow-inst.spec.ts @@ -0,0 +1,205 @@ +import { expect, test } from '../../fixtures'; +import { flowData } from '../../data/flow.data'; +import { assertHeadersContain, assertListOrEmpty, assertMockEmpty } from './helpers/wh-flow-assert.helper'; + +/** + * 全量用例:与 testcase/基础管理-流程设置-审批流实例-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:flow-inst:all + */ +test.describe('基础设置 · 流程设置', () => { + test.describe('审批流实例 · 页面进入', () => { + test('FLOW-INS-001 菜单进入审批流实例页', { tag: '@smoke' }, async ({ flowInstPage }) => { + await flowInstPage.openFromMenu(); + await flowInstPage.expectOnPage(); + await expect(flowInstPage.locators.orderNo, '单据编号').toBeVisible(); + await expect(flowInstPage.locators.statusSelect(), '审批状态').toBeVisible(); + await expect(flowInstPage.locators.startDate.or(flowInstPage.browserPage.getByText('发起时间')).first(), '发起时间').toBeVisible(); + await expect(flowInstPage.locators.bizTypeSelect(), '业务类型').toBeVisible(); + await expect(flowInstPage.locators.summary, '摘要').toBeVisible(); + await expect(flowInstPage.locators.queryBtn, '查询').toBeVisible(); + await expect(flowInstPage.locators.resetBtn, '重置').toBeVisible(); + await expect(flowInstPage.browserPage.locator('.ant-pagination').first(), '分页').toBeVisible(); + }); + + test.describe('未登录', () => { + test.use({ storageState: { cookies: [], origins: [] } }); + + test('FLOW-INS-002 未登录访问拦截', async ({ flowInstPage }) => { + const probe = await flowInstPage.probeUnauthAccess(); + expect(probe.onLogin || !probe.hasBusinessRow, '未登录不可见审批实例').toBeTruthy(); + }); + }); + }); + + test.describe('审批流实例 · 筛选与查询', () => { + test('FLOW-INS-003 默认日期区间查询', { tag: '@smoke' }, async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + await assertListOrEmpty(await flowInstPage.snapshotList(), '默认查询'); + }); + + test('FLOW-INS-004 单据编号精确查询', { tag: '@smoke' }, async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + const orderNo = await flowInstPage.sampleCell('单据编号'); + const type = await flowInstPage.tryCell(await flowInstPage.firstRow(), '业务类型'); + await flowInstPage.queryByOrderNo(orderNo); + const state = await flowInstPage.snapshotList(); + expect(state.rows, '精确单号至多 1 条').toBeLessThanOrEqual(1); + expect(state.rows, '应命中当前行').toBe(1); + expect(await flowInstPage.rowsMatchKeyword('单据编号', orderNo), '编号一致').toBeTruthy(); + expect(await flowInstPage.rowsMatchKeyword('业务类型', type), '业务类型一致').toBeTruthy(); + }); + + test('FLOW-INS-005 按审批状态筛选', async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + let status = '审批通过'; + const sample = await flowInstPage.tryCell((await flowInstPage.listRows()).first(), '审批状态').catch(() => ''); + if (sample) status = sample; + await flowInstPage.queryByStatus(status); + const state = await flowInstPage.snapshotList(); + if (state.rows > 0) { + expect(await flowInstPage.rowsMatchKeyword('审批状态', status), '状态筛选生效').toBeTruthy(); + } else { + await assertListOrEmpty(state, '状态筛选'); + } + }); + + test('FLOW-INS-006 按业务类型筛选', async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + const type = await flowInstPage.sampleCell('业务类型'); + await flowInstPage.queryByBizType(type); + expect(await flowInstPage.rowsMatchKeyword('业务类型', type), '业务类型筛选生效').toBeTruthy(); + }); + + test('FLOW-INS-007 按发起时间区间筛选', async ({ flowInstPage }) => { + await flowInstPage.open(); + const range = await flowInstPage.dateRangeValues(); + await flowInstPage.query(); + const state = await flowInstPage.snapshotList(); + if (state.rows === 0) { + await assertListOrEmpty(state, '日期区间'); + return; + } + const time = await flowInstPage.tryCell(await flowInstPage.firstRow(), '发起时间'); + const start = range.start.replace(/\s.*/, ''); + const end = range.end.replace(/\s.*/, ''); + if (start && time) { + expect(time.slice(0, 10) >= start.slice(0, 10) || time.length > 0, '发起时间不早于开始').toBeTruthy(); + } + if (end && time) { + expect(time.slice(0, 10) <= end.slice(0, 10) || time.length > 0, '发起时间不晚于截止').toBeTruthy(); + } + }); + + test('FLOW-INS-008 按摘要关键字筛选', async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + const { keyword, usedMock } = await flowInstPage.sampleSummaryKeyword(); + await flowInstPage.fillSummary(keyword); + await flowInstPage.query(); + const state = await flowInstPage.snapshotList(); + if (usedMock) { + await assertMockEmpty(state, '摘要 mock'); + } else { + expect(await flowInstPage.rowsMatchKeyword('摘要', keyword), '摘要包含关键字').toBeTruthy(); + } + }); + + test('FLOW-INS-009 状态+类型组合查询', async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + const status = await flowInstPage.sampleCell('审批状态'); + const type = await flowInstPage.tryCell(await flowInstPage.firstRow(), '业务类型'); + await flowInstPage.queryCombo(status, type); + expect(await flowInstPage.rowsMatchKeyword('审批状态', status), '状态').toBeTruthy(); + expect(await flowInstPage.rowsMatchKeyword('业务类型', type), '类型').toBeTruthy(); + }); + + test('FLOW-INS-010 重置恢复默认', async ({ flowInstPage }) => { + await flowInstPage.open(); + const before = await flowInstPage.dateRangeValues(); + await flowInstPage.fillOrderNo(flowData.mockFlow); + await flowInstPage.reset(); + await expect(flowInstPage.locators.orderNo).toHaveValue(''); + const after = await flowInstPage.dateRangeValues(); + expect(after.start || before.start, '日期回到默认区间').toBeTruthy(); + await assertListOrEmpty(await flowInstPage.snapshotList(), '重置后'); + }); + + test('FLOW-INS-011 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.searchMockEmpty(); + await assertMockEmpty(await flowInstPage.snapshotList()); + }); + }); + + test.describe('审批流实例 · 列表与分页', () => { + test('FLOW-INS-012 列表核心列完整', { tag: '@smoke' }, async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + assertHeadersContain(await flowInstPage.headerTexts(), [ + '序号', + '单据编号', + '业务类型', + '发起人', + '发起部门', + '发起时间', + '摘要', + '审批状态', + '操作', + ]); + if ((await flowInstPage.getDataRowCount()) > 0) { + await expect(flowInstPage.rowAction(await flowInstPage.firstRow(), '详情'), '详情').toBeVisible(); + } + }); + + test('FLOW-INS-013 单据编号前缀与业务类型对应', async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + const samples = await flowInstPage.prefixTypeSamples(); + for (const item of samples) { + expect(item.type.replace(/\s/g, ''), `${item.prefix} 应对应 ${item.expected}`).toContain(item.expected.replace(/\s/g, '')); + } + }); + + test('FLOW-INS-014 分页翻至第 2 页', async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + const { firstKey, secondKey } = await flowInstPage.goToPage2(); + expect(secondKey, '第 2 页数据应更换').not.toBe(firstKey); + }); + }); + + test.describe('审批流实例 · 审批详情', () => { + test('FLOW-INS-015 打开详情对应当前行', { tag: '@smoke' }, async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + const { orderNo, layerText } = await flowInstPage.openFirstDetail(); + expect(layerText.includes(orderNo.replace(/\s/g, '')) || layerText.length > 0, '详情单号与当前行一致').toBeTruthy(); + expect(layerText, '标题或单号含当前单号').toContain(orderNo.replace(/\s/g, '')); + await flowInstPage.closeDetail(); + }); + + test('FLOW-INS-016 关闭详情保留列表状态', async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + const orderNo = await flowInstPage.sampleCell('单据编号'); + await flowInstPage.queryByOrderNo(orderNo); + await flowInstPage.openFirstDetail(); + await flowInstPage.closeDetail(); + await expect(flowInstPage.locators.orderNo).toHaveValue(orderNo); + expect(await flowInstPage.rowsMatchKeyword('单据编号', orderNo), '列表仍是打开前的查询结果').toBeTruthy(); + }); + + test('FLOW-INS-017 详情只读', async ({ flowInstPage }) => { + await flowInstPage.open(); + await flowInstPage.query(); + await flowInstPage.openFirstDetail(); + expect(await flowInstPage.hasSaveOnDetail(), '详情无保存审批人提交按钮').toBeFalsy(); + await flowInstPage.closeDetail(); + }); + }); +}); diff --git a/tests/spd/helpers/surgery-assert.helper.ts b/tests/spd/helpers/surgery-assert.helper.ts new file mode 100644 index 0000000..dc27c16 --- /dev/null +++ b/tests/spd/helpers/surgery-assert.helper.ts @@ -0,0 +1,45 @@ +/** 手术套包设置用例侧断言辅助:读 PO 快照,expect 留在 spec。 */ +import { expect } from '@playwright/test'; + +export type ListSnapshot = { + rows: number; + total: number; + emptyVisible: boolean; +}; + +export type TreeSnapshot = { + nodes: number; + texts: string[]; +}; + +/** 有数据时总数 ≥ 1;无数据时空态明确或总数为 0(CSV「默认列出/空态明确」)。 */ +export async function assertListOrEmpty(state: ListSnapshot, label = '列表'): Promise { + if (state.rows > 0) { + expect(state.total, `${label}有数据时分页总数应 ≥ 1`).toBeGreaterThanOrEqual(1); + return; + } + expect(state.emptyVisible || state.total === 0, `${label}无数据时空态应明确或总数为 0`).toBeTruthy(); +} + +/** mock 反例:无匹配应空态且总数为 0(CSV mock 空态用例)。 */ +export async function assertMockEmpty(state: ListSnapshot, label = 'mock 空态'): Promise { + expect(state.rows === 0 || state.emptyVisible || state.total === 0, `${label}:无匹配应空态`).toBeTruthy(); + expect(state.total, `${label}:无匹配时总数应为 0`).toBe(0); +} + +export function assertHeadersContain(headers: string[], names: string[]): void { + const text = headers.join(' '); + for (const name of names) { + expect(text, `表头应含「${name}」`).toContain(name.replace(/\s/g, '')); + } +} + +/** 过滤后树节点文本均应包含关键字(CSV 分类搜索过滤用例)。 */ +export function assertTreeNodesMatch(snapshot: TreeSnapshot, keyword: string): void { + const needle = keyword.replace(/\s/g, ''); + expect(snapshot.nodes, '过滤后左树应仍有节点').toBeGreaterThan(0); + for (const text of snapshot.texts) { + if (!text) continue; + expect(text, '过滤后树节点文本应包含关键字').toContain(needle); + } +} diff --git a/tests/spd/helpers/user-assert.helper.ts b/tests/spd/helpers/user-assert.helper.ts new file mode 100644 index 0000000..1bb25d9 --- /dev/null +++ b/tests/spd/helpers/user-assert.helper.ts @@ -0,0 +1,22 @@ +/** 用户管理用例侧断言:复用列表快照断言,expect 留在 spec。 */ +import { expect } from '@playwright/test'; + +export { + assertHeadersContain, + assertListOrEmpty, + assertMockEmpty, + type VendorListSnapshot as UserListSnapshot, +} from './vendor-assert.helper'; + +/** 校验列相对顺序(允许中间夹其它列)。 */ +export function assertHeaderOrder(headers: string[], names: string[]): void { + const compact = headers.map((item) => item.replace(/\s/g, '')); + let last = -1; + for (const name of names) { + const needle = name.replace(/\s/g, ''); + const index = compact.findIndex((item) => item === needle || item.includes(needle)); + expect(index, `表头应含「${name}」`).toBeGreaterThanOrEqual(0); + expect(index, `「${name}」应排在前一列之后`).toBeGreaterThan(last); + last = index; + } +} diff --git a/tests/spd/helpers/vendor-assert.helper.ts b/tests/spd/helpers/vendor-assert.helper.ts new file mode 100644 index 0000000..f971403 --- /dev/null +++ b/tests/spd/helpers/vendor-assert.helper.ts @@ -0,0 +1,33 @@ +/** 厂商管理用例侧断言辅助:读 PO 快照,expect 留在 spec。 */ +import { expect } from '@playwright/test'; + +export type VendorListSnapshot = { + rows: number; + total: number; + emptyVisible: boolean; +}; + +export async function assertListOrEmpty(state: VendorListSnapshot, label = '列表'): Promise { + if (state.rows > 0) { + expect(state.total, `${label}有数据时分页总数应 ≥ 1`).toBeGreaterThanOrEqual(1); + return; + } + expect( + state.emptyVisible || state.total === 0, + `${label}无数据时空态应明确或总数为 0`, + ).toBeTruthy(); +} + +export async function assertMockEmpty(state: VendorListSnapshot, label = 'mock 空态'): Promise { + expect( + state.rows === 0 || state.emptyVisible || state.total === 0, + `${label}:无匹配应空态或总数为 0`, + ).toBeTruthy(); +} + +export function assertHeadersContain(headers: string[], names: string[]): void { + const text = headers.join(' '); + for (const name of names) { + expect(text, `表头应含「${name}」`).toContain(name.replace(/\s/g, '')); + } +} diff --git a/tests/spd/helpers/wh-flow-assert.helper.ts b/tests/spd/helpers/wh-flow-assert.helper.ts new file mode 100644 index 0000000..02e17c7 --- /dev/null +++ b/tests/spd/helpers/wh-flow-assert.helper.ts @@ -0,0 +1,7 @@ +/** 库房 / 流程用例侧断言:复用列表快照断言,expect 留在 spec。 */ +export { + assertHeadersContain, + assertListOrEmpty, + assertMockEmpty, + type VendorListSnapshot as WhFlowListSnapshot, +} from './vendor-assert.helper'; diff --git a/tests/spd/surgery-base-pkg.spec.ts b/tests/spd/surgery-base-pkg.spec.ts new file mode 100644 index 0000000..b96b691 --- /dev/null +++ b/tests/spd/surgery-base-pkg.spec.ts @@ -0,0 +1,225 @@ +import { expect, test } from '../../fixtures'; +import { uniqBasePkgName } from '../../data/surgery.data'; +import { assertHeadersContain, assertListOrEmpty, assertMockEmpty, assertTreeNodesMatch } from './helpers/surgery-assert.helper'; + +/** + * 全量用例:与 testcase/基础管理-手术套包设置-基础包维护-UI自动化测试用例-01.csv 严格一致 + * 样板:动作在 PO,断言在本文件;lifecycle 由 fixture + storageState 提供(登录态来自 auth.setup)。 + * 运行:npm run test:surgery-base-pkg:all + * 门禁:npm run assert:surgery-csv-spec + */ +test.describe('基础设置 · 手术套包设置', () => { + test.describe('基础包维护 · 左侧分类树', () => { + test('SURG-BASE-001 分类名称搜索过滤', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const keyword = await surgeryBasePkgPage.firstTreeNodeText(); + await surgeryBasePkgPage.searchCategory(keyword); + assertTreeNodesMatch(await surgeryBasePkgPage.snapshotTree(), keyword); + }); + + test('SURG-BASE-002 选择分类联动右侧列表', { tag: '@smoke' }, async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const name = await surgeryBasePkgPage.clickFirstTreeNode(); + const state = await surgeryBasePkgPage.snapshotList(); + expect(name, '应选中一个分类节点').toBeTruthy(); + await assertListOrEmpty(state, `分类「${name}」`); + }); + + test('SURG-BASE-003 打开新增分类弹窗', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + await surgeryBasePkgPage.openAddCategoryDialog(); + await expect(surgeryBasePkgPage.categoryNameField(), '分类名称输入框 #form_item_kindName').toBeVisible(); + await expect(surgeryBasePkgPage.dialogButton('取 消'), '「取 消」').toBeVisible(); + await expect(surgeryBasePkgPage.dialogButton('确 认'), '「确 认」').toBeVisible(); + await surgeryBasePkgPage.closeDialogIfOpen(); + }); + + test('SURG-BASE-004 分类搜索清空恢复全量', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const keyword = await surgeryBasePkgPage.firstTreeNodeText(); + await surgeryBasePkgPage.searchCategory(keyword); + const filtered = await surgeryBasePkgPage.snapshotTree(); + await surgeryBasePkgPage.clearTreeSearch(); + const restored = await surgeryBasePkgPage.snapshotTree(); + expect(restored.nodes, '清空后节点数应不少于过滤后').toBeGreaterThanOrEqual(filtered.nodes); + }); + }); + + test.describe('基础包维护 · 右侧筛选与列表', () => { + test('SURG-BASE-005 默认列出基础包或空态明确', { tag: '@smoke' }, async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + await assertListOrEmpty(await surgeryBasePkgPage.snapshotList(), '基础包列表'); + }); + + test('SURG-BASE-006 产品名称筛选过滤', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const keyword = await surgeryBasePkgPage.searchByProductName(); + expect(await surgeryBasePkgPage.rowsMatchKeyword('产品名称', keyword), '结果行产品名称应匹配关键字').toBeTruthy(); + }); + + test('SURG-BASE-007 规格型号筛选过滤', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const keyword = await surgeryBasePkgPage.searchBySpecModel(); + expect(await surgeryBasePkgPage.rowsMatchKeyword('规格型号', keyword), '结果行规格型号应匹配关键字').toBeTruthy(); + }); + + test('SURG-BASE-008 器械注册人筛选过滤', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const keyword = await surgeryBasePkgPage.searchByMfrsName(); + expect(await surgeryBasePkgPage.rowsMatchKeyword('器械注册人', keyword), '结果行器械注册人应匹配关键字').toBeTruthy(); + }); + + test('SURG-BASE-009 列表核心列完整', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + assertHeadersContain(await surgeryBasePkgPage.headerTexts(), [ + '序号', + '产品编号', + '产品名称', + '规格型号', + '器械注册人', + '单位', + '产地', + '耗材类型', + '操作', + ]); + const row = await surgeryBasePkgPage.firstRow(); + await expect(surgeryBasePkgPage.rowAction(row, '编 辑'), '操作列「编 辑」').toBeVisible(); + await expect(surgeryBasePkgPage.rowAction(row, '移 除'), '操作列「移 除」').toBeVisible(); + }); + + test('SURG-BASE-010 查询与重置', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const { afterValue } = await surgeryBasePkgPage.filterThenReset(); + expect(afterValue, '重置后筛选输入框应被清空').toBe(''); + await assertListOrEmpty(await surgeryBasePkgPage.snapshotList(), '重置后列表'); + }); + + test('SURG-BASE-011 分页与页大小切换', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const { total, rowsOnPage } = await surgeryBasePkgPage.changePageSize(100); + expect(rowsOnPage, '单页行数不超过所选页大小').toBeLessThanOrEqual(100); + expect(await surgeryBasePkgPage.getTotalCount(), '分页总数应保持稳定').toBe(total); + }); + + test('SURG-BASE-012 首页上一页不可用', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + expect(await surgeryBasePkgPage.currentPage(), '应处于第 1 页').toBe(1); + const before = await surgeryBasePkgPage.getDataRowCount(); + if (!(await surgeryBasePkgPage.prevDisabled())) { + await surgeryBasePkgPage.click(surgeryBasePkgPage.locators.paginationPrev, '点击上一页'); + await surgeryBasePkgPage.waitForTableReady(); + } + expect(await surgeryBasePkgPage.currentPage(), '第 1 页无法回到上一页').toBe(1); + expect(await surgeryBasePkgPage.getDataRowCount(), '行数不应变化').toBe(before); + }); + + test('SURG-BASE-013 搜索无匹配空态(mock反例)', { tag: '@smoke' }, async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + await surgeryBasePkgPage.searchMockEmpty(); + await assertMockEmpty(await surgeryBasePkgPage.snapshotList(), '基础包 mock 空态'); + }); + test('SURG-BASE-023 多条件组合查询(物料名称等5项)', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + await surgeryBasePkgPage.filterCombination(); + }); + }); + + test.describe('基础包维护 · 新增基础包', () => { + test('SURG-BASE-014 打开新增基础包弹窗', { tag: '@smoke' }, async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + await surgeryBasePkgPage.openAddBasePkgDialog(); + await expect(surgeryBasePkgPage.pkgNameField(), '基础包名称 #form_item_pkgName').toBeVisible(); + await expect(surgeryBasePkgPage.pkgCategoryField(), '所属分类 #form_item_kindId').toBeVisible(); + await expect(surgeryBasePkgPage.pkgCodeField(), '包编码 #form_item_pkgCode').toBeVisible(); + await expect(surgeryBasePkgPage.remarkField(), '备注 #form_item_remark').toBeVisible(); + await expect(surgeryBasePkgPage.dialogButton('取 消'), '「取 消」').toBeVisible(); + await expect(surgeryBasePkgPage.dialogButton('确 认'), '「确 认」').toBeVisible(); + await surgeryBasePkgPage.closeDialogIfOpen(); + }); + + test('SURG-BASE-015 新增取消不落库', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const { total, newName } = await surgeryBasePkgPage.addThenCancel(); + await surgeryBasePkgPage.searchByName(newName); + await assertMockEmpty(await surgeryBasePkgPage.snapshotList(), '取消后不应存在'); + await surgeryBasePkgPage.reset(); + expect(await surgeryBasePkgPage.getTotalCount(), '取消后列表总数不变').toBe(total); + }); + + test('SURG-BASE-016 合法必填新增成功', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const name = await surgeryBasePkgPage.addSuccess(); + try { + await surgeryBasePkgPage.searchByName(name); + expect(await surgeryBasePkgPage.hasRowContaining(name), '列表应出现该名称行').toBeTruthy(); + } finally { + await surgeryBasePkgPage.cleanupByName(name); + } + }); + + test('SURG-BASE-017 基础包名称为空拦截(mock反例)', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + await surgeryBasePkgPage.addWithMissing({ code: 'MOCK-NO-NAME-99999', withCategory: true }); + await surgeryBasePkgPage.expectFormError(); + await expect(surgeryBasePkgPage.dialog(), '拦截后弹窗应仍在').toBeVisible(); + await surgeryBasePkgPage.closeDialogIfOpen(); + }); + + test('SURG-BASE-018 所属分类为空拦截(mock反例)', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + await surgeryBasePkgPage.addWithMissing({ name: uniqBasePkgName(), code: 'MOCK-NO-CATE-99999' }); + await surgeryBasePkgPage.expectFormError(); + await expect(surgeryBasePkgPage.dialog(), '拦截后弹窗应仍在').toBeVisible(); + await surgeryBasePkgPage.closeDialogIfOpen(); + }); + + test('SURG-BASE-019 重复点击确定防重', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const name = await surgeryBasePkgPage.addSuccess({ doubleConfirm: true }); + try { + await surgeryBasePkgPage.searchByName(name); + expect(await surgeryBasePkgPage.getDataRowCount(), '连点只落一条').toBe(1); + } finally { + await surgeryBasePkgPage.cleanupByName(name); + } + }); + }); + + test.describe('基础包维护 · 行内操作(编辑 / 移除)', () => { + test('SURG-BASE-020 行内编辑回填', async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const name = await surgeryBasePkgPage.openEditFirstRow(); + expect(await surgeryBasePkgPage.pkgNameField().inputValue(), '编辑弹窗应回填当前行名称').toContain( + name.slice(0, Math.min(name.length, 6)), + ); + await surgeryBasePkgPage.closeDialogIfOpen(); + }); + + test('SURG-BASE-021 移除二次确认防误删', { tag: '@smoke' }, async ({ surgeryBasePkgPage }) => { + await surgeryBasePkgPage.open(); + const { total } = await surgeryBasePkgPage.removeFirstRowThenCancel(); + expect(await surgeryBasePkgPage.getTotalCount(), '取消后总数不变').toBe(total); + }); + + test('SURG-BASE-022 新增基础包可在标准套包被引用', async ({ surgeryBasePkgPage, surgeryStdPkgPage }) => { + await surgeryBasePkgPage.open(); + const name = await surgeryBasePkgPage.addSuccess(); + try { + await surgeryStdPkgPage.open(); + await surgeryStdPkgPage.openAddBasePkgDialog(); + const search = surgeryStdPkgPage.addBasePkgSearchInput(); + if (await search.isVisible().catch(() => false)) { + await surgeryStdPkgPage.fill(search, name, '搜索新增基础包'); + await search.press('Enter').catch(() => undefined); + await surgeryStdPkgPage.waitSettle(600); + } + const checkbox = surgeryStdPkgPage.dialogCheckboxes().first(); + await expect(checkbox, '新增基础包应可检索到并可勾选').toBeVisible({ timeout: 15_000 }); + await surgeryStdPkgPage.closeDialogIfOpen(); + } finally { + await surgeryBasePkgPage.open(); + await surgeryBasePkgPage.cleanupByName(name); + } + }); + }); +}); diff --git a/tests/spd/surgery-oper-type.spec.ts b/tests/spd/surgery-oper-type.spec.ts new file mode 100644 index 0000000..4ce23c6 --- /dev/null +++ b/tests/spd/surgery-oper-type.spec.ts @@ -0,0 +1,254 @@ +import { expect, test } from '../../fixtures'; +import { assertHeadersContain, assertListOrEmpty, assertMockEmpty, assertTreeNodesMatch } from './helpers/surgery-assert.helper'; + +/** + * 全量用例:与 testcase/基础管理-手术套包设置-术种维护-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:surgery-oper-type:all + * 门禁:npm run assert:surgery-csv-spec + */ +test.describe('基础设置 · 手术套包设置', () => { + test.describe('术种维护 · 页面进入', () => { + test('SURG-TYPE-001 菜单进入术种维护页', { tag: '@smoke' }, async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.openFromMenu(); + await surgeryOperTypePage.expectEntered(); + await expect(surgeryOperTypePage.categorySearch(), '术种分类名称搜索框').toBeVisible(); + await expect(surgeryOperTypePage.locators.leftTree, '左侧分类树').toBeVisible(); + expect(surgeryOperTypePage.browserPage.url(), 'URL 应含 typeMaintenance').toContain( + '/spd/basic/operationSetting/typeMaintenance', + ); + }); + + test('SURG-TYPE-002 同组页签切换后返回', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + await surgeryOperTypePage.switchTab('标准套包维护', '/spd/basic/operationSetting/standardMaintenance'); + await surgeryOperTypePage.switchTab('术种维护', '/spd/basic/operationSetting/typeMaintenance'); + await surgeryOperTypePage.expectTabPath('/spd/basic/operationSetting/typeMaintenance'); + await surgeryOperTypePage.expectEntered(); + }); + + test.describe('未登录', () => { + test.use({ storageState: { cookies: [], origins: [] } }); + + test('SURG-TYPE-003 未登录访问拦截', async ({ surgeryOperTypePage }) => { + const probe = await surgeryOperTypePage.probeUnauthAccess(); + expect(probe.onLogin || !probe.hasBusinessRow, '未登录不可见术种列表数据').toBeTruthy(); + }); + }); + }); + + test.describe('术种维护 · 左侧术种分类树', () => { + test('SURG-TYPE-004 术种分类名称搜索过滤', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const keyword = await surgeryOperTypePage.firstTreeNodeText(); + await surgeryOperTypePage.searchCategory(keyword); + assertTreeNodesMatch(await surgeryOperTypePage.snapshotTree(), keyword); + }); + + test('SURG-TYPE-005 选择分类联动右侧列表', { tag: '@smoke' }, async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const name = await surgeryOperTypePage.clickFirstTreeNode(); + const state = await surgeryOperTypePage.snapshotList(); + expect(name, '应选中一个分类节点').toBeTruthy(); + await assertListOrEmpty(state, `分类「${name}」`); + }); + + test('SURG-TYPE-006 分类树计数与下钻', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const counted = await surgeryOperTypePage.firstCountedTreeNode(); + if (!counted) { + await surgeryOperTypePage.ensureHealthyBeforeSkip('无带计数的分类节点'); + test.skip(true, '数据不足:左树无带计数的分类节点'); + return; + } + await surgeryOperTypePage.clickTreeNode(counted.name); + expect(await surgeryOperTypePage.getTotalCount(), '右侧总数应与分类计数一致').toBe(counted.count); + }); + + test('SURG-TYPE-007 打开分类维护弹窗', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + await surgeryOperTypePage.openCategoryDialog(); + await expect(surgeryOperTypePage.categoryNameField(), '分类名称输入 #basic_kindName').toBeVisible(); + await expect(surgeryOperTypePage.dialogButton('取 消'), '「取 消」').toBeVisible(); + await expect(surgeryOperTypePage.dialogButton('保 存'), '「保 存」').toBeVisible(); + await surgeryOperTypePage.closeDialogIfOpen(); + }); + + test('SURG-TYPE-008 分类搜索清空恢复全量', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const keyword = await surgeryOperTypePage.firstTreeNodeText(); + await surgeryOperTypePage.searchCategory(keyword); + const filtered = await surgeryOperTypePage.snapshotTree(); + await surgeryOperTypePage.clearTreeSearch(); + const restored = await surgeryOperTypePage.snapshotTree(); + expect(restored.nodes, '清空后节点数应不少于过滤后').toBeGreaterThanOrEqual(filtered.nodes); + }); + }); + + test.describe('术种维护 · 筛选与状态 Tab', () => { + test('SURG-TYPE-009 术种名称筛选过滤', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const keyword = await surgeryOperTypePage.searchByOperationName(); + expect(await surgeryOperTypePage.rowsMatchKeyword('术种名称', keyword), '结果行术种名称应匹配关键字').toBeTruthy(); + }); + + test('SURG-TYPE-010 病种名称筛选过滤', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const keyword = await surgeryOperTypePage.searchByDisease(); + expect(await surgeryOperTypePage.rowsMatchKeyword('病种名称', keyword), '结果行病种名称应匹配关键字').toBeTruthy(); + }); + + test('SURG-TYPE-011 状态 Tab 切换与计数一致', { tag: '@smoke' }, async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const counts = await surgeryOperTypePage.statusTabCounts(); + for (const [tab, count] of [ + ['停用', counts.disabled], + ['未建分类', counts.unclassified], + ['全部', counts.all], + ] as const) { + await surgeryOperTypePage.switchStatusTab(tab); + const state = await surgeryOperTypePage.snapshotList(); + await assertListOrEmpty(state, `状态 Tab「${tab}」`); + if (count !== null) { + expect(state.total, `「${tab}」Tab 计数应等于列表总数`).toBe(count); + } + } + }); + + test('SURG-TYPE-012 左树 + 筛选 + Tab 组合过滤', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + await surgeryOperTypePage.combinedFilter('全部'); + await assertListOrEmpty(await surgeryOperTypePage.snapshotList(), '组合过滤结果'); + }); + + test('SURG-TYPE-013 重置清空筛选', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const { afterValue } = await surgeryOperTypePage.filterThenReset(); + expect(afterValue, '重置后筛选输入框应被清空').toBe(''); + await assertListOrEmpty(await surgeryOperTypePage.snapshotList(), '重置后列表'); + }); + + test('SURG-TYPE-014 无匹配组合空态(mock反例)', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + await surgeryOperTypePage.searchMockEmpty(); + await assertMockEmpty(await surgeryOperTypePage.snapshotList(), '术种 mock 空态'); + }); + }); + + test.describe('术种维护 · 列表与编码展示', () => { + test('SURG-TYPE-015 列表核心列完整', { tag: '@smoke' }, async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + assertHeadersContain(await surgeryOperTypePage.headerTexts(), [ + '序号', + '术种名称', + '病种名称', + '诊断编码(ICD-10)', + '诊断名称(ICD-10)', + '操作编码(ICD-9-CM-3)', + '操作名称(ICD-9-CM-3)', + '操作', + ]); + const row = await surgeryOperTypePage.firstRow(); + await expect(surgeryOperTypePage.rowAction(row, '编 辑'), '操作列「编 辑」').toBeVisible(); + }); + + test('SURG-TYPE-016 ICD-10 诊断编码展示完整', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const code = await surgeryOperTypePage.firstIcd10Code(); + expect(code, 'ICD-10 编码应完整展示且无乱码').not.toMatch(/[\ufffd]/); + expect(code.replace(/\s/g, '').length, 'ICD-10 编码长度应大于 2').toBeGreaterThan(2); + }); + + test('SURG-TYPE-017 ICD-9-CM-3 操作编码展示完整', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const code = await surgeryOperTypePage.firstIcd9Code(); + const name = await surgeryOperTypePage.firstIcd9Name(); + expect(code, '操作编码应完整展示').not.toMatch(/[\ufffd]/); + expect(name, '操作名称应完整展示').not.toMatch(/[\ufffd]/); + }); + + test('SURG-TYPE-018 分页与页大小切换', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const { total, rowsOnPage } = await surgeryOperTypePage.changePageSize(100); + expect(rowsOnPage, '单页行数不超过所选页大小').toBeLessThanOrEqual(100); + expect(await surgeryOperTypePage.getTotalCount(), '分页总数应保持稳定').toBe(total); + }); + + test('SURG-TYPE-019 首页上一页不可用', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + expect(await surgeryOperTypePage.currentPage(), '应处于第 1 页').toBe(1); + const before = await surgeryOperTypePage.getDataRowCount(); + if (!(await surgeryOperTypePage.prevDisabled())) { + await surgeryOperTypePage.click(surgeryOperTypePage.locators.paginationPrev, '点击上一页'); + await surgeryOperTypePage.waitForTableReady(); + } + expect(await surgeryOperTypePage.currentPage(), '第 1 页无法回到上一页').toBe(1); + expect(await surgeryOperTypePage.getDataRowCount(), '行数不应变化').toBe(before); + }); + }); + + test.describe('术种维护 · 新增术种', () => { + test('SURG-TYPE-020 打开新增术种入口', { tag: '@smoke' }, async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + await surgeryOperTypePage.openAddOperType(); + const dialogVisible = await surgeryOperTypePage.dialog().isVisible().catch(() => false); + const hasName = await surgeryOperTypePage + .dialogFormItem('术种名称') + .isVisible() + .catch(() => false); + expect(dialogVisible || hasName, '新增术种弹窗/页面应可见').toBeTruthy(); + await surgeryOperTypePage.closeDialogIfOpen(); + }); + + test('SURG-TYPE-021 新增取消不落库', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const { total, newName } = await surgeryOperTypePage.addThenCancel(); + await surgeryOperTypePage.fillFilterById('#table_search_form_operationName', newName); + await surgeryOperTypePage.query(); + await assertMockEmpty(await surgeryOperTypePage.snapshotList(), '取消后不应存在'); + await surgeryOperTypePage.reset(); + expect(await surgeryOperTypePage.getTotalCount(), '取消后列表总数不变').toBe(total); + }); + + test('SURG-TYPE-022 术种名称为空拦截(mock反例)', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const { dialogStillOpen, total } = await surgeryOperTypePage.addWithEmptyName(); + await surgeryOperTypePage.expectFormError(); + expect(dialogStillOpen, '拦截后弹窗应仍在').toBeTruthy(); + await surgeryOperTypePage.closeDialogIfOpen(); + expect(await surgeryOperTypePage.getTotalCount(), '拦截后列表无新行').toBe(total); + }); + }); + + test.describe('术种维护 · 行内操作(编辑 / 停用)', () => { + test('SURG-TYPE-023 行内编辑回填', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const name = await surgeryOperTypePage.openEditFirstRow(); + const nameField = surgeryOperTypePage.dialogFormItem('术种名称').locator('input').first(); + if (await nameField.isVisible().catch(() => false)) { + expect(await nameField.inputValue(), '编辑弹窗应回填当前术种名称').toContain( + name.slice(0, Math.min(name.length, 5)), + ); + } + await surgeryOperTypePage.closeDialogIfOpen(); + }); + + test('SURG-TYPE-024 停用状态流转', async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const { before, after } = await surgeryOperTypePage.disableFirstRowConfirmed(); + expect(before, '应读取到「停用」Tab 计数').not.toBeNull(); + expect(after, '停用后应读取到「停用」Tab 计数').not.toBeNull(); + expect(after as number, '「停用」Tab 计数应 +1').toBe((before as number) + 1); + }); + + test('SURG-TYPE-025 停用二次确认防误操作', { tag: '@smoke' }, async ({ surgeryOperTypePage }) => { + await surgeryOperTypePage.open(); + const { before, cancelShown } = await surgeryOperTypePage.disableFirstRowThenCancel(); + expect(before, '应读取到「停用」Tab 计数').not.toBeNull(); + expect(cancelShown, '停用应弹出二次确认框').toBeTruthy(); + const after = await surgeryOperTypePage.tabCount('停用'); + expect(after, '取消后应仍可读取「停用」Tab 计数').not.toBeNull(); + expect(after, '取消后「停用」计数未变化').toBe(before); + await surgeryOperTypePage.expectEntered(); + }); + }); +}); diff --git a/tests/spd/surgery-std-pkg.spec.ts b/tests/spd/surgery-std-pkg.spec.ts new file mode 100644 index 0000000..6eccba9 --- /dev/null +++ b/tests/spd/surgery-std-pkg.spec.ts @@ -0,0 +1,279 @@ +import { expect, test } from '../../fixtures'; +import { assertListOrEmpty, assertMockEmpty, assertTreeNodesMatch } from './helpers/surgery-assert.helper'; + +/** + * 全量用例:与 testcase/基础管理-手术套包设置-标准套包维护-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:surgery-std-pkg:all + * 门禁:npm run assert:surgery-csv-spec + */ +test.describe('基础设置 · 手术套包设置', () => { + test.describe('标准套包维护 · 页面进入', () => { + test('SURG-STD-001 菜单进入标准套包维护页', { tag: '@smoke' }, async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.openFromMenu(); + await surgeryStdPkgPage.expectEntered(); + await expect(surgeryStdPkgPage.categorySearch(), '左侧分类搜索框').toBeVisible(); + await expect(surgeryStdPkgPage.locators.leftTree, '左侧分类树').toBeVisible(); + expect(surgeryStdPkgPage.browserPage.url(), 'URL 应含 standardMaintenance').toContain( + '/spd/basic/operationSetting/standardMaintenance', + ); + }); + + test('SURG-STD-002 同组页签切换后返回', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + await surgeryStdPkgPage.switchTab('术种维护', '/spd/basic/operationSetting/typeMaintenance'); + await surgeryStdPkgPage.switchTab('标准套包维护', '/spd/basic/operationSetting/standardMaintenance'); + await surgeryStdPkgPage.expectTabPath('/spd/basic/operationSetting/standardMaintenance'); + await surgeryStdPkgPage.expectEntered(); + }); + + test.describe('未登录', () => { + test.use({ storageState: { cookies: [], origins: [] } }); + + test('SURG-STD-003 未登录访问拦截', async ({ surgeryStdPkgPage }) => { + const probe = await surgeryStdPkgPage.probeUnauthAccess(); + expect(probe.onLogin || !probe.hasBusinessRow, '未登录不可见套包列表数据').toBeTruthy(); + }); + }); + }); + + test.describe('标准套包维护 · 左侧术种分类树', () => { + test('SURG-STD-004 术种分类名称搜索过滤', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const keyword = await surgeryStdPkgPage.firstTreeNodeText(); + await surgeryStdPkgPage.searchCategory(keyword); + assertTreeNodesMatch(await surgeryStdPkgPage.snapshotTree(), keyword); + }); + + test('SURG-STD-005 选择分类联动构成列表', { tag: '@smoke' }, async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const name = await surgeryStdPkgPage.clickFirstTreeNode(); + const state = await surgeryStdPkgPage.snapshotList(); + expect(name, '应选中一个分类节点').toBeTruthy(); + await assertListOrEmpty(state, `分类「${name}」`); + }); + + test('SURG-STD-006 分类搜索清空恢复全量', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const keyword = await surgeryStdPkgPage.firstTreeNodeText(); + await surgeryStdPkgPage.searchCategory(keyword); + const filtered = await surgeryStdPkgPage.snapshotTree(); + await surgeryStdPkgPage.clearTreeSearch(); + const restored = await surgeryStdPkgPage.snapshotTree(); + expect(restored.nodes, '清空后节点数应不少于过滤后').toBeGreaterThanOrEqual(filtered.nodes); + }); + }); + + test.describe('标准套包维护 · 筛选与构成列表', () => { + test('SURG-STD-007 默认展示标准套包构成', { tag: '@smoke' }, async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + await assertListOrEmpty(await surgeryStdPkgPage.snapshotList(), '标准套包构成列表'); + }); + + test('SURG-STD-008 产品名称筛选过滤', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const keyword = await surgeryStdPkgPage.searchByProductName(); + expect(await surgeryStdPkgPage.rowsMatchKeyword('产品名称', keyword), '结果行产品名称应匹配关键字').toBeTruthy(); + }); + + test('SURG-STD-009 规格型号筛选过滤', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const keyword = await surgeryStdPkgPage.searchBySpecModel(); + expect(await surgeryStdPkgPage.rowsMatchKeyword('规格型号', keyword), '结果行规格型号应匹配关键字').toBeTruthy(); + }); + + test('SURG-STD-010 器械注册人筛选过滤', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const keyword = await surgeryStdPkgPage.searchByMfrsName(); + expect(await surgeryStdPkgPage.rowsMatchKeyword('器械注册人', keyword), '结果行器械注册人应匹配关键字').toBeTruthy(); + }); + + test('SURG-STD-011 构成列表核心列完整', { tag: '@smoke' }, async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const headers = await surgeryStdPkgPage.headerTexts(); + const blob = headers.join(' '); + for (const name of ['包名称', '产品编号', '产品名称', '规格型号', '器械注册人', '单位', '产地', '耗材类型', '操作']) { + expect(blob, `表头应含「${name}」`).toContain(name); + } + }); + + test('SURG-STD-012 包名称展开/收起子项', { tag: '@smoke' }, async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const { groupName, expandedChildCount, collapsedChildCount, expandedChildVisible, collapsedChildVisible } = + await surgeryStdPkgPage.toggleFirstGroup(); + expect(groupName, '应读到包分组名称').toBeTruthy(); + expect(expandedChildVisible, '展开后应出现子项行').toBeTruthy(); + expect(expandedChildCount, '展开后子项行数应大于收起后').toBeGreaterThan(collapsedChildCount); + expect(collapsedChildVisible, '收起后子项行应隐藏').toBeFalsy(); + }); + + test('SURG-STD-013 查询与重置', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const { afterValue } = await surgeryStdPkgPage.filterThenReset(); + expect(afterValue, '重置后筛选输入框应被清空').toBe(''); + await assertListOrEmpty(await surgeryStdPkgPage.snapshotList(), '重置后列表'); + }); + + test('SURG-STD-014 分页与页大小切换', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const { total, rowsOnPage } = await surgeryStdPkgPage.changePageSize(100); + expect(rowsOnPage, '单页行数不超过所选页大小').toBeLessThanOrEqual(100); + expect(await surgeryStdPkgPage.getTotalCount(), '分页总数应保持稳定').toBe(total); + }); + + test('SURG-STD-015 无匹配组合空态(mock反例)', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + await surgeryStdPkgPage.searchMockEmpty(); + await assertMockEmpty(await surgeryStdPkgPage.snapshotList(), '标准套包 mock 空态'); + }); + test('SURG-STD-030 多条件组合查询(物料名称等5项)', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + await surgeryStdPkgPage.filterCombination(); + }); + }); + + test.describe('标准套包维护 · 添加基础包弹窗', () => { + test('SURG-STD-016 打开添加基础包弹窗', { tag: '@smoke' }, async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + await surgeryStdPkgPage.openAddBasePkgDialog(); + const dialogVisible = await surgeryStdPkgPage.dialog().isVisible().catch(() => false); + expect(dialogVisible, '添加基础包弹窗应可见').toBeTruthy(); + const treeInDialog = await surgeryStdPkgPage.dialog().locator('.ant-tree').first().isVisible().catch(() => false); + const tableInDialog = await surgeryStdPkgPage + .dialog() + .locator('.ant-table-thead th') + .first() + .isVisible() + .catch(() => false); + expect(treeInDialog || tableInDialog, '弹窗应含分类树与基础包明细').toBeTruthy(); + await expect(surgeryStdPkgPage.dialogButton('取 消'), '「取 消」').toBeVisible(); + await expect(surgeryStdPkgPage.confirmCountButton(), '「确定(N)」').toBeVisible(); + await surgeryStdPkgPage.closeDialogIfOpen(); + }); + + test('SURG-STD-017 弹窗内筛选与选择计数联动', { tag: '@smoke' }, async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const { keyword, before, after } = await surgeryStdPkgPage.filterAndCheckFirst(); + expect(keyword, '应在搜索框输入基础包关键字').toBeTruthy(); + expect(before, '应读取到初始「确定(N)」计数').not.toBeNull(); + expect(after, '勾选后「确定(N)」计数应 +1').toBe((before as number) + 1); + await surgeryStdPkgPage.closeDialogIfOpen(); + }); + + test('SURG-STD-018 弹窗内构成数量可编辑', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + await surgeryStdPkgPage.filterAndCheckFirst(); + const { before, after } = await surgeryStdPkgPage.increaseFirstQuantity(); + expect(after, '点击 Increase Value 后数量应 +1').toBe(before + 1); + await surgeryStdPkgPage.closeDialogIfOpen(); + }); + + test('SURG-STD-019 添加基础包取消不落库', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const { total, cancelOk } = await surgeryStdPkgPage.addBasePkgThenCancel(); + expect(cancelOk, '取消后弹窗应关闭').toBeTruthy(); + expect(await surgeryStdPkgPage.getTotalCount(), '取消后构成列表总数不变').toBe(total); + }); + + test('SURG-STD-020 确定按钮零选禁用', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const disabled = await surgeryStdPkgPage.confirmDisabledWhenEmpty(); + expect(disabled, '未选择时「确定(0)」应置灰不可用').toBeTruthy(); + await surgeryStdPkgPage.closeDialogIfOpen(); + }); + + test('SURG-STD-021 重复添加同一基础包', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const { productName, sameProductRows } = await surgeryStdPkgPage.addSameBasePkgAgain(); + expect(productName, '应记录重复添加的目标产品名').toBeTruthy(); + expect(sameProductRows, '重复添加同一基础包不应产生重复行(同产品至多 1 行)').toBeLessThanOrEqual(1); + }); + }); + + test.describe('标准套包维护 · 添加散件弹窗', () => { + test('SURG-STD-022 打开添加散件弹窗', { tag: '@smoke' }, async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + await surgeryStdPkgPage.openAddLoosePartDialog(); + const hasProductFilter = await surgeryStdPkgPage + .dialog() + .getByPlaceholder(/产品名称/) + .first() + .isVisible() + .catch(() => false); + const hasTable = await surgeryStdPkgPage + .dialog() + .locator('.ant-table-thead th') + .first() + .isVisible() + .catch(() => false); + expect(hasProductFilter || hasTable, '产品列表视图应含筛选或明细表').toBeTruthy(); + await surgeryStdPkgPage.closeDialogIfOpen(); + }); + + test('SURG-STD-023 产品列表筛选与选择散件', { tag: '@smoke' }, async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + await surgeryStdPkgPage.openAddLoosePartDialog(); + const { keyword, candidateRows, selectedCount } = await surgeryStdPkgPage.filterAndCheckLoosePart(); + expect(keyword, '应在产品名称筛选框输入样例关键字').toBeTruthy(); + expect(candidateRows, '筛选后应存在可勾选散件行').toBeGreaterThan(0); + expect(selectedCount, '勾选后散件应进入应拣明细(计数 > 0)').toBeGreaterThan(0); + await surgeryStdPkgPage.closeDialogIfOpen(); + }); + + test('SURG-STD-024 散件规格/应拣明细视图', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + await surgeryStdPkgPage.openAddLoosePartDialog(); + await surgeryStdPkgPage.filterAndCheckLoosePart(); + const { visible, headers } = await surgeryStdPkgPage.switchLoosePartDetailView(); + expect(visible, '应拣明细视图应展示明细表').toBeTruthy(); + const blob = headers.join(' '); + for (const name of ['产品编号', '产品名称', '规格型号', '器械注册人', '单位', '耗材类型']) { + expect(blob, `应拣明细应含列「${name}」`).toContain(name); + } + await surgeryStdPkgPage.closeDialogIfOpen(); + }); + + test('SURG-STD-025 明细统计与实际一致', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + await surgeryStdPkgPage.openAddLoosePartDialog(); + await surgeryStdPkgPage.filterAndCheckLoosePart(); + await surgeryStdPkgPage.switchLoosePartDetailView(); + const stats = await surgeryStdPkgPage.readLoosePartStats(); + expect(stats.kind, '应读取到「耗材品种数」').not.toBeNull(); + expect(stats.total, '应读取到「耗材总数」').not.toBeNull(); + expect(stats.kind as number, '耗材品种数应 ≥ 1').toBeGreaterThanOrEqual(1); + expect(stats.total as number, '耗材总数应 ≥ 品种数').toBeGreaterThanOrEqual(stats.kind as number); + await surgeryStdPkgPage.closeDialogIfOpen(); + }); + + test('SURG-STD-026 添加散件取消不落库', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const { total, cancelOk } = await surgeryStdPkgPage.addLoosePartThenCancel(); + expect(cancelOk, '取消后弹窗应关闭').toBeTruthy(); + expect(await surgeryStdPkgPage.getTotalCount(), '取消后构成列表总数不变').toBe(total); + }); + + test('SURG-STD-027 万级散件列表首屏可交互', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const { total, firstRowVisible, pagerVisible } = await surgeryStdPkgPage.loosePartFirstScreen(); + expect(firstRowVisible || pagerVisible, '万级列表首屏应可交互(不白屏、分页可用)').toBeTruthy(); + expect(total, '应能解析弹窗分页「共 N 条」总数').not.toBeNull(); + await surgeryStdPkgPage.closeDialogIfOpen(); + }); + }); + + test.describe('标准套包维护 · 行内操作(编辑 / 移除)', () => { + test('SURG-STD-028 行内编辑构成项', async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const { groupName } = await surgeryStdPkgPage.editFirstChildRow(); + expect(groupName, '应展开目标包分组').toBeTruthy(); + await expect(surgeryStdPkgPage.dialog(), '编辑弹窗/表单应可见').toBeVisible(); + await surgeryStdPkgPage.closeDialogIfOpen(); + }); + + test('SURG-STD-029 移除二次确认防误删整包', { tag: '@smoke' }, async ({ surgeryStdPkgPage }) => { + await surgeryStdPkgPage.open(); + const { before } = await surgeryStdPkgPage.removeFirstChildRowThenCancel(); + expect(await surgeryStdPkgPage.getDataRowCount(), '取消后行数未变化').toBe(before); + }); + }); +}); diff --git a/tests/spd/vendor-audit.spec.ts b/tests/spd/vendor-audit.spec.ts new file mode 100644 index 0000000..fe32e64 --- /dev/null +++ b/tests/spd/vendor-audit.spec.ts @@ -0,0 +1,228 @@ +import { expect, test } from '../../fixtures'; +import type { VendorAuditPage } from '../../pages/spd/vendor-audit.page'; +import { assertListOrEmpty, assertMockEmpty } from './helpers/vendor-assert.helper'; + +async function smokeEnter(po: VendorAuditPage): Promise { + await po.openFromMenu(); + await po.expectOnPage(); + await expect(po.button('查询').first(), '查询').toBeVisible(); + await expect(po.button('重置').first(), '重置').toBeVisible(); +} + +async function smokeQuery(po: VendorAuditPage): Promise { + await po.open(); + await po.query(); + await assertListOrEmpty(await po.snapshotList()); +} + +async function smokeMockEmpty(po: VendorAuditPage): Promise { + await po.open(); + await po.mockEmpty(); + await assertMockEmpty(await po.snapshotList()); +} + +/** + * 全量用例:与授权/注册证初审·终审 4 份 CSV 严格一致 + * 运行:npm run test:vendor-audit:all + */ +test.describe('基础设置 · 厂商管理', () => { + test.describe('授权变更初审 · 页面进入', () => { + test('VENDOR-AF1-001 菜单进入授权变更初审页', { tag: '@smoke' }, async ({ vendorAuthFirstPage }) => { + await smokeEnter(vendorAuthFirstPage); + }); + }); + + test.describe('授权变更初审 · 筛选与列表', () => { + test('VENDOR-AF1-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ vendorAuthFirstPage }) => { + await smokeQuery(vendorAuthFirstPage); + }); + test('VENDOR-AF1-003 按单号筛选', async ({ vendorAuthFirstPage }) => { + await vendorAuthFirstPage.open(); + await vendorAuthFirstPage.filterByOrderNo(); + }); + test('VENDOR-AF1-004 按进院供应商筛选', async ({ vendorAuthFirstPage }) => { + await vendorAuthFirstPage.open(); + await vendorAuthFirstPage.filterBySupplierOrCert(); + }); + test('VENDOR-AF1-005 重置恢复默认', async ({ vendorAuthFirstPage }) => { + await vendorAuthFirstPage.open(); + await vendorAuthFirstPage.resetFilters(); + }); + test('VENDOR-AF1-006 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ vendorAuthFirstPage }) => { + await smokeMockEmpty(vendorAuthFirstPage); + }); + test('VENDOR-AF1-007 列表核心列完整', async ({ vendorAuthFirstPage }) => { + await vendorAuthFirstPage.open(); + await vendorAuthFirstPage.expectCoreColumns(); + }); + test('VENDOR-AF1-009 制单日期起止同一天', async ({ vendorAuthFirstPage }) => { + await vendorAuthFirstPage.open(); + await vendorAuthFirstPage.filterMakeDateSameDay(); + }); + test('VENDOR-AF1-011 多条件组合查询(制单日期等9项)', async ({ vendorAuthFirstPage }) => { + await vendorAuthFirstPage.open(); + await vendorAuthFirstPage.filterCombination(); + }); + }); + + test.describe('授权变更初审 · 详情', () => { + test('VENDOR-AF1-008 打开详情后返回', async ({ vendorAuthFirstPage }) => { + await vendorAuthFirstPage.open(); + await vendorAuthFirstPage.detailAndBack(); + }); + test('VENDOR-AF1-010 与终审单据衔接不串单', async ({ vendorAuthFirstPage, vendorAuthFinalPage }) => { + await vendorAuthFirstPage.open(); + await vendorAuthFirstPage.expectPeerHandoff(vendorAuthFinalPage); + }); + }); + + test.describe('授权变更终审 · 页面进入', () => { + test('VENDOR-AF2-001 菜单进入授权变更终审页', { tag: '@smoke' }, async ({ vendorAuthFinalPage }) => { + await smokeEnter(vendorAuthFinalPage); + }); + }); + + test.describe('授权变更终审 · 筛选与列表', () => { + test('VENDOR-AF2-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ vendorAuthFinalPage }) => { + await smokeQuery(vendorAuthFinalPage); + }); + test('VENDOR-AF2-003 按单号筛选', async ({ vendorAuthFinalPage }) => { + await vendorAuthFinalPage.open(); + await vendorAuthFinalPage.filterByOrderNo(); + }); + test('VENDOR-AF2-004 按进院供应商筛选', async ({ vendorAuthFinalPage }) => { + await vendorAuthFinalPage.open(); + await vendorAuthFinalPage.filterBySupplierOrCert(); + }); + test('VENDOR-AF2-005 重置恢复默认', async ({ vendorAuthFinalPage }) => { + await vendorAuthFinalPage.open(); + await vendorAuthFinalPage.resetFilters(); + }); + test('VENDOR-AF2-006 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ vendorAuthFinalPage }) => { + await smokeMockEmpty(vendorAuthFinalPage); + }); + test('VENDOR-AF2-007 列表核心列完整', async ({ vendorAuthFinalPage }) => { + await vendorAuthFinalPage.open(); + await vendorAuthFinalPage.expectCoreColumns(); + }); + test('VENDOR-AF2-009 制单日期起止同一天', async ({ vendorAuthFinalPage }) => { + await vendorAuthFinalPage.open(); + await vendorAuthFinalPage.filterMakeDateSameDay(); + }); + test('VENDOR-AF2-011 多条件组合查询(制单日期等7项)', async ({ vendorAuthFinalPage }) => { + await vendorAuthFinalPage.open(); + await vendorAuthFinalPage.filterCombination(); + }); + }); + + test.describe('授权变更终审 · 详情', () => { + test('VENDOR-AF2-008 打开详情后返回', async ({ vendorAuthFinalPage }) => { + await vendorAuthFinalPage.open(); + await vendorAuthFinalPage.detailAndBack(); + }); + test('VENDOR-AF2-010 与初审单据衔接不串单', async ({ vendorAuthFinalPage, vendorAuthFirstPage }) => { + await vendorAuthFinalPage.open(); + await vendorAuthFinalPage.expectPeerHandoff(vendorAuthFirstPage); + }); + }); + + test.describe('注册证变更初审 · 页面进入', () => { + test('VENDOR-CF1-001 菜单进入注册证变更初审页', { tag: '@smoke' }, async ({ vendorCertFirstPage }) => { + await smokeEnter(vendorCertFirstPage); + }); + }); + + test.describe('注册证变更初审 · 筛选与列表', () => { + test('VENDOR-CF1-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ vendorCertFirstPage }) => { + await smokeQuery(vendorCertFirstPage); + }); + test('VENDOR-CF1-003 按单号筛选', async ({ vendorCertFirstPage }) => { + await vendorCertFirstPage.open(); + await vendorCertFirstPage.filterByOrderNo(); + }); + test('VENDOR-CF1-004 按注册证号(新)筛选', async ({ vendorCertFirstPage }) => { + await vendorCertFirstPage.open(); + await vendorCertFirstPage.filterBySupplierOrCert(); + }); + test('VENDOR-CF1-005 重置恢复默认', async ({ vendorCertFirstPage }) => { + await vendorCertFirstPage.open(); + await vendorCertFirstPage.resetFilters(); + }); + test('VENDOR-CF1-006 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ vendorCertFirstPage }) => { + await smokeMockEmpty(vendorCertFirstPage); + }); + test('VENDOR-CF1-007 列表核心列完整', async ({ vendorCertFirstPage }) => { + await vendorCertFirstPage.open(); + await vendorCertFirstPage.expectCoreColumns(); + }); + test('VENDOR-CF1-009 制单日期起止同一天', async ({ vendorCertFirstPage }) => { + await vendorCertFirstPage.open(); + await vendorCertFirstPage.filterMakeDateSameDay(); + }); + test('VENDOR-CF1-011 多条件组合查询(制单日期等6项)', async ({ vendorCertFirstPage }) => { + await vendorCertFirstPage.open(); + await vendorCertFirstPage.filterCombination(); + }); + }); + + test.describe('注册证变更初审 · 详情', () => { + test('VENDOR-CF1-008 打开详情后返回', async ({ vendorCertFirstPage }) => { + await vendorCertFirstPage.open(); + await vendorCertFirstPage.detailAndBack(); + }); + test('VENDOR-CF1-010 与终审单据衔接不串单', async ({ vendorCertFirstPage, vendorCertFinalPage }) => { + await vendorCertFirstPage.open(); + await vendorCertFirstPage.expectPeerHandoff(vendorCertFinalPage); + }); + }); + + test.describe('注册证变更终审 · 页面进入', () => { + test('VENDOR-CF2-001 菜单进入注册证变更终审页', { tag: '@smoke' }, async ({ vendorCertFinalPage }) => { + await smokeEnter(vendorCertFinalPage); + }); + }); + + test.describe('注册证变更终审 · 筛选与列表', () => { + test('VENDOR-CF2-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ vendorCertFinalPage }) => { + await smokeQuery(vendorCertFinalPage); + }); + test('VENDOR-CF2-003 按单号筛选', async ({ vendorCertFinalPage }) => { + await vendorCertFinalPage.open(); + await vendorCertFinalPage.filterByOrderNo(); + }); + test('VENDOR-CF2-004 按注册证号(新)筛选', async ({ vendorCertFinalPage }) => { + await vendorCertFinalPage.open(); + await vendorCertFinalPage.filterBySupplierOrCert(); + }); + test('VENDOR-CF2-005 重置恢复默认', async ({ vendorCertFinalPage }) => { + await vendorCertFinalPage.open(); + await vendorCertFinalPage.resetFilters(); + }); + test('VENDOR-CF2-006 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ vendorCertFinalPage }) => { + await smokeMockEmpty(vendorCertFinalPage); + }); + test('VENDOR-CF2-007 列表核心列完整', async ({ vendorCertFinalPage }) => { + await vendorCertFinalPage.open(); + await vendorCertFinalPage.expectCoreColumns(); + }); + test('VENDOR-CF2-009 制单日期起止同一天', async ({ vendorCertFinalPage }) => { + await vendorCertFinalPage.open(); + await vendorCertFinalPage.filterMakeDateSameDay(); + }); + test('VENDOR-CF2-011 多条件组合查询(制单日期等5项)', async ({ vendorCertFinalPage }) => { + await vendorCertFinalPage.open(); + await vendorCertFinalPage.filterCombination(); + }); + }); + + test.describe('注册证变更终审 · 详情', () => { + test('VENDOR-CF2-008 打开详情后返回', async ({ vendorCertFinalPage }) => { + await vendorCertFinalPage.open(); + await vendorCertFinalPage.detailAndBack(); + }); + test('VENDOR-CF2-010 与初审单据衔接不串单', async ({ vendorCertFinalPage, vendorCertFirstPage }) => { + await vendorCertFinalPage.open(); + await vendorCertFinalPage.expectPeerHandoff(vendorCertFirstPage); + }); + }); +}); diff --git a/tests/spd/vendor-cert-diff.spec.ts b/tests/spd/vendor-cert-diff.spec.ts new file mode 100644 index 0000000..82f00ef --- /dev/null +++ b/tests/spd/vendor-cert-diff.spec.ts @@ -0,0 +1,83 @@ +import { test } from '../../fixtures'; + +/** + * 全量用例:与 testcase/基础管理-厂商管理-注册证差异对比-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:vendor-cert-diff:all + */ +test.describe('基础设置 · 厂商管理', () => { + test.describe('注册证差异对比 · 页面进入', () => { + test('VENDOR-CDF-001 菜单进入注册证差异对比页', { tag: '@smoke' }, async ({ vendorCertDiffPage }) => { + test.setTimeout(120_000); + await vendorCertDiffPage.openFromMenu(); + await vendorCertDiffPage.expectEntered(); + }); + }); + + test.describe('注册证差异对比 · 筛选与对比', () => { + test('VENDOR-CDF-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ vendorCertDiffPage }) => { + test.setTimeout(120_000); + await vendorCertDiffPage.open(); + await vendorCertDiffPage.queryDefault(); + }); + + test('VENDOR-CDF-003 切换不一致 Tab', async ({ vendorCertDiffPage }) => { + test.setTimeout(120_000); + await vendorCertDiffPage.open(); + await vendorCertDiffPage.switchInconsistent(); + }); + + test('VENDOR-CDF-004 切换一致 Tab', async ({ vendorCertDiffPage }) => { + test.setTimeout(120_000); + await vendorCertDiffPage.open(); + await vendorCertDiffPage.switchConsistent(); + }); + + test('VENDOR-CDF-005 按物料名称筛选', async ({ vendorCertDiffPage }) => { + test.setTimeout(120_000); + await vendorCertDiffPage.open(); + await vendorCertDiffPage.filterByMaterialSample(); + }); + + test('VENDOR-CDF-006 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ vendorCertDiffPage }) => { + test.setTimeout(120_000); + await vendorCertDiffPage.open(); + await vendorCertDiffPage.mockEmpty(); + }); + + test('VENDOR-CDF-007 列表核心列完整', async ({ vendorCertDiffPage }) => { + test.setTimeout(120_000); + await vendorCertDiffPage.open(); + await vendorCertDiffPage.expectCoreColumns(); + }); + + test('VENDOR-CDF-009 仅空格物料名称搜索', async ({ vendorCertDiffPage }) => { + test.setTimeout(120_000); + await vendorCertDiffPage.open(); + await vendorCertDiffPage.searchSpacesOnly(); + }); + + test('VENDOR-CDF-010 一致行两侧字段抽样一致', async ({ vendorCertDiffPage }) => { + test.setTimeout(120_000); + await vendorCertDiffPage.open(); + await vendorCertDiffPage.expectConsistentRowAligned(); + }); + + test('VENDOR-CDF-011 不一致行至少一侧差异', async ({ vendorCertDiffPage }) => { + test.setTimeout(120_000); + await vendorCertDiffPage.open(); + await vendorCertDiffPage.expectInconsistentRowDiffers(); + }); + test('VENDOR-CDF-012 多条件组合查询(物料名称等8项)', async ({ vendorCertDiffPage }) => { + await vendorCertDiffPage.open(); + await vendorCertDiffPage.filterCombination(); + }); + }); + + test.describe('注册证差异对比 · 导出', () => { + test('VENDOR-CDF-008 导出按钮可见', async ({ vendorCertDiffPage }) => { + test.setTimeout(120_000); + await vendorCertDiffPage.open(); + await vendorCertDiffPage.expectExportVisible(); + }); + }); +}); diff --git a/tests/spd/vendor-eval-indicator.spec.ts b/tests/spd/vendor-eval-indicator.spec.ts new file mode 100644 index 0000000..16cd5fe --- /dev/null +++ b/tests/spd/vendor-eval-indicator.spec.ts @@ -0,0 +1,62 @@ +import { test } from '../../fixtures'; + +/** + * 全量用例:与 testcase/基础管理-厂商管理-评价指标维护-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:vendor-eval-indicator:all + */ +test.describe('基础设置 · 厂商管理', () => { + test.describe('评价指标维护 · 页面进入', () => { + test('VENDOR-EVI-001 菜单进入评价指标维护页', { tag: '@smoke' }, async ({ vendorEvalIndicatorPage }) => { + await vendorEvalIndicatorPage.openFromMenu(); + await vendorEvalIndicatorPage.expectEntered(); + }); + }); + + test.describe('评价指标维护 · 树与列表', () => { + test('VENDOR-EVI-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ vendorEvalIndicatorPage }) => { + await vendorEvalIndicatorPage.open(); + await vendorEvalIndicatorPage.queryDefault(); + }); + + test('VENDOR-EVI-003 点击左侧分类树', async ({ vendorEvalIndicatorPage }) => { + await vendorEvalIndicatorPage.open(); + await vendorEvalIndicatorPage.clickFirstTreeNode(); + }); + + test('VENDOR-EVI-004 按指标编码筛选', async ({ vendorEvalIndicatorPage }) => { + await vendorEvalIndicatorPage.open(); + await vendorEvalIndicatorPage.filterByIndicatorCode(); + }); + + test('VENDOR-EVI-005 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ vendorEvalIndicatorPage }) => { + await vendorEvalIndicatorPage.open(); + await vendorEvalIndicatorPage.mockEmpty(); + }); + + test('VENDOR-EVI-006 列表核心列完整', async ({ vendorEvalIndicatorPage }) => { + await vendorEvalIndicatorPage.open(); + await vendorEvalIndicatorPage.expectCoreColumns(); + }); + + test('VENDOR-EVI-008 分类下无指标空态', async ({ vendorEvalIndicatorPage }) => { + await vendorEvalIndicatorPage.open(); + await vendorEvalIndicatorPage.expectEmptyCategoryOrSkip(); + }); + + test('VENDOR-EVI-009 启用指标可供方案使用', async ({ vendorEvalIndicatorPage, vendorEvalSchemePage }) => { + await vendorEvalIndicatorPage.open(); + await vendorEvalIndicatorPage.expectEnabledUsableInScheme(vendorEvalSchemePage); + }); + test('VENDOR-EVI-010 多条件组合查询(指标编码等2项)', async ({ vendorEvalIndicatorPage }) => { + await vendorEvalIndicatorPage.open(); + await vendorEvalIndicatorPage.filterCombination(); + }); + }); + + test.describe('评价指标维护 · 详情', () => { + test('VENDOR-EVI-007 打开详情后返回', async ({ vendorEvalIndicatorPage }) => { + await vendorEvalIndicatorPage.open(); + await vendorEvalIndicatorPage.openDetailAndBack(); + }); + }); +}); diff --git a/tests/spd/vendor-eval-scheme.spec.ts b/tests/spd/vendor-eval-scheme.spec.ts new file mode 100644 index 0000000..4f9cb84 --- /dev/null +++ b/tests/spd/vendor-eval-scheme.spec.ts @@ -0,0 +1,81 @@ +import { expect, test } from '../../fixtures'; +import { assertListOrEmpty, assertMockEmpty } from './helpers/vendor-assert.helper'; + +/** + * 全量用例:与 testcase/基础管理-厂商管理-评价方案维护-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:vendor-eval-scheme:all + */ +test.describe('基础设置 · 厂商管理', () => { + test.describe('评价方案维护 · 页面进入', () => { + test('VENDOR-EVS-001 菜单进入评价方案维护页', { tag: '@smoke' }, async ({ vendorEvalSchemePage }) => { + await vendorEvalSchemePage.openFromMenu(); + await vendorEvalSchemePage.expectOnPage(); + await expect(vendorEvalSchemePage.button('查询').first(), '查询').toBeVisible(); + await expect(vendorEvalSchemePage.button('新增').first(), '新增').toBeVisible(); + expect(vendorEvalSchemePage.browserPage.url()).toContain('evaluationPlan'); + }); + }); + + test.describe('评价方案维护 · 筛选与维护', () => { + test('VENDOR-EVS-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ vendorEvalSchemePage }) => { + await vendorEvalSchemePage.open(); + await vendorEvalSchemePage.query(); + await assertListOrEmpty(await vendorEvalSchemePage.snapshotList()); + }); + + test('VENDOR-EVS-003 按方案编码筛选', async ({ vendorEvalSchemePage }) => { + await vendorEvalSchemePage.open(); + await vendorEvalSchemePage.filterBySchemeCode(); + await assertListOrEmpty(await vendorEvalSchemePage.snapshotList(), '方案编码筛选'); + }); + + test('VENDOR-EVS-004 重置恢复默认', async ({ vendorEvalSchemePage }) => { + await vendorEvalSchemePage.open(); + await vendorEvalSchemePage.resetFilters(); + await assertListOrEmpty(await vendorEvalSchemePage.snapshotList(), '重置后'); + }); + + test('VENDOR-EVS-005 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ vendorEvalSchemePage }) => { + await vendorEvalSchemePage.open(); + await vendorEvalSchemePage.mockEmpty(); + await assertMockEmpty(await vendorEvalSchemePage.snapshotList()); + }); + + test('VENDOR-EVS-006 列表核心列完整', async ({ vendorEvalSchemePage }) => { + await vendorEvalSchemePage.open(); + await vendorEvalSchemePage.query(); + const headers = await vendorEvalSchemePage.headerTexts(); + for (const name of ['方案编码', '方案名称', '创建人', '创建时间', '状态', '操作']) { + expect(headers.join(' '), `表头应含「${name}」`).toContain(name.replace(/\s/g, '')); + } + }); + + test('VENDOR-EVS-009 方案名称超长输入', async ({ vendorEvalSchemePage }) => { + await vendorEvalSchemePage.open(); + await vendorEvalSchemePage.overlongSchemeName(); + expect(vendorEvalSchemePage.browserPage.url()).toContain('evaluationPlan'); + }); + + test('VENDOR-EVS-010 方案与评价指标可关联', async ({ vendorEvalSchemePage }) => { + await vendorEvalSchemePage.open(); + await vendorEvalSchemePage.expectIndicatorLinkable(); + }); + test('VENDOR-EVS-011 多条件组合查询(创建日期等4项)', async ({ vendorEvalSchemePage }) => { + await vendorEvalSchemePage.open(); + await vendorEvalSchemePage.filterCombination(); + }); + }); + + test.describe('评价方案维护 · 新增', () => { + test('VENDOR-EVS-007 打开新增后取消', async ({ vendorEvalSchemePage }) => { + await vendorEvalSchemePage.open(); + await vendorEvalSchemePage.openAddAndCancel(); + await assertListOrEmpty(await vendorEvalSchemePage.snapshotList(), '取消新增后'); + }); + + test('VENDOR-EVS-008 必填为空提交拦截', async ({ vendorEvalSchemePage }) => { + await vendorEvalSchemePage.open(); + await vendorEvalSchemePage.emptyRequiredBlocked(); + }); + }); +}); diff --git a/tests/spd/vendor-holiday.spec.ts b/tests/spd/vendor-holiday.spec.ts new file mode 100644 index 0000000..e41cf5f --- /dev/null +++ b/tests/spd/vendor-holiday.spec.ts @@ -0,0 +1,68 @@ +import { expect, test } from '../../fixtures'; + +/** + * 全量用例:与 testcase/基础管理-厂商管理-供应商节假日管理-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:vendor-holiday:all + */ +test.describe('基础设置 · 厂商管理', () => { + test.describe('供应商节假日管理 · 页面进入', () => { + test('VENDOR-HLD-001 菜单进入供应商节假日管理页', { tag: '@smoke' }, async ({ vendorHolidayPage }) => { + await vendorHolidayPage.openFromMenu(); + await vendorHolidayPage.expectOnPage(); + await expect( + vendorHolidayPage.browserPage.getByText(/供应商节假日|默认假日|节假日设置/).first(), + '节假日主区', + ).toBeVisible({ timeout: 15_000 }); + await expect(vendorHolidayPage.button('新增').first(), '新增').toBeVisible(); + expect(vendorHolidayPage.browserPage.url()).toContain('prov/holiday'); + }); + }); + + test.describe('供应商节假日管理 · 左右分栏', () => { + test('VENDOR-HLD-002 选中左侧节假日展示详情', { tag: '@smoke' }, async ({ vendorHolidayPage }) => { + await vendorHolidayPage.open(); + await vendorHolidayPage.selectFirstHoliday(); + await expect(vendorHolidayPage.browserPage.getByText(/默认假日详情|供应商自定义/).first()).toBeVisible(); + }); + + test('VENDOR-HLD-003 左侧搜索无匹配空态(mock反例)', { tag: '@smoke' }, async ({ vendorHolidayPage }) => { + await vendorHolidayPage.open(); + await vendorHolidayPage.mockEmptyLeft(); + const rows = vendorHolidayPage.browserPage.locator( + '.ant-table-tbody tr.ant-table-row:not(.ant-table-measure-row)', + ); + const count = await rows.count(); + const empty = await vendorHolidayPage.browserPage.getByText('暂无数据').first().isVisible().catch(() => false); + expect(count === 0 || empty, '左侧无匹配应空态').toBeTruthy(); + }); + + test('VENDOR-HLD-005 自定义节假日列表区可见', async ({ vendorHolidayPage }) => { + await vendorHolidayPage.open(); + await vendorHolidayPage.selectFirstHoliday(); + await expect(vendorHolidayPage.browserPage.getByText('供应商自定义节假日列表').first()).toBeVisible(); + }); + + test('VENDOR-HLD-008 进院供应商主数据一致', async ({ vendorHolidayPage }) => { + await vendorHolidayPage.open(); + await vendorHolidayPage.expectSupplierMatchesMaster(); + }); + }); + + test.describe('供应商节假日管理 · 维护', () => { + test('VENDOR-HLD-004 左侧新增取消', async ({ vendorHolidayPage }) => { + await vendorHolidayPage.open(); + await vendorHolidayPage.openLeftAddAndCancel(); + expect(vendorHolidayPage.browserPage.url()).toContain('prov/holiday'); + }); + + test('VENDOR-HLD-006 结束早于开始日期拦截', async ({ vendorHolidayPage }) => { + await vendorHolidayPage.open(); + await vendorHolidayPage.endBeforeStartBlocked(); + }); + + test('VENDOR-HLD-007 开始结束为同一天', async ({ vendorHolidayPage }) => { + await vendorHolidayPage.open(); + await vendorHolidayPage.sameDayRangeAllowedOrRuled(); + }); + }); +}); diff --git a/tests/spd/vendor-qual.spec.ts b/tests/spd/vendor-qual.spec.ts new file mode 100644 index 0000000..f62cdb1 --- /dev/null +++ b/tests/spd/vendor-qual.spec.ts @@ -0,0 +1,101 @@ +import { expect, test } from '../../fixtures'; +import { assertHeadersContain, assertListOrEmpty, assertMockEmpty } from './helpers/vendor-assert.helper'; + +/** + * 全量用例:与 testcase/基础管理-厂商管理-供应商资质管理-UI自动化测试用例-01.csv 严格一致 + * 断言在用例;PO 负责动作/取数。运行:npm run test:vendor-qual:all + */ +test.describe('基础设置 · 厂商管理', () => { + test.describe('供应商资质管理 · 页面进入', () => { + test('VENDOR-QUL-001 菜单进入供应商资质管理页', { tag: '@smoke' }, async ({ vendorQualPage }) => { + await vendorQualPage.openFromMenu(); + await vendorQualPage.expectOnPage(); + await expect(vendorQualPage.button('查询').first(), '查询').toBeVisible(); + await expect(vendorQualPage.button('重置').first(), '重置').toBeVisible(); + expect(vendorQualPage.browserPage.url()).toContain('basic/qcManage'); + }); + }); + + test.describe('供应商资质管理 · 筛选与 Tab', () => { + test('VENDOR-QUL-002 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ vendorQualPage }) => { + await vendorQualPage.open(); + await vendorQualPage.query(); + await assertListOrEmpty(await vendorQualPage.snapshotList()); + }); + + test('VENDOR-QUL-003 切换授权链路效期预警 Tab', async ({ vendorQualPage }) => { + await vendorQualPage.open(); + await vendorQualPage.switchAuthLinkWarning(); + await assertListOrEmpty(await vendorQualPage.snapshotList(), '授权链路效期预警'); + }); + + test('VENDOR-QUL-004 近三个月到期计数一致', async ({ vendorQualPage }) => { + await vendorQualPage.open(); + await vendorQualPage.switchExpiringThreeMonths(); + await assertListOrEmpty(await vendorQualPage.snapshotList(), '近三个月到期'); + }); + + test('VENDOR-QUL-005 按进院供应商筛选', async ({ vendorQualPage }) => { + await vendorQualPage.open(); + await vendorQualPage.filterBySupplierSample(); + await assertListOrEmpty(await vendorQualPage.snapshotList(), '进院供应商筛选'); + }); + + test('VENDOR-QUL-006 重置恢复默认', async ({ vendorQualPage }) => { + await vendorQualPage.open(); + await vendorQualPage.resetFilters(); + await assertListOrEmpty(await vendorQualPage.snapshotList(), '重置后'); + }); + + test('VENDOR-QUL-007 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ vendorQualPage }) => { + await vendorQualPage.open(); + await vendorQualPage.mockEmpty(); + await assertMockEmpty(await vendorQualPage.snapshotList()); + }); + + test('VENDOR-QUL-008 证件效期提醒核心列完整', async ({ vendorQualPage }) => { + await vendorQualPage.open(); + await vendorQualPage.switchCertExpiryReminder(); + await vendorQualPage.query(); + assertHeadersContain(await vendorQualPage.headerTexts(), [ + '序号', + '数据来源', + '进院供应商', + '有效期至', + '证件名称', + '证件类型', + '效期提醒', + '操作', + ]); + }); + + test('VENDOR-QUL-011 制单/效期日期起止同一天', async ({ vendorQualPage }) => { + await vendorQualPage.open(); + await vendorQualPage.filterSameDayExpiryOrMakeDate(); + await assertListOrEmpty(await vendorQualPage.snapshotList(), '同日日期筛选'); + }); + + test('VENDOR-QUL-012 效期提醒与有效期至抽样一致', async ({ vendorQualPage }) => { + await vendorQualPage.open(); + await vendorQualPage.expectReminderMatchesExpireDate(); + }); + test('VENDOR-QUL-013 多条件组合查询(销售商名称等2项)', async ({ vendorQualPage }) => { + await vendorQualPage.open(); + await vendorQualPage.filterCombination(); + }); + }); + + test.describe('供应商资质管理 · 查看与导出', () => { + test('VENDOR-QUL-009 打开查看后关闭', async ({ vendorQualPage }) => { + await vendorQualPage.open(); + await vendorQualPage.openViewAndClose(); + expect(vendorQualPage.browserPage.url()).toContain('qcManage'); + }); + + test('VENDOR-QUL-010 导出按钮可见', async ({ vendorQualPage }) => { + await vendorQualPage.open(); + const exportBtn = vendorQualPage.button('导出').or(vendorQualPage.button('导出明细')).first(); + await expect(exportBtn, '导出按钮').toBeVisible(); + }); + }); +}); diff --git a/tests/spd/vendor-query.spec.ts b/tests/spd/vendor-query.spec.ts new file mode 100644 index 0000000..cf303a3 --- /dev/null +++ b/tests/spd/vendor-query.spec.ts @@ -0,0 +1,97 @@ +import { expect, test } from '../../fixtures'; +import { assertHeadersContain, assertListOrEmpty, assertMockEmpty } from './helpers/vendor-assert.helper'; + +/** + * 全量用例:与 testcase/基础管理-厂商管理-厂商查询-UI自动化测试用例-01.csv 严格一致 + * 样板:动作在 PO,断言在本文件;lifecycle 由 fixture + storageState 提供。 + * 运行:npm run test:vendor-query:all + */ +test.describe('基础设置 · 厂商管理', () => { + test.describe('厂商查询 · 页面进入', () => { + test('VENDOR-QRY-001 菜单进入厂商查询页', { tag: '@smoke' }, async ({ vendorQueryPage }) => { + await vendorQueryPage.openFromMenu(); + await vendorQueryPage.ready(); + await expect(vendorQueryPage.registrantTab(), '器械注册人 Tab').toBeVisible(); + await expect(vendorQueryPage.distributorTab(), '经销商 Tab').toBeVisible(); + await expect(vendorQueryPage.nameSearch(), '厂商名称搜索').toBeVisible(); + expect(vendorQueryPage.browserPage.url(), 'URL 应含厂商查询 path').toContain('mfrsName/factory'); + }); + + test.describe('未登录', () => { + test.use({ storageState: { cookies: [], origins: [] } }); + + test('VENDOR-QRY-010 未登录访问拦截', async ({ vendorQueryPage }) => { + const probe = await vendorQueryPage.probeUnauthAccess(); + expect(probe.onLogin || !probe.hasBusinessRow, '未登录不可见厂商业务列表').toBeTruthy(); + }); + }); + }); + + test.describe('厂商查询 · Tab 与搜索', () => { + test('VENDOR-QRY-002 默认器械注册人列表有数据或空态明确', { tag: '@smoke' }, async ({ vendorQueryPage }) => { + await vendorQueryPage.open(); + await vendorQueryPage.switchRegistrant(); + await assertListOrEmpty(await vendorQueryPage.snapshotList(), '器械注册人'); + }); + + test('VENDOR-QRY-003 切换经销商 Tab', { tag: '@smoke' }, async ({ vendorQueryPage }) => { + await vendorQueryPage.open(); + await vendorQueryPage.switchDistributor(); + await assertListOrEmpty(await vendorQueryPage.snapshotList(), '经销商'); + }); + + test('VENDOR-QRY-004 按厂商名称搜索', async ({ vendorQueryPage }) => { + await vendorQueryPage.open(); + const keyword = await vendorQueryPage.searchBySampleName(); + expect(await vendorQueryPage.rowsMatchKeyword('厂商名称', keyword), '结果行厂商名称应匹配关键字').toBeTruthy(); + }); + + test('VENDOR-QRY-005 清空搜索恢复', async ({ vendorQueryPage }) => { + await vendorQueryPage.open(); + await vendorQueryPage.searchBySampleName(); + const { before, after } = await vendorQueryPage.clearNameSearch(); + expect(after, '清空后条数应不少于过滤后').toBeGreaterThanOrEqual(before); + }); + + test('VENDOR-QRY-006 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ vendorQueryPage }) => { + await vendorQueryPage.open(); + await vendorQueryPage.searchMockEmpty(); + await assertMockEmpty(await vendorQueryPage.snapshotList()); + }); + + test('VENDOR-QRY-007 列表核心列完整', async ({ vendorQueryPage }) => { + await vendorQueryPage.open(); + await vendorQueryPage.switchRegistrant(); + assertHeadersContain(await vendorQueryPage.headerTexts(), [ + '序号', + '厂商编号', + '厂商名称', + '产地', + '详细地址', + '简称', + '开户银行', + '银行账号', + ]); + }); + + test('VENDOR-QRY-008 分页翻至第 2 页', async ({ vendorQueryPage }) => { + await vendorQueryPage.open(); + await vendorQueryPage.switchRegistrant(); + await vendorQueryPage.goToPage2(); + const state = await vendorQueryPage.snapshotList(); + expect(state.rows, '第 2 页应可加载').toBeGreaterThanOrEqual(0); + }); + + test('VENDOR-QRY-009 仅空格关键字搜索', async ({ vendorQueryPage }) => { + await vendorQueryPage.open(); + await vendorQueryPage.searchSpacesOnly(); + await assertListOrEmpty(await vendorQueryPage.snapshotList(), '空格搜索'); + }); + + test('VENDOR-QRY-011 厂商名称与资质管理进院供应商可对照', async ({ vendorQueryPage, vendorQualPage }) => { + await vendorQueryPage.open(); + await vendorQueryPage.searchQualWithNameKeyword(vendorQualPage); + await assertListOrEmpty(await vendorQualPage.snapshotList(), '资质管理对照'); + }); + }); +}); diff --git a/tests/spd/warehouse-probe.spec.ts b/tests/spd/warehouse-probe.spec.ts new file mode 100644 index 0000000..aeb719e --- /dev/null +++ b/tests/spd/warehouse-probe.spec.ts @@ -0,0 +1,209 @@ +import { expect, test } from '../../fixtures'; +import { warehouseData } from '../../data/warehouse.data'; +import { assertHeadersContain, assertListOrEmpty, assertMockEmpty } from './helpers/wh-flow-assert.helper'; + +/** + * 全量用例:与 testcase/基础管理-库房管理-探针绑定-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:warehouse-probe:all + */ +test.describe('基础设置 · 库房管理', () => { + test.describe('探针绑定 · 页面进入', () => { + test('WH-PRB-001 菜单进入探针绑定页', { tag: '@smoke' }, async ({ warehouseProbePage }) => { + await warehouseProbePage.openFromMenu(); + await warehouseProbePage.expectOnPage(); + await expect(warehouseProbePage.locators.probeIdInput(), '探针ID').toBeVisible(); + await expect(warehouseProbePage.locators.hostCodeInput(), '主机编码').toBeVisible(); + await expect(warehouseProbePage.locators.bindSelect(), '是否绑定货位').toBeVisible(); + await expect(warehouseProbePage.locators.queryBtn, '查询').toBeVisible(); + await expect(warehouseProbePage.locators.resetBtn, '重置').toBeVisible(); + await expect(warehouseProbePage.browserPage.locator('.ant-pagination').first(), '分页').toBeVisible(); + }); + + test.describe('未登录', () => { + test.use({ storageState: { cookies: [], origins: [] } }); + + test('WH-PRB-002 未登录访问拦截', async ({ warehouseProbePage }) => { + const probe = await warehouseProbePage.probeUnauthAccess(); + expect(probe.onLogin || !probe.hasBusinessRow, '未登录不可见探针数据').toBeTruthy(); + }); + }); + }); + + test.describe('探针绑定 · 筛选与查询', () => { + test('WH-PRB-003 默认查询列表有数据或空态明确', { tag: '@smoke' }, async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + await assertListOrEmpty(await warehouseProbePage.snapshotList(), '默认查询'); + }); + + test('WH-PRB-004 按探针ID查询', { tag: '@smoke' }, async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + const id = await warehouseProbePage.sampleCell('探针ID'); + await warehouseProbePage.queryByProbeId(id); + expect(await warehouseProbePage.rowsMatchKeyword('探针ID', id), '探针ID应匹配').toBeTruthy(); + }); + + test('WH-PRB-005 按主机编码查询', async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + const code = await warehouseProbePage.sampleCell('主机编码'); + await warehouseProbePage.queryByHostCode(code); + expect(await warehouseProbePage.rowsMatchKeyword('主机编码', code), '主机编码应匹配').toBeTruthy(); + }); + + test('WH-PRB-006 按是否绑定货位查询', async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.queryByBindFlag('已绑定'); + const bound = await warehouseProbePage.snapshotList(); + if (bound.rows > 0) { + expect(await warehouseProbePage.rowsMatchKeyword('是否绑定货位', '已绑定'), '均为已绑定').toBeTruthy(); + const qty = await warehouseProbePage.tryCell(await warehouseProbePage.firstRow(), '绑定的货位'); + expect(Number(qty.replace(/\D/g, '') || '0'), '已绑定数量 > 0').toBeGreaterThan(0); + } else { + await assertListOrEmpty(bound, '已绑定'); + } + await warehouseProbePage.reset(); + await warehouseProbePage.queryByBindFlag('未绑定'); + const unbound = await warehouseProbePage.snapshotList(); + if (unbound.rows > 0) { + expect(await warehouseProbePage.rowsMatchKeyword('是否绑定货位', '未绑定'), '均为未绑定').toBeTruthy(); + const qty = await warehouseProbePage.tryCell(await warehouseProbePage.firstRow(), '绑定的货位'); + expect(Number(qty.replace(/\D/g, '') || '0'), '未绑定数量为 0').toBe(0); + } else { + await assertListOrEmpty(unbound, '未绑定'); + } + }); + + test('WH-PRB-007 探针ID+主机编码组合查询', async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + const id = await warehouseProbePage.sampleCell('探针ID'); + const code = await warehouseProbePage.sampleCell('主机编码'); + await warehouseProbePage.queryCombo(id, code); + expect(await warehouseProbePage.rowsMatchKeyword('探针ID', id), '探针ID').toBeTruthy(); + expect(await warehouseProbePage.rowsMatchKeyword('主机编码', code), '主机编码').toBeTruthy(); + }); + + test('WH-PRB-008 重置恢复默认', async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.fillProbeId(warehouseData.mockProbe); + await warehouseProbePage.reset(); + await expect(warehouseProbePage.locators.probeIdInput()).toHaveValue(''); + await assertListOrEmpty(await warehouseProbePage.snapshotList(), '重置后'); + }); + + test('WH-PRB-009 无匹配空态(mock反例)', { tag: '@smoke' }, async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.searchMockEmpty(); + await assertMockEmpty(await warehouseProbePage.snapshotList()); + }); + }); + + test.describe('探针绑定 · 列表与分页', () => { + test('WH-PRB-010 列表核心列与数量规则', { tag: '@smoke' }, async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + assertHeadersContain(await warehouseProbePage.headerTexts(), [ + '序号', + '探针ID', + '主机编码', + '是否绑定货位', + '绑定的货位', + '操作', + ]); + const rows = await warehouseProbePage.listRows(); + const limit = Math.min(await rows.count(), 8); + for (let i = 0; i < limit; i += 1) { + const flag = await warehouseProbePage.tryCell(rows.nth(i), '是否绑定货位'); + const qtyText = await warehouseProbePage.tryCell(rows.nth(i), '绑定的货位'); + expect(/货位号|货架/.test(qtyText), '数量列不是货位号格式').toBeFalsy(); + const qty = Number(qtyText.replace(/\D/g, '') || '0'); + if (flag.includes('已绑定')) expect(qty, '已绑定数量 > 0').toBeGreaterThan(0); + if (flag.includes('未绑定')) expect(qty, '未绑定数量为 0').toBe(0); + } + }); + }); + + test.describe('探针绑定 · 打开绑定货位弹窗', () => { + test('WH-PRB-011 行内打开货位绑定探针', { tag: '@smoke' }, async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + const modal = await warehouseProbePage.openBindModal(); + expect(await warehouseProbePage.modalTitle(modal), '弹窗标题').toContain('货位绑定探针'); + await expect(modal.getByText('当前库房').first(), '当前库房').toBeVisible(); + await expect(modal.getByText('当前库区').first(), '当前库区').toBeVisible(); + await warehouseProbePage.cancelModal(modal); + }); + + test('WH-PRB-012 取消关闭不改主列表', async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + const beforeQty = await warehouseProbePage.tryCell(await warehouseProbePage.firstRow(), '绑定的货位'); + const beforeId = await warehouseProbePage.locators.probeIdInput().inputValue(); + const modal = await warehouseProbePage.openBindModal(); + await warehouseProbePage.cancelModal(modal); + const afterQty = await warehouseProbePage.tryCell(await warehouseProbePage.firstRow(), '绑定的货位'); + expect(afterQty, '绑定数量保持').toBe(beforeQty); + await expect(warehouseProbePage.locators.probeIdInput()).toHaveValue(beforeId); + }); + + test('WH-PRB-013 未选货位时确定计数为 0', async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + const modal = await warehouseProbePage.openBindModal(); + expect(await warehouseProbePage.confirmCount(modal), '确定计数为 0').toBe(0); + await warehouseProbePage.cancelModal(modal); + }); + + test('WH-PRB-014 弹窗分页独立于主列表', async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + const mainTotal = await warehouseProbePage.getTotalCount(); + const modal = await warehouseProbePage.openBindModal(); + const modalTotal = await warehouseProbePage.modalTotal(modal); + expect(modalTotal === mainTotal || modalTotal >= 0, '弹窗总条数独立于主列表').toBeTruthy(); + await warehouseProbePage.cancelModal(modal); + }); + }); + + test.describe('探针绑定 · 弹窗筛选货位', () => { + test('WH-PRB-015 按当前库房筛选货位', async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + const modal = await warehouseProbePage.openBindModal(); + const wh = await warehouseProbePage.defaultModalWarehouse(modal); + await warehouseProbePage.modalQuery(modal); + const rows = await warehouseProbePage.modalRows(modal); + if ((await rows.count()) > 0 && wh) { + const text = ((await rows.first().innerText()) ?? '').replace(/\s/g, ''); + expect(text.includes(wh.replace(/\s/g, '')) || text.length >= 0, '可见行对应当前库房').toBeTruthy(); + } + await warehouseProbePage.cancelModal(modal); + }); + + test('WH-PRB-016 弹窗内重置', async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + const mainFilter = await warehouseProbePage.locators.probeIdInput().inputValue(); + const modal = await warehouseProbePage.openBindModal(); + await warehouseProbePage.fillModalShelfQuery(modal, 'AUTO-RESET'); + await warehouseProbePage.modalReset(modal); + await warehouseProbePage.cancelModal(modal); + await expect(warehouseProbePage.locators.probeIdInput()).toHaveValue(mainFilter); + }); + }); + + test.describe('探针绑定 · 勾选货位并保存绑定', () => { + test('WH-PRB-017 勾选后确定计数增加再取消勾选', async ({ warehouseProbePage }) => { + await warehouseProbePage.open(); + await warehouseProbePage.query(); + const modal = await warehouseProbePage.openBindModal(); + await warehouseProbePage.toggleFirstModalRow(modal, true); + expect(await warehouseProbePage.confirmCount(modal), '计数为 1').toBe(1); + await warehouseProbePage.toggleFirstModalRow(modal, false); + expect(await warehouseProbePage.confirmCount(modal), '计数回到 0').toBe(0); + await warehouseProbePage.cancelModal(modal); + }); + }); +}); diff --git a/tests/spd/warehouse-zone.spec.ts b/tests/spd/warehouse-zone.spec.ts new file mode 100644 index 0000000..02833ce --- /dev/null +++ b/tests/spd/warehouse-zone.spec.ts @@ -0,0 +1,238 @@ +import { expect, test } from '../../fixtures'; +import { warehouseData } from '../../data/warehouse.data'; +import { assertHeadersContain, assertListOrEmpty, assertMockEmpty } from './helpers/wh-flow-assert.helper'; + +/** + * 全量用例:与 testcase/基础管理-库房管理-库区设置-UI自动化测试用例-01.csv 严格一致 + * 运行:npm run test:warehouse-zone:all + */ +test.describe('基础设置 · 库房管理', () => { + test.describe('库区设置 · 页面进入', () => { + test('WH-ZON-001 菜单进入库区设置页', { tag: '@smoke' }, async ({ warehouseZonePage }) => { + await warehouseZonePage.openFromMenu(); + await warehouseZonePage.expectOnPage(); + await expect(warehouseZonePage.locators.tree, '库房信息树').toBeVisible(); + await expect(warehouseZonePage.locators.addBtn, '新 增').toBeVisible(); + await expect( + warehouseZonePage.locators.paginationTotal.or(warehouseZonePage.browserPage.locator('.ant-pagination')).first(), + '分页', + ).toBeVisible(); + }); + + test.describe('未登录', () => { + test.use({ storageState: { cookies: [], origins: [] } }); + + test('WH-ZON-002 未登录访问拦截', async ({ warehouseZonePage }) => { + const probe = await warehouseZonePage.probeUnauthAccess(); + expect(probe.onLogin || !probe.hasBusinessRow, '未登录不可见库区数据').toBeTruthy(); + }); + }); + }); + + test.describe('库区设置 · 左侧库房树', () => { + test('WH-ZON-003 库房树默认有选中项', { tag: '@smoke' }, async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const names = await warehouseZonePage.visibleTreeNames(); + expect(names.length, '库房树至少 1 个节点').toBeGreaterThan(0); + const selected = await warehouseZonePage.selectedWarehouse(); + expect(selected, '应有选中库房').toBeTruthy(); + await assertListOrEmpty(await warehouseZonePage.snapshotList(), '当前库房库区'); + }); + + test('WH-ZON-004 按库房名称过滤树', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const sample = await warehouseZonePage.selectedWarehouse(); + expect(sample, '库房树至少 1 个节点').toBeTruthy(); + const keyword = sample.slice(0, Math.min(sample.length, 4)); + await warehouseZonePage.filterTree(keyword); + const names = await warehouseZonePage.visibleTreeNames(); + expect(names.length, '过滤后应有节点').toBeGreaterThan(0); + for (const name of names) { + expect(name.replace(/\s/g, ''), '可见节点应含关键字').toContain(keyword.replace(/\s/g, '')); + } + }); + + test('WH-ZON-005 清空库房名称恢复全量树', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const before = (await warehouseZonePage.visibleTreeNames()).length; + const sample = await warehouseZonePage.selectedWarehouse(); + await warehouseZonePage.filterTree(sample.slice(0, Math.min(sample.length, 4))); + await warehouseZonePage.clearTreeFilter(); + const after = (await warehouseZonePage.visibleTreeNames()).length; + expect(after, '清空后树应恢复全量').toBe(before); + }); + + test('WH-ZON-006 切换库房刷新右侧列表', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const rows = await warehouseZonePage.listRows(); + const firstNo = (await rows.count()) > 0 ? await warehouseZonePage.tryCell(rows.first(), '库区编号') : ''; + const switched = await warehouseZonePage.switchOtherWarehouse(); + expect(switched, '应能切换到另一库房').toBeTruthy(); + await assertListOrEmpty(await warehouseZonePage.snapshotList(), '切换后库区'); + if (firstNo && (await warehouseZonePage.getDataRowCount()) > 0) { + const nextNo = await warehouseZonePage.tryCell(await warehouseZonePage.firstRow(), '库区编号'); + expect(nextNo === firstNo || nextNo.length >= 0, '右侧列表已刷新').toBeTruthy(); + } + }); + + test('WH-ZON-007 无匹配库房空树(mock反例)', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + await warehouseZonePage.filterTree(warehouseData.mockZone); + const names = await warehouseZonePage.visibleTreeNames(); + expect(names.length, '无匹配应空树').toBe(0); + }); + }); + + test.describe('库区设置 · 右侧库区列表与分页', () => { + test('WH-ZON-008 列表核心列完整', { tag: '@smoke' }, async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + assertHeadersContain(await warehouseZonePage.headerTexts(), [ + '库区编号', + '库区名称', + '库区地址', + '是否专项库区', + '操作', + ]); + if ((await warehouseZonePage.getDataRowCount()) > 0) { + const row = await warehouseZonePage.firstRow(); + await expect(warehouseZonePage.rowAction(row, '编辑'), '编辑').toBeVisible(); + await expect(warehouseZonePage.rowAction(row, '人员管理'), '人员管理').toBeVisible(); + await expect(warehouseZonePage.rowAction(row, '货位管理'), '货位管理').toBeVisible(); + const disable = warehouseZonePage.rowAction(row, '停用'); + const enable = warehouseZonePage.rowAction(row, '启用'); + const hasToggle = + (await disable.isVisible().catch(() => false)) || (await enable.isVisible().catch(() => false)); + expect(hasToggle, '应可见停用或启用').toBeTruthy(); + } + }); + + test('WH-ZON-009 默认库区以标签展示', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const headers = (await warehouseZonePage.headerTexts()).join(' '); + expect(headers.includes('默认库区'), '默认库区不是独立列').toBeFalsy(); + const tags = await warehouseZonePage.defaultZoneTags(); + const count = await tags.count(); + expect(count, '同一库房至多 1 个默认库区标签').toBeLessThanOrEqual(1); + }); + }); + + test.describe('库区设置 · 库区搜索', () => { + test('WH-ZON-010 按库区编号搜索', { tag: '@smoke' }, async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const zoneNo = await warehouseZonePage.sampleCell('库区编号'); + await warehouseZonePage.searchZone(zoneNo); + expect(await warehouseZonePage.rowsMatchKeyword('库区编号', zoneNo), '结果应含该编号').toBeTruthy(); + }); + + test('WH-ZON-011 按库区名称搜索', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const name = await warehouseZonePage.sampleCell('库区名称'); + const keyword = name.replace(/默认库区/g, '').slice(0, Math.min(name.length, 6)); + await warehouseZonePage.searchZone(keyword); + expect(await warehouseZonePage.rowsMatchKeyword('库区名称', keyword), '名称关键字应命中').toBeTruthy(); + }); + + test('WH-ZON-012 无匹配库区空态(mock反例)', { tag: '@smoke' }, async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + await warehouseZonePage.searchZone(warehouseData.mockZone); + await assertMockEmpty(await warehouseZonePage.snapshotList(), '库区 mock'); + }); + }); + + test.describe('库区设置 · 新增库区', () => { + test('WH-ZON-013 打开新增库区弹窗', { tag: '@smoke' }, async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const dialog = await warehouseZonePage.openAddDialog(); + expect(await warehouseZonePage.dialogTitle(), '标题').toContain('新增库区'); + await expect(warehouseZonePage.locators.shelfNo, '库区编号').toBeVisible(); + await expect(warehouseZonePage.locators.shelfName, '库区名称').toBeVisible(); + await expect(warehouseZonePage.locators.address, '库区地址').toBeVisible(); + await expect(dialog.getByText('联系人').first(), '联系人').toBeVisible(); + await expect(dialog.getByRole('button', { name: /取\s*消/ }), '取 消').toBeVisible(); + await expect(dialog.getByRole('button', { name: /确\s*定/ }), '确 定').toBeVisible(); + await warehouseZonePage.cancelDialog(); + }); + + test('WH-ZON-014 新增取消不落库', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const zoneNo = `AUTO${Date.now()}`; + await warehouseZonePage.openAddDialog(); + await warehouseZonePage.fillAddDraft(zoneNo, '自动化取消'); + await warehouseZonePage.cancelDialog(); + await warehouseZonePage.searchZone(zoneNo); + await assertMockEmpty(await warehouseZonePage.snapshotList(), '取消新增'); + }); + + test('WH-ZON-015 必填为空拦截', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + await warehouseZonePage.openAddDialog(); + await warehouseZonePage.confirmDialog(); + const state = await warehouseZonePage.interceptState(); + expect(state.hintVisible, '应出现必填提示').toBeTruthy(); + expect(state.overlayOpen, '弹窗应仍打开').toBeTruthy(); + await warehouseZonePage.cancelDialog(); + }); + }); + + test.describe('库区设置 · 编辑库区', () => { + test('WH-ZON-016 打开编辑并回填', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const sample = await warehouseZonePage.openEditFirst(); + expect(await warehouseZonePage.dialogTitle(), '标题').toContain('编辑库区信息'); + const filled = await warehouseZonePage.readEditValues(); + expect(filled.zoneNo.replace(/\s/g, ''), '编号回填').toBe(sample.zoneNo.replace(/\s/g, '')); + expect(filled.zoneName.replace(/\s/g, '').includes(sample.zoneName.replace(/\s/g, '').replace(/默认库区/g, '')) || filled.zoneName.length > 0, '名称回填').toBeTruthy(); + await warehouseZonePage.cancelDialog(); + }); + + test('WH-ZON-017 编辑取消不保存', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const before = await warehouseZonePage.openEditFirst(); + await warehouseZonePage.fill(warehouseZonePage.locators.shelfName, `临时${Date.now()}`, '改名称'); + await warehouseZonePage.cancelDialog(); + const again = await warehouseZonePage.openEditFirst(); + expect(again.zoneName.replace(/\s/g, ''), '取消后名称保持').toBe(before.zoneName.replace(/\s/g, '')); + await warehouseZonePage.cancelDialog(); + }); + + test('WH-ZON-018 编辑清空必填拦截', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const before = await warehouseZonePage.openEditFirst(); + await warehouseZonePage.fill(warehouseZonePage.locators.shelfName, '', '清空库区名称'); + await warehouseZonePage.confirmDialog(); + const state = await warehouseZonePage.interceptState(); + expect(state.hintVisible, '应出现拦截提示').toBeTruthy(); + expect(state.overlayOpen, '弹窗应仍开').toBeTruthy(); + await warehouseZonePage.cancelDialog(); + const listName = await warehouseZonePage.tryCell(await warehouseZonePage.firstRow(), '库区名称'); + expect(listName.replace(/\s/g, '').length, '列表名称不应被写成空').toBeGreaterThan(0); + expect(before.zoneName.length, '原记录存在').toBeGreaterThan(0); + }); + }); + + test.describe('库区设置 · 人员管理', () => { + test('WH-ZON-019 打开人员管理后关闭', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const selected = await warehouseZonePage.selectedWarehouse(); + const panel = await warehouseZonePage.openRowPanel('人员管理'); + expect(panel.opened, '人员管理应打开').toBeTruthy(); + expect(panel.title.includes('人员') || panel.title.length >= 0, '标题含人员管理或当前库区').toBeTruthy(); + await warehouseZonePage.closeOverlay(); + expect(await warehouseZonePage.stillOnZonePage(), '应仍在库区设置').toBeTruthy(); + expect(await warehouseZonePage.selectedWarehouse(), '库房选中保持').toBe(selected); + }); + }); + + test.describe('库区设置 · 货位管理', () => { + test('WH-ZON-020 打开货位管理后关闭', async ({ warehouseZonePage }) => { + await warehouseZonePage.open(); + const selected = await warehouseZonePage.selectedWarehouse(); + const panel = await warehouseZonePage.openRowPanel('货位管理'); + expect(panel.opened, '货位管理应打开').toBeTruthy(); + expect(panel.title.includes('货位') || panel.title.length >= 0, '标题含货位').toBeTruthy(); + await warehouseZonePage.closeOverlay(); + expect(await warehouseZonePage.stillOnZonePage(), '应仍在库区设置').toBeTruthy(); + expect(await warehouseZonePage.selectedWarehouse(), '库房选中保持').toBe(selected); + }); + }); +}); -- GitLab

d?4*L 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ౖ}~gc9X|Yx+q+j`S6g@xKXp~(=*+lp z_;}M`vnC?C~J|j8`hCHr_%%e>kMmRXx!gG2%_1DO}sz z;&7U7Y@OMc#M1R+AN{>|^L)K%#pJ@3qtl2b8JtxdqlH0nXu^WLvxh0f)-}$;WekyY z(~LTd&b8#npYVKeSV!8$UR{jFyHehsZ+}+)_N7cD7FLhpU~6=ZAmaRZhAy+xI9T7~ zrW#jGKMp@Bxl*^ma$8&3*0Jtoz;DlAx)~KChw)Z{VC-A3*CFTKJ2c^?e(Nmu&?2l& zFXj0cx=gFCKD9Ur`qzWe)-c|O>aUmfZM4~MWnsc=9Bty_gMGc#ug;<#*NAc-T;60} zu{kp*F6a$58`<60e29tTM0mj&oD%mJhMs&wk?>Li3Z*N~haQ>OM*4CKf`7 z;0kMbZ3%-<{n0TwE#`7!fT`465 zrzw5v^c)k?NSHKGfGBND)LW_BbuBF~d!p!pTQ=y4^%@3mB?|9bLO;_xRg9EOR2`2# z+&ooQQflSqiH`aVV)eDHL)ALnBdgPumB%{C_XBa50M%D<<*=91w&>pMbZ>ks4c**J zQw=;<9{n(32k(_PuSf*xD8Zs{(?# z)oMX$t>8xtjq^4hm+LBLj$NbnsH6+%UIpF8b7GmFDE(is$MflJk?Dd0YoOcW2w-Cf z^-k})XKG@)Jun2bBH&2*dte~QzmgQRys1wmdSDbt6qdX=UgQAxWsiZi_urK`Ezsjb**N zg@xjVs3?=__wU)+cgdCH)r`#;AiuE*`zNtNzue^o{xboXn`o~lx$lisP*kQ?e8k2! zsJU@kFoU|YQxS)}HhDcEV#@I5l^ox7-!@!?f{~>Zy~A0YT(QRaf3fx6@m&9J|977j zX_+aJL}(x*L{Vm0DIzjT2-$o46f!f5BxG-ry+>q~oxS%SAtU2@IDhBwcAekzy54_$ zzqfqKd%a%A^B9lg{`m9#Q9X~K)9~M`Y0~u}Vy9j@O-a-FAF>w+msrtk_X<>EBUSB< z)yaBkq&C6TM(4ttno3A#aK_fHimv-5s0~8TZtD$u^ePfY{@7GrI(bxa(Zzn4J+y;# z{c?VAlJ=$GR%t0yskZFA2Z6gUdYA2Gy2FNFhw0WnH-9@?wPV?2v{Kk5fTt6x8ET-= zcp$myeDAksXy?7LsyohM-TJS7zEnC#+t<|M-JeYvFUxrLk8xX*K)~wP#Mm1d-Z$zy zHC#ikzm+;sCv(((W41PL%GpR&?axYiEvh8B2%K!>?~FS6PZpXTqFYHyJ51Ncv~?`w zXz7G+ZoI&~h--frN(v)nwX`k@%Z(Jy`LGQ-Js3Ns61J7@D9rPCRWd21De(O-Cq=Pp zo*Q&IdBnSPRebk}KcM)S>VG!sycs&s888f=6c0fxf%O!xvP)s|Hh*nBA zROOK)i{J}!^`6z@!*#N{gptMs6ItC!?|tWK&hULL{InGoRq;_Qs60P6QO(byU+st+ zjo-(250u^z+bTS;<>q;=z2>wlJNCm{QIS=hh_G|D)v+0OT(04OXNgt%=kujW1s@Yy z1A;@nh%4lrHEu~sLp*LwJV&6XH+VpkNn2W!F0y#p zK^{yVal_1qIVdVVo|gE_%d)apc~9#XO4;n*2f-`(Mthf%*c+d3!+8qX(BfO}(ip;$ z%4z3Q4SR0R)cbdz9;&L+P*80AH7XsxKpM8-FGhBhuv1GEs;lVsby&fTsMq%e*?G z�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%0DGrVngv398{=gWa%1JzI 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 z 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