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}" )