常用basepage模块代码
import time
from logging import config
import randomimport allure
from selenium. webdriver. common. alert import Alert
from selenium. webdriver. remote. webelement import WebElement
from selenium. webdriver. support. select import Selectimport config
from common. utils. ReturnTime import ReturnTime
from jc_log. setting import get_logger
import pyautogui
from pywinauto. keyboard import send_keys
from selenium. common. exceptions import NoSuchElementException, InvalidSelectorException, TimeoutException
from selenium. webdriver import ActionChains
from selenium. webdriver. support import expected_conditions as ec
from selenium. webdriver. support. wait import WebDriverWaitlogger = get_logger( __name__)
g_timeout = 10
g_poll_frequency = 0.2 class BasePage : """UI自动化基础操作封装""" def __init__ ( self, driver) : self. driver = driverself. __img_dir = config. REPORT_DIRdef refresh ( self) : """刷新网页""" self. driver. refresh( ) def get_url ( self, url) : """访问指定地址""" self. driver. get( url= url) return self. driver. current_urldef get_current_url ( self) : """获取当前浏览器驱动的地址""" return self. driver. current_urldef force_click ( self, locator: tuple , mode: str = "click" , force= False ) : """鼠标点击,当元素不可点击的时候,使用强制点击:param locator: 元素定位,元祖类型:param mode: visible(元素可见),notvisible(元素消失不可见), exist(元素存在),click(元素可点击):param force: 强制点击,默认false""" try : elem = self. find_element( locator= locator, mode= mode) if not force: self. driver. execute_script( "arguments[0].click()" , elem) else : elem. click( ) self. driver. execute_script( "arguments[0].click({force: true})" , elem) except Exception as e: print ( "未找到元素:{}" . format ( e) ) raise edef wait_element_visibility ( self, locator: tuple , timeout= 20 , poll_frequency= g_poll_frequency) : """显性等待: 等待元素可见:param locator: 元素定位,元祖类型:param timeout:param poll_frequency:return:""" page = self. get_current_url( ) logger. debug( f"-开始-等待页面 { page} 的元素: { locator} 可见" ) try : __start_time = ReturnTime. get_timestamp( ) rtn = WebDriverWait( self. driver, timeout, poll_frequency) . until( ec. visibility_of_element_located( locator) ) __wait_time = ReturnTime. get_timestamp( ) - __start_timelogger. debug( f"页面: { page} 上的元素 { locator} 已可见,共计等待 { __wait_time: 0.2f } 秒" ) except TimeoutException: logger. error( f"页面: { page} ,等待元素 { locator} 超时" ) self. set_error_img( ) raise TimeoutException( '元素等待超时' ) except InvalidSelectorException as e: logger. error( f"页面: { page} ,元素不可见或定位表达式: { locator} 异常\n { e} " ) raise InvalidSelectorException( '元素定位异常' ) return rtndef wait_element_clickable ( self, locator: tuple , timeout= g_timeout, poll_frequency= g_poll_frequency) : """显性等待: 等待元素可点击:param locator: 元素定位,元祖类型:param timeout::param poll_frequency:return:""" page = self. get_current_url( ) logger. debug( f"-开始-等待页面 { page} 的元素: { locator} 可点击" ) try : __start_time = ReturnTime. get_timestamp( ) rtn = WebDriverWait( self. driver, timeout, poll_frequency) . until( ec. element_to_be_clickable( locator) ) __wait_time = ReturnTime. get_timestamp( ) - __start_timelogger. debug( f"页面: { page} 上的元素 { locator} 已可点击,共计等待 { __wait_time: 0.2f } 秒" ) except TimeoutException as e: logger. error( f"页面: { page} ,等待元素 { locator} 超时" ) self. set_error_img( ) raise eexcept InvalidSelectorException as e: logger. error( f"页面: { page} ,元素不可点击或定位表达式: { locator} 异常\n { e} " ) raise ereturn rtndef wait_element_presence ( self, locator: tuple , timeout= g_timeout, poll_frequency= g_poll_frequency) : """显性等待: 等待元素被加载出来:param locator: 元素定位,元祖类型:param timeout:param poll_frequency:return:""" page = self. get_current_url( ) logger. debug( f"-开始-等待页面 { page} 的元素: { locator} 存在" ) try : __start_time = ReturnTime. get_timestamp( ) rtn = WebDriverWait( self. driver, timeout, poll_frequency) . until( ec. presence_of_all_elements_located( locator) ) __wait_time = ReturnTime. get_timestamp( ) - __start_timelogger. debug( f"页面: { page} 上的元素 { locator} 已存在,共计等待 { __wait_time: 0.2f } 秒" ) except TimeoutException as e: logger. error( f"页面: { page} ,等待元素 { locator} 超时" ) self. set_error_img( ) raise eexcept InvalidSelectorException as e: logger. error( f"页面: { page} ,元素不存在或定位表达式: { locator} 异常\n { e} " ) raise ereturn rtndef wait_element_not_visible ( self, locator: tuple , timeout= g_timeout, poll_frequency= g_poll_frequency) : """等待元素不可见:param locator: 元素定位表达式:param timeout:param poll_frequency:return::return: 无返回值""" page = self. get_current_url( ) logger. debug( f"-开始-等待页面 { page} 的元素: { locator} 存在" ) try : __start_time = ReturnTime. get_timestamp( ) time. sleep( 1 ) rtn = WebDriverWait( self. driver, timeout, poll_frequency) . until( ec. invisibility_of_element_located( locator) ) __wait_time = ReturnTime. get_timestamp( ) - __start_timelogger. debug( f"页面: { page} 上的元素 { locator} 已经不可见,共计等待 { __wait_time: 0.2f } 秒" ) except TimeoutException as e: logger. error( f"页面: { page} ,等待元素 { locator} 不可见超时" ) self. set_error_img( ) raise eexcept InvalidSelectorException as e: logger. error( f"页面: { page} ,元素不存在或定位表达式: { locator} 异常\n { e} " ) raise ereturn rtndef get_element_attribute ( self, locator: tuple , attr_name) : """获取元素属性值:param locator: 元素定位,元祖类型:param attr_name:return: 元素属性值""" try : return self. driver. find_element( * locator) . get_attribute( attr_name) except NoSuchElementException as e: print ( "未找到元素:{}" . format ( e) ) raise edef get_name ( self, locator: tuple ) : """获取元素的name属性值""" return self. get_element_attribute( locator, "name" ) def get_title ( self, locator: tuple ) : """获取元素的title属性值""" return self. get_element_attribute( locator, "title" ) def get_class ( self, locator: tuple ) : """获取元素的class属性值""" return self. get_element_attribute( locator, "class" ) def switch_to_frame ( self, reference= None , timeout= g_timeout, poll= g_poll_frequency) : """iframe切换:param reference: 可以是id, name,索引或者元素定位(元祖):param timeout::param poll::return:""" if not reference: return self. driver. switch_to. default_content( ) return WebDriverWait( self. driver, timeout, poll) . until( ec. frame_to_be_available_and_switch_to_it( reference) ) def __get_title_handle ( self, title) : handles = self. driver. window_handlesfor handle in handles: self. driver. switch_to. window( handle) if self. driver. title == title: return handledef switch_to_window ( self, title) : """切换到指定窗口""" self. __get_title_handle( title) return selfdef switch_next_window ( self) : """切换到另一个窗口""" windows = self. driver. window_handlesif len ( windows) >= 2 : self. driver. switch_to. window( self. driver. window_handles[ - 1 ] ) return selfdef open_new_window ( self, url) : """打开一个新窗口""" start_window = self. driver. window_handlsjs = "window.open({})" . format ( url) self. driver. execute_script( js) WebDriverWait( self. driver, 5 , 0.5 ) . until( ec. new_window_is_opened( start_window) ) self. driver. switch_to. window( self. driver. window_handls[ - 1 ] ) return selfdef __select_wait_method ( self, locator: tuple , mode: str = "visible" ) - > None : """选择元素定位的等待方式:param locator: 元素的定位表达式 例:(By.xx,'定位表达式'):param mode: visible(元素可见), exist(元素存在),click(元素可点击):return: 无返回值""" page = self. get_current_url( ) if mode == "visible" : self. wait_element_visibility( locator= locator) elif mode == "exist" : self. wait_element_presence( locator= locator) elif mode == "click" : self. wait_element_clickable( locator= locator) else : logger. error( f"定位 { page} 页面的元素: { locator} ,mode参数传值异常,入参值为: { mode} " ) def find_element ( self, locator: tuple , mode: str = "visible" ) - > WebElement: """定位元素,支持所有定位单个元素的定位表达式:param locator: 元素的定位表达式 例:(By.xx,'定位表达式'):param mode: visible(元素可见), exist(元素存在),click(元素可点击):return: 定位到的元素对象""" page = self. get_current_url( ) self. __select_wait_method( locator= locator, mode= mode) try : logger. debug( f"正在定位 { page} 页面的: { locator} 的元素" ) element = self. driver. find_element( * locator) self. element_dyeing( element= element) return elementexcept TimeoutException: logger. error( f"页面: { page} ,定位元素: { locator} 定位超时" ) self. set_error_img( ) raise TimeoutException( "元素定位超时请检查" ) except Exception: logger. error( f"页面: { page} ,定位元素: { locator} 定位失败" ) self. set_error_img( ) raise Exception( "元素定位失败请检查" ) def find_elements ( self, locator: tuple , mode: str = "visible" ) - > list : """定位一组元素,返回一个列表:param locator: 元素的定位表达式 例:(By.xx,'定位表达式'):param mode: visible(元素可见),notvisible(元素消失不可见), exist(元素存在):return: 返回一组元素是一个列表""" page = self. get_current_url( ) self. __select_wait_method( locator= locator, mode= mode) try : logger. debug( f"正在定位 { page} 页面的: { locator} 的元素" ) element_list = self. driver. find_elements( * locator) return element_listexcept NoSuchElementException as e: logger. error( f"页面: { page} ,定位元素: { locator} 定位失败" ) self. set_error_img( ) raise edef __move_element_visible ( self, locator: tuple , element: WebElement, alignment: bool = False ) - > None : """将元素移动到页面可见区域:param locator: 元素的定位表达式 例:(By.xx,'定位表达式'):param alignment 默认对齐方式是元素和当前页面的底部对齐,可以传 alignment=''表示和顶部对齐:param element 需要可见的元素:return: 无返回值""" page = self. get_current_url( ) logger. debug( f'将 { page} 页面的元素: { locator} 移动至浏览器可见区域' ) try : time. sleep( 0.5 ) self. driver. execute_script( 'arguments[0].scrollIntoView({0});' . format ( alignment) , element) time. sleep( 1 ) except Exception as e: logger. error( f" { page} 页面的元素: { locator} 移动失败\n { e} " ) self. set_error_img( ) raise edef click ( self, locator: tuple , mode: str = "click" , alignment: bool = False , move_element: bool = False , is_double_click: bool = False ) - > None : """点击元素:param locator: 元素的定位表达式 例:(By.xx,'定位表达式'):param mode: visible(元素可见),notvisible(元素消失不可见), exist(元素存在),click(元素可点击):param alignment: 默认对齐方式是元素和当前页面的底部对齐,可以传 alignment=''表示和顶部对齐:param move_element: 这里是布尔值 传入True 表示需要让元素滚动到页面可见区域 False 表示不用:param is_double_click: False单击元素,传入True 双击元素:return: 无返回值""" page = self. get_current_url( ) element = self. find_element( locator= locator, mode= mode) if move_element is True : self. __move_element_visible( locator= locator, element= element, alignment= alignment) try : logger. debug( f"点击: { page} 页面,属性为 { locator} 的元素" ) if is_double_click: ActionChains( self. driver) . double_click( element) . perform( ) else : element. click( ) except Exception as e: logger. error( f"页面 { page} 的元素: { locator} 点击失败" ) self. set_error_img( ) raise edef click_elements ( self, locator: tuple , mode: str = "click" , alignment: bool = False , move_element: bool = False , is_double_click: bool = False ) - > None : """点击一组元素:param locator: 元素的定位表达式 例:(By.xx,'定位表达式'):param mode: visible(元素可见),notvisible(元素消失不可见), exist(元素存在),click(元素可点击):param alignment: 默认对齐方式是元素和当前页面的底部对齐,可以传 alignment=''表示和顶部对齐:param move_element: 这里是布尔值 传入True 表示需要让元素滚动到页面可见区域 False 表示不用:param is_double_click: False单击元素,传入True 双击元素:return: 无返回值""" page = self. get_current_url( ) elements = self. find_elements( locator= locator, mode= mode) if move_element is True : self. __move_element_visible( locator= locator, element= elements[ 0 ] , alignment= alignment) try : logger. debug( f"点击: { page} 页面,属性为 { locator} 的元素" ) for element in elements: if is_double_click: ActionChains( self. driver) . double_click( element) . perform( ) else : element. click( ) except Exception as e: logger. error( f"页面 { page} 的元素: { locator} 点击失败" ) self. set_error_img( ) raise edef random_click_element ( self, locator: tuple , num: int , mode: str = "click" , alignment: bool = False , move_element: bool = False ) - > str : """随机点击一个元素:param locator: 元素的定位表达式 例:(By.xx,'定位表达式'):param num: 从第几位元素开始点击:param mode: visible(元素可见), exist(元素存在),click(元素可点击):param alignment: 默认对齐方式是元素和当前页面的底部对齐,可以传 alignment=''表示和顶部对齐:param move_element: 这里是布尔值 传入True 表示需要让元素滚动到页面可见区域 False 表示不用:return: 点击的元素的文本""" page = self. get_current_url( ) elements = self. find_elements( locator= locator, mode= mode) click_num: int = random. randint( num, len ( elements) - 1 ) try : element = elements[ click_num] if move_element is True : self. __move_element_visible( locator= locator, element= element, alignment= alignment) self. element_dyeing( element) logger. debug( f"点击: { page} 页面,属性为 { locator} 的元素中的 { click_num} 位" ) element. click( ) except Exception as e: logger. error( f"页面 { page} 的元素: { locator} 中的第 { click_num} 位元素点击失败" ) self. set_error_img( ) raise ereturn element. textdef input_text ( self, locator: tuple , content: str or int , mode: str = "visible" , alignment: bool = False , move_element: bool = False ) - > WebElement: """输入文本内容:param locator: 传入元素定位表达式:param content: 传入输入的文本内容:param mode: visible(元素可见), exist(元素存在):param alignment: 默认对齐方式是元素和当前页面的底部对齐,可以传 alignment=''表示和顶部对齐:param move_element: 这里是布尔值 传入True 表示需要让元素滚动到页面可见区域 False 表示不用:return: 无返回值""" page = self. get_current_url( ) element = self. find_element( locator= locator, mode= mode) if move_element is True : self. __move_element_visible( locator= locator, element= element, alignment= alignment) try : self. clear_contents( locator= locator, mode= mode, alignment= alignment) logger. debug( f"输入操作: { page} 页面下的属性为: { locator} 的元素,输入内容为 { content} " ) element. send_keys( content) self. driver. execute_script( "arguments[0].setAttribute('style', 'background: write; border: 1px solid black;');" , element) except Exception as e: self. set_error_img( ) logger. error( f"页面 { page} 的属性: { locator} 输入操作失败" ) raise ereturn elementdef clear_contents ( self, locator: tuple , mode: str = "visible" , alignment: bool = False , move_element: bool = False ) - > None : """清除文本内容:param locator: 传入元素定位表达式:param mode: visible(元素可见), exist(元素存在):param alignment: 默认对齐方式是元素和当前页面的底部对齐,可以传 alignment=''表示和顶部对齐:param move_element: 这里是布尔值 传入True 表示需要让元素滚动到页面可见区域 False 表示不用:return: 无返回值""" page = self. get_current_url( ) element = self. find_element( locator= locator, mode= mode) if move_element is True : self. __move_element_visible( locator= locator, element= element, alignment= alignment) try : logger. debug( f"输入操作: { page} 页面下的属性为: { locator} 的元素,清除内容" ) time. sleep( 0.2 ) element. clear( ) except Exception as e: self. set_error_img( ) logger. error( f"页面 { page} 的属性: { locator} 清除操作失败" ) raise edef get_element_text ( self, locator: tuple , mode: str = 'visible' , alignment: bool = False , move_element: bool = False ) - > str : """获取元素的文本内容:param locator: 传入元素定位表达式:param mode: visible(元素可见), exist(元素存在):param alignment: 默认对齐方式是元素和当前页面的底部对齐,可以传 alignment=''表示和顶部对齐:param move_element: 这里是布尔值 传入True 表示需要让元素滚动到页面可见区域 False 表示不用:return: 返回获取到的元素文本内容""" page = self. get_current_url( ) element = self. find_element( locator= locator, mode= mode) if move_element is True : self. __move_element_visible( locator= locator, element= element, alignment= alignment) try : logger. debug( f"文本获取操作:获取 { page} 页面下的属性为: { locator} 的元素的文本内容" ) return element. textexcept Exception as e: logger. error( f"页面 { page} 的元素: { locator} 获取文本操作失败" ) self. set_error_img( ) raise edef click_radios ( self, locator: tuple , method: str , amount: int = None , mode: str = 'visible' , alignment: bool = False , move_element: bool = False ) - > WebElement: """复选框内容点击:param locator: 传入元素定位表达式:param mode: visible(元素可见), exist(元素存在):param amount: 传入复选项的数量 例子如果是3个选项就传入3:param method: 选择对应的内容选择方式 all 点击复选框的全部内容 random 随机点击复选框的中的某一个选项 assign点击指定的某个复选项:param alignment: 默认对齐方式是元素和当前页面的底部对齐,可以传 alignment=''表示和顶部对齐:param move_element: 这里是布尔值 传入True 表示需要让元素滚动到页面可见区域 False 表示不用:return: 全部点击时无返回,其他返回被点击的元素""" page = self. get_current_url( ) elements: list [ WebElement] = self. find_elements( locator= locator, mode= mode) try : logger. debug( '点击方式为:{}' . format ( method) ) if method == 'all' : for ele in elements: if move_element is True : self. __move_element_visible( locator= locator, element= ele, alignment= alignment) ele. click( ) elif method == 'random' : import randomnum = random. randint( 0 , amount - 1 ) element = elements[ num] if move_element is True : self. __move_element_visible( locator= locator, element= element, alignment= alignment) element. click( ) return elementelif method == 'assign' : element = elements[ amount - 1 ] if move_element is True : self. __move_element_visible( locator= locator, element= element, alignment= alignment) element. click( ) return elementelse : logger. error( '点击方式输入错误,请检查' ) except Exception as e: logger. error( f"页面 { page} 的元素: { locator} 复选框点击操作失败" ) self. set_error_img( ) raise e@staticmethod def element_is_selected ( element: WebElement) : """判断元素是否勾选:param element: 需要校验是否勾选的元素:return: 选中是Ture 没有选择是False""" return element. is_selected( ) def select_contents_menu ( self, locator: tuple , text: str , mode: str = 'visible' , alignment: bool = False , move_element: bool = False ) - > None : """选择下拉菜单中的内容:param locator: 传入元素定位表达式:param text: 出入下拉列表需要选择的内容:param mode: visible(元素可见), exist(元素存在):param alignment: 默认对齐方式是元素和当前页面的底部对齐,可以传 alignment=''表示和顶部对齐:param move_element: 这里是布尔值 传入True 表示需要让元素滚动到页面可见区域 False 表示不用""" page = self. get_current_url( ) element = self. find_element( locator= locator, mode= mode) option = [ ] if move_element is True : self. __move_element_visible( locator= locator, element= element, alignment= alignment) try : options = element. find_elements_by_tag_name( "option" ) for value in options: option. append( value) if text in option: Select( element) . select_by_visible_text( text) else : logger. error( f"选项: { text} 不在下拉列表之中请检查" ) except Exception as e: self. set_error_img( ) logger. error( f"页面 { page} 的元素: { locator} 下拉框操作失败请检查" ) raise edef dispose_alert ( self, action: str ) - > str : """处理页面alert:param action: 参数为 accept 点击alert的确定 dismiss点击alert的取消:return: 返回alert的文本内容 可能有些用例需要用这个参数去校验""" WebDriverWait( self. driver, 5 , 0.5 ) . until( ec. alert_is_present( ) ) alert: Alert = self. driver. switch_to. alerttry : if action == 'accept' : alert. accept( ) elif action == 'dismiss' : alert. dismiss( ) else : logger. error( 'alert 处理参数错误请检查' ) return alert. textexcept Exception as e: logger. error( 'alert处理异常' ) raise edef get_text ( self, locator: tuple ) : """获取元素的文本值:param locator: 元素定位:return:""" try : elem = self. driver. find_element( * locator) value = elem. textreturn valueexcept NoSuchElementException as e: print ( f"get未找到元素 { e} " ) raise edef element_dyeing ( self, element) - > None : """将被操作的元素染色:return: None""" self. driver. execute_script( "arguments[0].setAttribute('style', 'background: yellow; border: 2px solid red;');" , element) def set_error_img ( self) - > None : """截图:return: None""" __time_tag = ReturnTime. get_timestamp( ) __img_path = self. __img_dir + f"/ { __time_tag} .png" try : self. driver. save_screenshot( filename= __img_path) logger. error( f"截图成功文件名称为: { __time_tag} .png" ) __file = open ( __img_path, "rb" ) . read( ) allure. attach( __file, "用例执行失败截图" , allure. attachment_type. PNG) except Exception as e: logger. error( f"执行失败截图未能正确添加进入测试报告: { e} " ) raise edef set_case_img ( self) - > None : """用例执行完毕截图,并且将截图加入allure测试报告中:return: 无返回值""" with allure. step( "关键步骤截图" ) : __img_name = ReturnTime. get_timestamp( ) __img_path = self. __img_dir + f"/ { __img_name} .png" try : time. sleep( 1 ) self. driver. save_screenshot( filename= __img_path) logger. debug( f"用例执行完成,截图成功,文件名称为 { __img_name} .png" ) __file = open ( file = __img_path, mode= "rb" ) . read( ) allure. attach( __file, "关键步骤截图" , allure. attachment_type. PNG) except Exception as e: logger. error( f"测试结果截图,未能正确添加进入测试报告: { e} " ) raise edef execute_js ( self, js, * args) : """执行javascript脚本js: 元组形式参数""" self. driver. execute_script( js, * args) return selfdef upload_file_pywinauto ( self, file_path) : """使用pywinauto来上传缺点:只能在windows上使用。优点:可以选择多个文件,路径中有中文也可以。安装:pip install pywinauto -i https://mirrors.aliyun.com/pypi/simple/:param file_path: 文件绝对路径,支持传数组""" if isinstance ( file_path, list ) : for path in file_path: send_keys( path) else : send_keys( file_path) send_keys( "{VK_RETURN}" ) return selfdef upload_file_pyautogui ( self, file_path) : """使用pyautogui来上传缺点:只能选择一个文件,路径中有中文会出问题。优点:跨平台。Linux, mac,windows都可以。安装:pip install pyautogui -i https://mirrors.aliyun.com/pypi/simple/:param file_path: 文件绝对路径,支持传数组""" if isinstance ( file_path, list ) : print ( "只能选择一个文件,默认选择第一个" ) file_path = file_path[ 0 ] pyautogui. write( file_path) pyautogui. press( "enter" , 2 ) return selfdef double_click ( self, locator) : """鼠标双击:param locator::return:""" try : elem = self. driver. find_element( * locator) action = ActionChains( self. driver) action. double_click( elem) . perform( ) return selfexcept NoSuchElementException as e: print ( "未找到元素:{}" . format ( e) ) raise edef drag_and_drop ( self, start_locator, end_locator) : """鼠标拖动""" elem_start = self. driver. find_element( * start_locator) elem_end = self. driver. find_element( * end_locator) action = ActionChains( self. driver) action. double_click( ( elem_start, elem_end) ) . perform( ) return selfdef hover ( self, locator) : """鼠标悬停""" el = self. driver. find_element( * locator) action = ActionChains( self. driver) action. move_to_element( el) . perform( ) return self