Files
smart-management-auto-test/zhyy/test_case/Resource/UI/contract_manage_page.py

195 lines
6.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import re
from playwright.sync_api import expect
from zhyy.test_case.Resource.UI.base_page import BasePage
class SmartManagementLoginPage(BasePage):
"""
智慧运营登录页对象。
TODO:
当前未提供登录页侦察结果,以下 selector 为常见候选。
落地时请优先使用真实 DOM 中稳定属性,例如 data-testid、id、name。
"""
USERNAME_INPUT_SELECTORS = [
"input[name='username']",
"input[name='userName']",
"input[id*='username']",
"input[placeholder*='用户名']",
"input[placeholder*='账号']",
]
PASSWORD_INPUT_SELECTORS = [
"input[name='password']",
"input[type='password']",
"input[placeholder*='密码']",
]
LOGIN_BUTTON_SELECTORS = [
"button:has-text('登录')",
"button:has-text('登 录')",
"[role='button']:has-text('登录')",
]
def login_if_needed(self, username: str, password: str):
"""
如果当前页面出现登录表单则登录;如果已登录或 SSO 已生效则跳过。
"""
username_input = self.page.locator(",".join(self.USERNAME_INPUT_SELECTORS)).first
try:
username_input.wait_for(state="visible", timeout=5000)
except Exception:
return
self.fill_first_visible(self.USERNAME_INPUT_SELECTORS, username, description="用户名输入框")
self.fill_first_visible(self.PASSWORD_INPUT_SELECTORS, password, description="密码输入框")
self.click_first_visible(self.LOGIN_BUTTON_SELECTORS, description="登录按钮")
self.wait_network_idle()
self.screenshot("after_login.png")
class ContractManagePage(BasePage):
"""
合同管理页面对象。
被测页面:
https://smart-management-web-st.best-envision.com/SZPurchase/PurchaseManage/PurchaseOrderManage
TODO:
reconnaissanceResult 为空,缺少真实 DOM。
以下 selector 采用“清晰候选 + TODO”方式落地执行前需通过页面侦察结果替换为真实稳定 selector。
"""
CONTRACT_MENU_SELECTORS = [
# TODO: 使用真实菜单 DOM 替换,例如 [data-testid='contract-menu']
"text=合同管理",
"a:has-text('合同管理')",
"li:has-text('合同管理')",
"[role='menuitem']:has-text('合同管理')",
]
CONTRACT_NO_INPUT_SELECTORS = [
# TODO: 使用真实合同编号输入框 selector 替换
"input[placeholder*='合同编号']",
"input[aria-label*='合同编号']",
".ant-form-item:has-text('合同编号') input",
".el-form-item:has-text('合同编号') input",
"label:has-text('合同编号') + div input",
]
QUERY_BUTTON_SELECTORS = [
"button:has-text('查询')",
"button:has-text('搜索')",
"[role='button']:has-text('查询')",
"[role='button']:has-text('搜索')",
]
TABLE_ROW_SELECTORS = [
# Ant Design Table
".ant-table-tbody > tr:not(.ant-table-placeholder)",
# Element Plus / Element UI Table
".el-table__body tbody tr",
# 原生 table
"table tbody tr",
]
EMPTY_SELECTORS = [
".ant-empty",
".el-empty",
"text=暂无数据",
"text=无数据",
"text=No Data",
]
def open(self, url: str):
self.goto(url)
self.screenshot("contract_manage_opened.png")
def open_contract_menu_if_visible(self):
"""
用户步骤要求“找到合同菜单”。
如果当前 URL 已直达合同管理页,则菜单可能无需点击。
若菜单可见则点击;不可见不强制失败,避免直达 URL 场景误报。
"""
for selector in self.CONTRACT_MENU_SELECTORS:
locator = self.page.locator(selector).first
try:
locator.wait_for(state="visible", timeout=2000)
locator.click()
self.wait_network_idle()
self.screenshot("contract_menu_clicked.png")
return
except Exception:
continue
def search_by_contract_no_keyword(self, keyword: str):
self.fill_first_visible(
self.CONTRACT_NO_INPUT_SELECTORS,
keyword,
description="合同编号搜索框",
)
self.click_first_visible(
self.QUERY_BUTTON_SELECTORS,
description="查询按钮",
)
self.wait_network_idle()
self.wait_for_timeout(1000)
self.screenshot("contract_no_search_result.png")
def _table_rows_locator(self):
for selector in self.TABLE_ROW_SELECTORS:
rows = self.page.locator(selector)
try:
if rows.count() > 0 and rows.first.is_visible(timeout=2000):
return rows
except Exception:
continue
return None
def has_empty_result(self) -> bool:
for selector in self.EMPTY_SELECTORS:
try:
locator = self.page.locator(selector).first
if locator.is_visible(timeout=1000):
return True
except Exception:
continue
return False
def get_result_row_texts(self) -> list[str]:
rows = self._table_rows_locator()
if rows is None:
return []
row_texts = []
count = rows.count()
for index in range(count):
row = rows.nth(index)
text = re.sub(r"\s+", " ", row.inner_text()).strip()
if text:
row_texts.append(text)
return row_texts
def assert_contract_no_fuzzy_match(self, keyword: str):
"""
断言查询结果与合同编号模糊匹配条件一致。
说明:
由于缺少真实表格列 selector当前按整行文本包含 keyword 断言。
落地后建议替换为“合同编号列单元格”精确读取:
- AntD: 根据 th 文本定位列 index再读取 tbody tr td[index]
- Element: 使用列 prop 或 class 定位
"""
row_texts = self.get_result_row_texts()
assert row_texts, (
f"合同编号关键字【{keyword}】查询后未获取到表格数据。"
f"若业务允许无结果,请调整测试数据为存在的合同编号片段。"
)
unmatched_rows = [text for text in row_texts if keyword not in text]
assert not unmatched_rows, (
f"存在与合同编号关键字【{keyword}】不匹配的查询结果:{unmatched_rows}"
)