head_page.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. '''
  4. @File : head_page.py
  5. @Time : 2024/11/07 15:19:33
  6. @Author : dulip3ng
  7. @Version : 1.0
  8. @Desc : None
  9. '''
  10. import re
  11. from playwright.sync_api import expect, Error, Page
  12. from pages.base_page import BasePage
  13. from common.field_type import FieldType
  14. class HeadPage(BasePage):
  15. def __init__(self, page:Page, locator):
  16. super().__init__(page)
  17. self.locator = locator
  18. def click_button(self, button_name:str, sub_button_name:str=None):
  19. """
  20. 点击单据头按钮
  21. :param button_name: 按钮名称
  22. :param sub_button_name: 子按钮名称
  23. :return: None
  24. """
  25. if not sub_button_name:
  26. self.page.locator(self.locator.BUTTON_ARGS_LOC % button_name).click()
  27. else:
  28. self.page.locator(self.locator.BUTTON_ARROW_ARGS_LOC % button_name).hover()
  29. self.page.locator(self.locator.SUB_BUTTON_ARGS_LOC % sub_button_name).click()
  30. def change_sheet(self, sheet_name:str):
  31. """
  32. 点击单据头页签
  33. :param sheet_name: 页签名称
  34. :return: None
  35. """
  36. self.page.locator(self.locator.SHEET_ARGS_LOC % sheet_name).click()
  37. def _set_text(self, field_name:str, value:str):
  38. """
  39. 单据头文本字段赋值
  40. :param field_name: 字段名
  41. :param value: 字段值
  42. :return: None
  43. """
  44. locator = self.page.locator(self.locator.TEXT_FIELD_ARGS_LOC % field_name)
  45. locator.clear()
  46. locator.fill(value)
  47. def _set_base(self, field_name:str, value:str):
  48. """
  49. 单据头基础资料字段赋值
  50. :param field_name: 字段名
  51. :param value: 字段值
  52. :return: None
  53. """
  54. # 直接点击BASE_FIELD_ARGS_LOC input元素可操作性检查不通过,被BASE_FIELD_SPAN_ARGS_LOC span劫持,这里直接对span进行点击
  55. self.page.locator(self.locator.BASE_FIELD_SPAN_ARGS_LOC % field_name).click()
  56. locator = self.page.locator(self.locator.BASE_FIELD_ARGS_LOC % field_name)
  57. locator.clear()
  58. locator.fill(value)
  59. # 输入空格弹出基础资料选项,fill方法会覆盖当前输入内容,使用press_sequentially方法模拟输入
  60. locator.press_sequentially(" ")
  61. locator.press("Backspace")
  62. self.page.locator(self.locator.BASE_ITEM_ARGS_LOC % value).nth(0).click()
  63. def _set_select(self, field_name:str, item_value:str):
  64. """
  65. 单据头下拉列表字段赋值
  66. :param field_name: 字段名
  67. :param item_value: 字段值
  68. :return: None
  69. """
  70. # Locator.click()普通点击无法弹出下拉选项值,使用force强制点击
  71. # 注意:force=True强制点击不执行非必要的元素可操作性检查
  72. self.page.locator(self.locator.SELECT_FIELD_ARGS_LOC % field_name).click()
  73. self.page.locator(self.locator.SELECT_ITEM_ARGS_LOC % (item_value,item_value)).click()
  74. def _set_textarea(self, field_name:str, value:str):
  75. """
  76. 单据头大文本框字段赋值
  77. :param field_name: 字段名
  78. :param value: 字段值
  79. :return: None
  80. """
  81. locator = self.page.locator(self.locator.TEXTAREA_FIELD_ARGS_LOC % field_name)
  82. locator.clear()
  83. locator.fill(value)
  84. def _set_multiselect(self, field_name:str, values:str):
  85. """
  86. 单据头多选下拉列表字段赋值
  87. :param field_name: 字段名
  88. :param values: 字段值
  89. :return: None
  90. """
  91. self.page.locator(self.locator.MULTISELECT_FIELD_ARGS_LOC % field_name).click()
  92. for item_value in values:
  93. self.page.locator(self.locator.MULTISELECT_ITEM_ARGS_LOC % item_value).click()
  94. self.page.locator(self.locator.MULTISELECT_OK_BTN_LOC).click()
  95. def set_value(self, field_name:str, *values:str, field_type: FieldType = None):
  96. """
  97. 单据头字段赋值统一方法,根据给定或自动识别的字段类型调用不同方法完成赋值
  98. :param field_name: 字段名
  99. :param value: 字段值
  100. :param field_type: 可选参数,字段类型,不填时自动识别字段类型
  101. :return: None
  102. """
  103. if not field_type:
  104. field_type = self._parse_field_type(field_name)
  105. match field_type:
  106. case FieldType.TEXT:
  107. self._set_text(field_name, values[0])
  108. case FieldType.SELECT:
  109. self._set_select(field_name, values[0])
  110. case FieldType.BASE:
  111. self._set_base(field_name, values[0])
  112. case FieldType.TEXTAREA:
  113. self._set_textarea(field_name, values[0])
  114. case FieldType.MULTISELECT:
  115. self._set_multiselect(field_name, *values)
  116. def set_checked(self, field_name:str):
  117. """
  118. 单据头勾选复选框
  119. :param field_name: 复选框名
  120. :return: None
  121. """
  122. self.page.locator(self.locator.CHECKBOX_FIELD_ARGS_LOC % field_name).click()
  123. def set_text_in_head_body(self, row:int, field_name:str, value:str):
  124. """
  125. 单据头中的单据体文本字段赋值
  126. 如样品通知单-接收人页签
  127. :param field_name: 字段名
  128. :param value: 字段值
  129. :param row: 行号,从1开始
  130. :return: None
  131. """
  132. self.page.locator(self.locator.TRIGGER_IN_HEAD_BODY_FIELD_ARGS_LOC % (field_name, row-1)).click()
  133. locator = self.page.locator(self.locator.TEXT_FIELD_IN_HEAD_BODY_ARGS_LOC % (field_name, row-1))
  134. locator.clear()
  135. locator.fill(value)
  136. def set_base_in_head_body(self, row:int, field_name:str, value:str):
  137. """
  138. 单据头中的单据体基础资料字段赋值
  139. 如样品通知单-接收人页签
  140. :param field_name: 字段名
  141. :param value: 字段值
  142. :param row: 行号,从1开始
  143. :return: None
  144. """
  145. self.page.locator(self.locator.TRIGGER_IN_HEAD_BODY_FIELD_ARGS_LOC % (field_name, row - 1)).click()
  146. locator = self.page.locator(self.locator.BASE_FIELD_IN_HEAD_BODY_ARGS_LOC % (field_name, row - 1))
  147. locator.click()
  148. locator.fill(value)
  149. # 输入空格弹出基础资料选项,fill方法会覆盖当前输入内容,使用press_sequentially方法模拟输入
  150. locator.press_sequentially(" ")
  151. locator.press("Backspace")
  152. self.page.locator(self.locator.BASE_ITEM_ARGS_LOC % value).nth(0).click()
  153. def _get_text_value(self, field_name:str) -> str:
  154. """
  155. 单据头文本字段取值
  156. 文本字段有两种形式,一种是可编辑状态,值存放在input元素中,另一种是锁定状态值存放在span元素中,
  157. 默认取input值,500ms超时未取到时取span中的值
  158. :param field_name: 字段名
  159. :return: 字段值
  160. """
  161. try:
  162. locator_input = self.page.locator(self.locator.TEXT_VALUE_INPUT_ARGS_LOC % field_name)
  163. expect(locator_input).to_be_visible(timeout=500)
  164. return locator_input.input_value()
  165. except AssertionError:
  166. locator_span = self.page.locator(self.locator.TEXT_VALUE_SPAN_ARGS_LOC % field_name)
  167. expect(locator_span).to_be_visible(timeout=500)
  168. return locator_span.inner_text()
  169. def _get_select_value(self, field_name:str) -> str:
  170. """
  171. 单据头下拉列表字段取值
  172. 下拉列表字段有两种形式,一种是可编辑状态,值存放在input元素中,另一种是锁定状态值存放在span元素中,
  173. 默认取input值,未找到时立即去span中的值
  174. 与取text不同的是text由两个xpath语句获取值,select由一个通用的xpath取值,所以判断方式不同
  175. :param field_name: 字段名
  176. :return: 字段值
  177. """
  178. try:
  179. locator = self.page.locator(self.locator.SELECT_VALUE_ARGS_LOC % field_name)
  180. return locator.input_value()
  181. except Error:
  182. return locator.inner_text()
  183. def _get_base_value(self, field_name:str) -> str:
  184. """
  185. 获取单据头基础资料字段值
  186. :param field_name: 字段名
  187. :return: 字段值
  188. """
  189. return self.page.locator(self.locator.BASE_VALUE_ARGS_LOC % field_name).inner_text()
  190. def _get_textarea_value(self, field_name:str):
  191. """
  192. 单据头大文本字段取值
  193. :param field_name: 字段名
  194. :return: 字段值
  195. """
  196. return self.page.locator(self.locator.TEXTAREA_FIELD_ARGS_LOC % field_name).input_value()
  197. def _get_multiselect_value(self, field_name:str):
  198. """
  199. 单据头多选下拉列表字段取值
  200. :param field_name: 字段名
  201. :return: 字段值
  202. """
  203. return self.page.locator(self.locator.MULTISELECT_VALUE_ARGS_LOC % field_name).inner_text()
  204. def get_value(self, field_name:str, field_type: FieldType=None) -> str:
  205. """
  206. 单据头字段取值统一方法,根据给定或自动识别的字段类型调用不同取值方法取值
  207. :param field_name: 字段名
  208. :param field_type: 可选参数,字段类型
  209. :return: 字段值
  210. """
  211. if not field_type:
  212. field_type = self._parse_field_type(field_name)
  213. match field_type:
  214. case FieldType.TEXT:
  215. return self._get_text_value(field_name)
  216. case FieldType.SELECT:
  217. return self._get_select_value(field_name)
  218. case FieldType.BASE:
  219. return self._get_base_value(field_name)
  220. case FieldType.MULTISELECT:
  221. return self._get_multiselect_value(field_name)
  222. case FieldType.TEXTAREA:
  223. return self._get_textarea_value(field_name)
  224. case _:
  225. return ""
  226. def _parse_field_type(self, field_name:str) -> FieldType:
  227. """
  228. 分析字段类型,set_value(), get_value()中调用的核心方法
  229. 注意:金蝶版本升级后可能不同字段类型判断依据有变化,字段类型是根据元素"data-attdata"属性值判断,
  230. 定位器可用性检查通过不代表分析字段类型可正常使用
  231. :param field_name: 字段名
  232. :return: 字段类型 FieldType
  233. """
  234. data_role = self.page.locator(self.locator.PARSE_FIELD_TYPE_ARGS_LOC % field_name).get_attribute("data-attdata")
  235. pattern = "xtype\":\"([a-zA-Z0-9]+)"
  236. match_strs = re.findall(pattern, data_role)
  237. if match_strs:
  238. match match_strs[0]:
  239. case "bosf7field":
  240. return FieldType.BASE
  241. case "kdeditcombo" | "kdcombo":
  242. return FieldType.SELECT
  243. case "kdmulticombo":
  244. return FieldType.MULTISELECT
  245. case "kdtextarea" | "kdlangtextarea" | "kdtabpage":
  246. return FieldType.TEXTAREA
  247. case _:
  248. return FieldType.TEXT
  249. return FieldType.UNKNOWN
  250. def click_button_in_head_body(self, button_name:str, sub_button_name:str=None):
  251. """
  252. 点击单据头中单据体按钮或子按钮
  253. 如:新增行
  254. **用法:**
  255. HeadPage.click_button_in_head_body("新增行")
  256. HeadPage.click_button_in_head_body("新增行", "复制行")
  257. :param button_name: 按钮名称
  258. :param sub_button_name: 可选参数,子按钮名称
  259. :return: None
  260. """
  261. if not sub_button_name:
  262. self.page.locator(self.locator.BUTTON_IN_HEAD_BODY_ARGS_LOC % button_name).click()
  263. else:
  264. self.page.locator(self.locator.BUTTON_ARROW_IN_HEAD_BODY_ARGS_LOC % button_name).hover()
  265. self.page.locator(self.locator.SUB_BUTTON_IN_HEAD_BODY_ARGS_LOC % sub_button_name).click()
  266. self._click_blank()