def find_element(self, locator, condition='presence', retries=1): """ 通过等待条件定位元素 :param locator: 定义元组。例:(By.ID, '//*[@id="kw"]') :param condition: 等待条件 :param retries: 重试次数,默认1 :return: node or None """ error_info = None for times in range(retries + 1): try: LOGGER.debug(f'定位元素:{locator}') if condition == 'visibility': # 等待节点可见 node = self.wait.until( EC.visibility_of_element_located(locator)) else: # 等待节点加载出来 node = self.wait.until( EC.presence_of_element_located(locator)) break except Exception as ex: error_info = f'定位 {locator} 失败,错误信息:{ex}' LOGGER.error(error_info) if times < retries: LOGGER.warning(f'正在重试,当前重试次数:{times},总数:{retries}') time.sleep(1) else: raise Exception(error_info) return node
def __init__(self, driver='Chrome', enable_headless=False, enable_no_picture=False, enable_maximize_window=False): """ Browser driver初始化 :param driver: 浏览器驱动 :param enable_headless: 是否启动无界面模式,默认False :param enable_no_picture: 是否不加载图片,加快访问速度,默认False :param enable_maximize_window: 是否最大化窗口,默认False """ # 浏览器选项配置 options = webdriver.ChromeOptions() if enable_headless: options.add_argument('--headless') if enable_no_picture: options.add_experimental_option( "prefs", {"profile.managed_default_content_settings.images": 2}) if enable_maximize_window: options.add_argument("--start-maximized") # 选择浏览器驱动 if driver == 'Firefox': self.driver = webdriver.Firefox(options=options) elif driver == 'Ie': self.driver = webdriver.Ie(options=options) else: self.driver = webdriver.Chrome(options=options) LOGGER.debug(f'加载浏览器驱动:{driver}') self.wait = WebDriverWait(self.driver, 30) # 设置显示等待30秒 self.driver.implicitly_wait(30) # 设置隐示等待30秒 self.actions = webdriver.ActionChains(self.driver) # 动作链初始化
def send_mail(self, sender, receiver, subject, body, attachments=[]): """ 发送邮件 QQ邮箱 smtp_password 获取方式:进入QQ邮箱-设置-账户-开启服务-开启POP3/SMTP服务,然后点击生成授权码 :param sender: 发件人 :param receiver: 收件人 :param subject: 邮件主旨 :param body: 邮件正文(超文本格式) :param attachments: 附件列表 :return: """ mail_obj = self.build_mail_object(sender, receiver, subject, body, attachments) try: # 登陆smtp服务器 sftp_obj = smtplib.SMTP(self.smtp_server, self.smtp_port) sftp_obj.login(user=self.smtp_user, password=self.smtp_password) # 发送邮件 sftp_obj.sendmail(sender, receiver, mail_obj.as_string()) LOGGER.debug(f'发送邮件成功,发件人:{sender},收件人:{receiver},主旨:{subject}') # 退出登陆 sftp_obj.quit() except Exception as ex: LOGGER.error( f'发送邮件失败,错误原因:{ex},发件人:{sender},收件人:{receiver},主旨:{subject}')
def add_cookies(self, cookies={}): """ 添加cookies到driver :param cookies: 字典类型 :return: """ self.driver.add_cookie(cookies) LOGGER.debug(f'添加cookies:{cookies}')
def open_windows(self, url=''): """ 请求URL,打开windows窗口 :param url: :return: """ self.driver.get(url) LOGGER.debug(f'打开网址:{url}') time.sleep(1)
def open_new_windows(self, url=''): """ 打开一个新的windows窗口 :param url: URL :return: """ js = "window.open({})".format(url) LOGGER.debug(f'打开新网址:{url}') self.driver.execute_script(js) time.sleep(1)
def click_button(self, locator): """ 点击按钮 :param locator: :return: """ node = self.find_element(locator) LOGGER.debug('点击按钮') node.click() time.sleep(1)
def screenshot(self, storage_path, picture_name): """ 截取当前网页并保存为图片 :param storage_path: 图片存储的路径 :param picture_name: 图片名称 :return: """ current_times = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') storage = os.path.join(storage_path, f'{current_times}_{picture_name}.jpg') self.driver.save_screenshot(storage) LOGGER.debug(f'截取当前页面,图片保存路径:{storage}')
def popup_window_operation(self, action='yes', send_info='', get_window_info=False): """ 弹窗操作 :param action: 要执行的动作,yes or no :param send_info: 在弹窗的文本框内输入信息 :param get_window_info: 获取弹窗的文本信息 :return: """ if send_info: LOGGER.debug(f'在弹窗上输入信息:{send_info}') self.driver.switch_to.alert.send_keys(send_info) if get_window_info: popup_info = self.driver.switch_to.alert.text LOGGER.debug(f'获取弹窗的文本信息:{popup_info}') return popup_info if action == 'yes': LOGGER.debug('在弹窗上点击确认') self.driver.switch_to.alert.accept() # 点击确认 else: LOGGER.debug('在弹窗上点击取消') self.driver.switch_to.alert.dismiss() # 点击取消 time.sleep(1)
def switch_to_windows(self, to_parent_windows=False): """ 切换到不同的windows窗口 :param to_parent_windows: 是否回到主窗口,默认False :return: """ total = self.driver.window_handles if to_parent_windows: self.driver.switch_to.window(total[0]) else: current_windows = self.driver.current_window_handle for window in total: if window != current_windows: self.driver.switch_to.window(window) LOGGER.debug(f'切换windows窗口')
def wrapper(*args, **kwargs): browser_driver = kwargs.get('browser') # 获取driver func_name = func.__name__ # 获取测试函数名 start_time = datetime.datetime.now() LOGGER.debug(f'用例({func_name})执行时间:{start_time}') try: func(*args, **kwargs) # 执行测试函数 except Exception as ex: LOGGER.error(f'用例({func_name})执行失败,错误信息:{ex}') # 截图保存 browser_driver.screenshot(storage_path=MODULE_DIR['failure_screenshot'], picture_name=func_name) end_time = datetime.datetime.now() LOGGER.debug(f'用例({func_name})结束时间:{start_time},耗时:{(end_time - start_time).total_seconds()}s')
def action_chain(self, source, target): """ 执行鼠标拖曳 Example: source = self.driver.find_element_by_xpath('//*[@id="result_logo"]/img[1]') target = self.driver.find_element_by_xpath('//*[@id="kw"]') self.action_chain(source=source, target=target) :param source: 拖曳前位置 :param target: 拖曳后位置 :return: """ self.actions.drag_and_drop(source, target) self.actions.perform() LOGGER.debug(f'执行鼠标拖曳,拖曳前位置:{source},拖曳后位置:{target}') time.sleep(1)
def send_keys(self, locator, value, directly_enter=False): """ 输入信息到文本框 :param locator: 定位元组 :param value: 输入值 :param directly_enter: 是否直接回车,默认False :return: """ node = self.find_element(locator) node.clear() # 清空文本 node.send_keys(value) # 输入值 LOGGER.debug(f'输入值:{value}') if directly_enter: node.send_keys(Keys.ENTER) # 回车 LOGGER.debug('执行回车') time.sleep(1)
def switch_to_frame(self, index=0, to_parent_frame=False, to_default_frame=False): """ 切换到不同的frame框架 :param index: expect by frame index value or id or name or element :param to_parent_frame: 是否切换到上一个frame,默认False :param to_default_frame: 是否切换到最上层的frame,默认False :return: """ if to_parent_frame: self.driver.switch_to.parent_frame() elif to_default_frame: self.driver.switch_to.default_content() else: self.driver.switch_to.frame(index) LOGGER.debug(f'切换frame,to:{index}')
def page_scrolling(self, go_to_bottom=False, rolling_distance=(0, 1000), wait_time=5): """ 页面滚动,如果没有滚动效果,添加延时(页面需要全部加载完毕才能滚动) :param bool go_to_bottom: 是否直接滚动到当前页面的最底部,默认False :param tuple rolling_distance: 滚动距离,默认是向下滚动1000像素 :param int wait_time: 滚动前的页面等待时间,默认5秒 :return: """ time.sleep(wait_time) if go_to_bottom: js = "window.scrollTo(0, document.body.scrollHeight)" else: js = "window.scrollBy({}, {})".format(rolling_distance[0], rolling_distance[1]) self.driver.execute_script(js) LOGGER.debug(f'页面滚动完毕')
def build_mail_object(sender, receiver, subject, body, attachments=[]): """ 构建邮件对象(主旨、正文、附件) :param sender: 发件人 :param receiver: 收件人 :param subject: 邮件主旨 :param body: 邮件正文(超文本格式) :param attachments: 附件列表 :return: mail obj """ # 构造邮件头信息 mail_obj = MIMEMultipart('mixed') mail_obj['From'] = sender mail_obj['To'] = ','.join(receiver) if isinstance(receiver, list) else receiver mail_obj['subject'] = Header(subject, 'utf-8') # 构造邮件正文(使用超文本格式) html_obj = MIMEText(body, 'html', 'utf-8') mail_obj.attach(html_obj) if attachments: # 添加附件到邮件中 for each_att in attachments: if not os.path.exists(each_att): LOGGER.error( f'添加附件失败,请检查文件:({each_att}),发件人:{sender},收件人:{receiver},主旨:{subject}' ) continue att_file = open(each_att, 'rb') file_obj = MIMEText(att_file.read(), 'base64', 'utf-8') att_file.close() file_obj["Content-Type"] = 'application/octet-stream' file_obj["Content-Disposition"] = 'attachment;filename="{}"' \ .format(Header(os.path.basename(each_att).encode('utf-8'), 'utf-8')) mail_obj.attach(file_obj) LOGGER.debug( f'添加附件成功,文件:({each_att}),发件人:{sender},收件人:{receiver},主旨:{subject}' ) return mail_obj
def switch_to_windows(self, to_parent_windows=False): """ 切换到不同的浏览器窗口 :param to_parent_windows: 是否回到主窗口,默认False :return: """ total = self.driver.window_handles LOGGER.debug(f'浏览器所有窗口;{total}') if to_parent_windows: LOGGER.debug(f'切换到第一个窗口') self.driver.switch_to.window(total[0]) else: current_windows = self.driver.current_window_handle for window in total: if window != current_windows: LOGGER.debug(f'切换浏览器窗口') self.driver.switch_to.window(window)
def back(self): """网页后退""" self.driver.back() LOGGER.debug('网页后退')
def wrapper(*args, **kwargs): func_name = func.__name__ # 获取测试函数名 LOGGER.debug(f'开始执行用例:{func_name}') LOGGER.debug('测试数据' + '*' * 50) for each in args[1:]: # 打印所有测试数据 LOGGER.debug(each) LOGGER.debug('*' * 58) start_time = datetime.datetime.now() LOGGER.debug(f'执行时间:{start_time}') try: func(*args, **kwargs) # 执行测试函数 except Exception as ex: LOGGER.error(f'执行失败,错误信息:{ex}') end_time = datetime.datetime.now() LOGGER.debug( f'结束时间:{start_time},耗时:{(end_time - start_time).total_seconds()}s' )
def forward(self): """网页前进""" self.driver.forward() LOGGER.debug('网页前进')
def quit_browser(self): """关闭所有页面,退出浏览器""" if self.driver: self.driver.quit() LOGGER.debug('退出浏览器')
def close_current_windows(self): """关闭当前页面""" if self.driver: self.driver.close() LOGGER.debug(f'关闭当前页面')
def refresh(self): """网页刷新""" self.driver.refresh() LOGGER.debug('网页刷新')
def delete_cookies(self): """删除当前页面所有的cookies""" self.driver.delete_all_cookies() LOGGER.debug(f'删除所有cookies')