Update UI automation test files
This commit is contained in:
208
zhyy/test_case/Resource/UI/contract_management_page.py
Normal file
208
zhyy/test_case/Resource/UI/contract_management_page.py
Normal file
@@ -0,0 +1,208 @@
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
import allure
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
from zhyy.test_case.Resource.UI.base_page import BasePage
|
||||
|
||||
|
||||
class ContractManagementPage(BasePage):
|
||||
"""合同管理页面对象。
|
||||
|
||||
注意:
|
||||
- 当前未提供页面侦察结果,以下 selector 采用“候选定位 + TODO”方式。
|
||||
- 落地执行前,建议通过侦察脚本确认真实 DOM 后替换 TODO selector。
|
||||
"""
|
||||
|
||||
def __init__(self, page: Page, screenshot_dir: str):
|
||||
super().__init__(page, screenshot_dir)
|
||||
|
||||
def open(self, url: str) -> None:
|
||||
with allure.step("打开合同管理被测页面"):
|
||||
self.goto(url)
|
||||
self.attach_screenshot("01_open_contract_management_page.png")
|
||||
|
||||
def login_if_needed(self, username: str, password: str) -> None:
|
||||
"""如页面跳转到登录页,则执行登录。
|
||||
|
||||
TODO:
|
||||
请根据真实登录页 DOM 替换用户名、密码、登录按钮 selector。
|
||||
当前保留常见中文系统候选定位,避免在无侦察结果时硬编码单一 selector。
|
||||
"""
|
||||
with allure.step("如需要则登录系统"):
|
||||
username_candidates = [
|
||||
self.page.get_by_placeholder(re.compile("用户名|账号|请输入用户名|请输入账号")),
|
||||
self.page.locator("input[name='username']"),
|
||||
self.page.locator("input[type='text']").first,
|
||||
# TODO: 替换为真实用户名输入框 selector,例如:self.page.locator("#username")
|
||||
]
|
||||
|
||||
password_candidates = [
|
||||
self.page.get_by_placeholder(re.compile("密码|请输入密码")),
|
||||
self.page.locator("input[name='password']"),
|
||||
self.page.locator("input[type='password']"),
|
||||
# TODO: 替换为真实密码输入框 selector,例如:self.page.locator("#password")
|
||||
]
|
||||
|
||||
login_button_candidates = [
|
||||
self.page.get_by_role("button", name=re.compile("登录|登 录|Login", re.I)),
|
||||
self.page.locator("button[type='submit']"),
|
||||
self.page.locator("text=登录"),
|
||||
# TODO: 替换为真实登录按钮 selector
|
||||
]
|
||||
|
||||
login_form_visible = any(
|
||||
locator.first.is_visible()
|
||||
for locator in username_candidates + password_candidates
|
||||
)
|
||||
|
||||
if not login_form_visible:
|
||||
return
|
||||
|
||||
self.fill_first_visible(username_candidates, username, description="用户名输入框")
|
||||
self.fill_first_visible(password_candidates, password, description="密码输入框")
|
||||
self.attach_screenshot("02_before_login.png")
|
||||
|
||||
self.click_first_visible(login_button_candidates, description="登录按钮")
|
||||
self.wait_network_idle()
|
||||
self.attach_screenshot("03_after_login.png")
|
||||
|
||||
def ensure_contract_menu_selected(self) -> None:
|
||||
"""进入/确认合同菜单。
|
||||
|
||||
若 URL 已直接进入合同管理页面,本方法不会强制点击菜单。
|
||||
TODO:
|
||||
如系统必须通过左侧菜单进入,请用页面真实菜单 selector 替换候选定位。
|
||||
"""
|
||||
with allure.step("找到并选择合同菜单"):
|
||||
menu_candidates = [
|
||||
self.page.get_by_role("menuitem", name=re.compile("合同管理|合同")),
|
||||
self.page.get_by_text(re.compile("^合同管理$|^合同$"), exact=False),
|
||||
self.page.locator("text=合同管理"),
|
||||
self.page.locator("text=合同"),
|
||||
# TODO: 替换为真实合同菜单 selector
|
||||
]
|
||||
|
||||
try:
|
||||
menu = self.first_visible_locator(
|
||||
menu_candidates,
|
||||
timeout=3000,
|
||||
description="合同菜单",
|
||||
)
|
||||
menu.click()
|
||||
self.wait_network_idle()
|
||||
except AssertionError:
|
||||
# 直接 URL 进入时可能没有可见菜单或菜单已选中,不阻断用例。
|
||||
pass
|
||||
|
||||
self.attach_screenshot("04_contract_menu_selected.png")
|
||||
|
||||
def search_by_contract_no_fuzzy(self, partial_contract_no: str) -> None:
|
||||
"""按合同编号进行模糊查询。"""
|
||||
with allure.step(f"输入合同编号部分字符并点击查询:{partial_contract_no}"):
|
||||
contract_no_input_candidates = [
|
||||
self.page.get_by_placeholder(re.compile("合同编号|请输入合同编号")),
|
||||
self.page.locator("input[placeholder*='合同编号']"),
|
||||
self.page.locator("label:has-text('合同编号')").locator("xpath=following::input[1]"),
|
||||
self.page.locator("text=合同编号").locator("xpath=following::input[1]"),
|
||||
# TODO: 替换为真实合同编号搜索框 selector
|
||||
]
|
||||
|
||||
query_button_candidates = [
|
||||
self.page.get_by_role("button", name=re.compile("^查询$|搜索")),
|
||||
self.page.locator("button:has-text('查询')"),
|
||||
self.page.locator("text=查询"),
|
||||
# TODO: 替换为真实查询按钮 selector
|
||||
]
|
||||
|
||||
self.fill_first_visible(
|
||||
contract_no_input_candidates,
|
||||
partial_contract_no,
|
||||
description="合同编号搜索框",
|
||||
)
|
||||
self.attach_screenshot("05_filled_contract_no.png")
|
||||
|
||||
self.click_first_visible(
|
||||
query_button_candidates,
|
||||
description="查询按钮",
|
||||
)
|
||||
self.wait_network_idle()
|
||||
|
||||
# 等待常见表格渲染完成
|
||||
self.wait_for_result_table()
|
||||
self.attach_screenshot("06_after_contract_no_search.png")
|
||||
|
||||
def wait_for_result_table(self) -> None:
|
||||
"""等待查询结果表格出现。
|
||||
|
||||
TODO:
|
||||
根据真实表格框架替换为唯一稳定 selector。
|
||||
"""
|
||||
table_candidates = [
|
||||
self.page.locator(".el-table__body-wrapper"),
|
||||
self.page.locator(".el-table__body"),
|
||||
self.page.locator(".ant-table-tbody"),
|
||||
self.page.locator("table tbody"),
|
||||
self.page.locator("[role='table']"),
|
||||
# TODO: 替换为真实结果表格 selector
|
||||
]
|
||||
self.first_visible_locator(table_candidates, timeout=10000, description="合同结果表格")
|
||||
|
||||
def result_rows_locator(self):
|
||||
"""结果表格行候选。
|
||||
|
||||
TODO:
|
||||
建议替换为合同列表真实数据行 selector。
|
||||
"""
|
||||
candidates = [
|
||||
self.page.locator(".el-table__body tbody tr"),
|
||||
self.page.locator(".ant-table-tbody tr"),
|
||||
self.page.locator("table tbody tr"),
|
||||
self.page.locator("[role='row']"),
|
||||
# TODO: 替换为真实合同列表行 selector
|
||||
]
|
||||
|
||||
for locator in candidates:
|
||||
try:
|
||||
if locator.count() > 0:
|
||||
return locator
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
raise AssertionError("未找到合同列表数据行,请根据真实页面补充 result_rows_locator selector。")
|
||||
|
||||
def get_result_row_texts(self) -> list[str]:
|
||||
with allure.step("获取合同列表查询结果"):
|
||||
rows = self.result_rows_locator()
|
||||
texts = self.text_contents(rows)
|
||||
# 过滤空行、表头、加载占位行
|
||||
return [
|
||||
text
|
||||
for text in texts
|
||||
if text and "暂无数据" not in text and "No Data" not in text
|
||||
]
|
||||
|
||||
def assert_contract_no_fuzzy_match(self, partial_contract_no: str) -> None:
|
||||
"""断言查询结果与合同编号模糊查询条件一致。
|
||||
|
||||
当前无页面侦察结果,无法确认“合同编号”列的真实 selector。
|
||||
因此先断言每条结果行文本中包含输入的合同编号部分字符。
|
||||
如果后续确认合同编号列 selector,请改为仅校验合同编号列文本。
|
||||
"""
|
||||
with allure.step("断言列表仅展示合同编号与输入内容模糊匹配的数据"):
|
||||
row_texts = self.get_result_row_texts()
|
||||
|
||||
assert row_texts, "查询结果为空,无法验证合同编号模糊匹配。请确认测试数据或查询条件。"
|
||||
|
||||
unmatched_rows = [
|
||||
row_text
|
||||
for row_text in row_texts
|
||||
if partial_contract_no not in row_text
|
||||
]
|
||||
|
||||
assert not unmatched_rows, (
|
||||
f"存在与合同编号查询条件不匹配的结果。\n"
|
||||
f"查询条件:{partial_contract_no}\n"
|
||||
f"不匹配行:{unmatched_rows}"
|
||||
)
|
||||
Reference in New Issue
Block a user