常用basepage模块代码
# -*- coding: utf-8 -*-
# @Desc: UI自动化测试的一些基础浏览器操作方法
# 第三方库导入
import time
from logging import config
import random
import allure
from selenium.webdriver.common.alert import Alert
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.select import Select
import 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 WebDriverWait
logger = get_logger(__name__)
g_timeout = 10
g_poll_frequency = 0.2
class BasePage:
"""
UI自动化基础操作封装
"""
def __init__(self, driver):
self.driver = driver
self.__img_dir = config.REPORT_DIR
def refresh(self):
"""刷新网页"""
self.driver.refresh()
def get_url(self, url):
"""
访问指定地址
"""
self.driver.get(url=url)
return self.driver.current_url
def get_current_url(self):
"""
获取当前浏览器驱动的地址
"""
return self.driver.current_url
def 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 e
def 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_time
logger.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 rtn
def 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_time
logger.debug(f"页面:{page}上的元素{locator}已可点击,共计等待{__wait_time:0.2f}秒")
except TimeoutException as e:
logger.error(f"页面:{page},等待元素{locator}超时")
self.set_error_img()
raise e
except InvalidSelectorException as e:
logger.error(f"页面:{page},元素不可点击或定位表达式:{locator}异常\n {e}")
raise e
return rtn
def 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_time
logger.debug(f"页面:{page}上的元素{locator}已存在,共计等待{__wait_time:0.2f}秒")
except TimeoutException as e:
logger.error(f"页面:{page},等待元素{locator}超时")
self.set_error_img()
raise e
except InvalidSelectorException as e:
logger.error(f"页面:{page},元素不存在或定位表达式:{locator}异常\n {e}")
raise e
return rtn
def 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_time
logger.debug(f"页面:{page}上的元素{locator}已经不可见,共计等待{__wait_time:0.2f}秒")
except TimeoutException as e:
logger.error(f"页面:{page},等待元素{locator}不可见超时")
self.set_error_img()
raise e
except InvalidSelectorException as e:
logger.error(f"页面:{page},元素不存在或定位表达式:{locator}异常\n {e}")
raise e
return rtn
def 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 e
def 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_handles
for handle in handles:
self.driver.switch_to.window(handle)
if self.driver.title == title:
return handle
def switch_to_window(self, title):
"""切换到指定窗口"""
self.__get_title_handle(title)
return self
def switch_next_window(self):
"""切换到另一个窗口"""
# 获取所有的窗口
windows = self.driver.window_handles
if len(windows) >= 2:
# 切换窗口
self.driver.switch_to.window(self.driver.window_handles[-1])
return self
def open_new_window(self, url):
"""打开一个新窗口"""
# 获取所有的窗口
start_window = self.driver.window_handls
# 打开新窗口
js = "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 self
def __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 element
except 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_list
except NoSuchElementException as e:
logger.error(f"页面:{page},定位元素:{locator}定位失败")
self.set_error_img()
raise e
def __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:
# 代码执行比页面渲染速度快 这里放0.5秒等待页面渲染
time.sleep(0.5)
self.driver.execute_script('arguments[0].scrollIntoView({0});'.format(alignment), element)
# 休眠1秒让页面可以正常滚动到对应的位置再执行下去
time.sleep(1)
except Exception as e:
logger.error(f"{page}页面的元素:{locator}移动失败\n{e}")
self.set_error_img()
raise e
def 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 e
def 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 e
def 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 e
return element.text
def 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 e
return element
def 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 e
def 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.text
except Exception as e:
logger.error(f"页面{page}的元素:{locator}获取文本操作失败")
self.set_error_img()
raise e
def 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 random
# 生成指定范围之内的随机数作为需要点击的radio
num = 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 element
# 点击复选框中指定位置的选项
elif method == 'assign':
# 因为从0开始计数,所以传入的 amount-1
element = elements[amount - 1]
if move_element is True:
self.__move_element_visible(locator=locator, element=element, alignment=alignment)
element.click()
return element
else:
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 e
def dispose_alert(self, action: str) -> str:
"""
处理页面alert
:param action: 参数为 accept 点击alert的确定 dismiss点击alert的取消
:return: 返回alert的文本内容 可能有些用例需要用这个参数去校验
"""
# 等待alert出现再去操作
WebDriverWait(self.driver, 5, 0.5).until(ec.alert_is_present())
alert: Alert = self.driver.switch_to.alert
# 尝试点击alert
try:
if action == 'accept':
alert.accept()
elif action == 'dismiss':
alert.dismiss()
else:
logger.error('alert 处理参数错误请检查')
return alert.text
except Exception as e:
logger.error('alert处理异常')
raise e
def get_text(self, locator: tuple):
"""
获取元素的文本值
:param locator: 元素定位
:return:
"""
try:
elem = self.driver.find_element(*locator)
value = elem.text
return value
except NoSuchElementException as e:
print(f"get未找到元素{e}")
raise e
def 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 e
def 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:
# 截图前等待1秒防止图片没有正常加载
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 e
# ------------------------ START: JS事件 ------------------------ #
def execute_js(self, js, *args):
"""
执行javascript脚本
js: 元组形式参数
"""
self.driver.execute_script(js, *args)
return self
def 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 self
def 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 self
# ------------------------ END: JS事件 ------------------------ #
# ------------------------ START: 鼠标事件:双击,悬停,拖动 ------------------------ #
def double_click(self, locator):
"""
鼠标双击
:param locator:
:return:
"""
try:
elem = self.driver.find_element(*locator)
action = ActionChains(self.driver)
action.double_click(elem).perform()
return self
except NoSuchElementException as e:
print("未找到元素:{}".format(e))
raise e
def 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 self
def hover(self, locator):
"""鼠标悬停"""
el = self.driver.find_element(*locator)
action = ActionChains(self.driver)
action.move_to_element(el).perform()
return self
文章来源地址https://www.toymoban.com/news/detail-696563.html
文章来源:https://www.toymoban.com/news/detail-696563.html
到了这里,关于UI界面自动化BagePage的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!