def get_by_type(self, types): """ 获取定位类型 :param types: str in(id,name,xpath,css,class,link,partlink,tag) :return: False """ locatorType = types.lower() if locatorType == "id": return By.ID elif locatorType == "name": return By.NAME elif locatorType == "xpath": return By.XPATH elif locatorType == "css": return By.CSS_SELECTOR elif locatorType == "class": return By.CLASS_NAME elif locatorType == "link": return By.LINK_TEXT elif locatorType == "partlink": return By.PARTIAL_LINK_TEXT elif locatorType == "tag": return By.TAG_NAME else: logger.info(f"Locator type {locatorType} not correct/supported") raise Exception('定位类型错误!!!!')
def run(cls): """ 正式运行所有脚本 :return: """ # 执行前检查是否清除报告 #del_clean_report() # 接收参数 results_dir, module_name, mlist, thread_num, reruns = cls.receiving_argv( ) # 生成测试结果目录 prpore_json_dir, prpore_allure_dir = cls.output_path(results_dir) # 判断运行模块 run_modle = cls.run_modle(module_name, thread_num, reruns, mlist, prpore_json_dir) # 生成测试报告 if run_modle: os.system( f'allure generate {prpore_json_dir} -o {prpore_allure_dir} --clean' ) logger.info('测试报告生成完成!') html_index = os.path.join(prpore_allure_dir, 'index.html') logger.info(html_index) return html_index
def refresh(self): """ 刷新当前页面 :return: """ logger.info('刷新当前页面') return self.driver.refresh()
def back(self): """ 返回上一个页面 :return: """ self.driver.back() logger.info('返回上一个页面')
def forward(self): """ 前进到下一个页面 :return: """ self.driver.forward() logger.info('前进到下一个页面')
def save_as_img(self, types, locate, filename, sleep=1): """ 图片另存为 下载文件也可以直接使用 :param locatorType: 定位类型 :param locate: 定位器 :param filename: 图片名称 路径必须要输入正确 以为函数没办法判断是否成功 :param sleep: 等待windo 窗口时间 默认 1 秒 :return: str path 文件路径 """ # 右键点击 self.used_right_click(types=types, locate=locate) # 图片另存为 pyautogui.typewrite(['V']) # 将地址以及文件名复制 pic_dir = os.path.join(PRPORE_SCREEN_DIR, f'{filename}.jpg') pyperclip.copy(pic_dir) # 等待窗口打开,以免命令冲突,粘贴失败,试过很多次才有0.8,具体时间自己试 time.sleep(sleep) # 粘贴 pyautogui.hotkey('ctrlleft', 'V') # 保存 pyautogui.press('enter') logger.info(f'图片路径为{filename}!') return pic_dir
def upload_files(self, types, locate, filepath, sleep=1): """ 文件上传 :param locatorType: 定位类型 :param locate: 定位器 :param filepath: 文件路径 路径必须要输入正确 以为函数没办法判断是否成功 :param sleep: 等待windo 窗口时间 默认 1 秒 :return: """ # pyautogui.write(filepath) # 不支持中文路径 # 支持中文路径 if sys.platform.lower() == 'win32': import pyautogui, pyperclip self.used_right_click(types, locate) self.sleep(sleep) pyperclip.copy(filepath) self.sleep(sleep) pyautogui.hotkey('ctrl', 'v') pyautogui.press('enter', presses=2) logger.info(f'上传文件路径{filepath}') return True return False
def __if_commonly_used_predicate(self, types, locate, operate=None, text=None, el=None, index=None): """ * 私有方法不继承 判断 CommonlyUsed 执行操作 :param locate: 表达 或者定位元素 :param operate: 执行操作 类型input(输入) , clear(清除) , clear_continue_input(清除在输入) 、click(点击) ,text(提取文本) , :param text: 输入文本内容 :param el: 输入文本内容 :return: """ if operate is None: return self.used_operate(types=types, locate=locate, el=el) if operate in ('text', 'click', 'input', 'clear', 'clear_continue_input'): if operate == 'text': # 提取文本 return self.used_text(types=types, locate=locate, el=el, index=index) elif operate == 'click': # 点击操作 self.used_click(types=types, locate=locate, el=el, index=index) elif operate == 'input': # 输入操作 if text is not None: return self.used_input(types=types, locate=locate, text=text, el=el, index=index) logger.error(' 函数必须传递 text 参数') elif operate == 'clear': # 清除操作 return self.used_clear(types=types, locate=locate, el=el, index=index) elif operate == 'clear_continue_input': # 清除后在输入操作 if text is not None: return self.used_clear_continue_input(types=types, locate=locate, text=text, el=el, index=index) logger.info(' 函数必须传递 text 参数') else: logger.error(f'输入的{operate}暂时不支持此操作!!!') logger.error(""" 目前只支持类型 : input(输入) , clear(清除) , clear_continue_input(清除在输入) 、click(点击) ,text(提取文本) """) raise ErrorExcep(f'输入的{operate}暂时不支持此操作!!!')
def all_handle(self): """ 获取所有句柄 :return: list """ handle = self.driver.window_handles logger.info(f'获取所有句柄 {handle}') return handle
def get_title(self): """ 获取当前页面 title :return: """ title = self.driver.title logger.info(f"获取当前title {title}") return title
def current_window(self): """ 获取当前窗口句柄 不能单一使用 实际获取的不是当前句柄 :return: """ current_window = self.driver.current_window_handle logger.info(f'获取当前句柄 {current_window}') return current_window
def sleep(s: float): """ 休眠秒数 :param s: :return: """ time.sleep(s) logger.info('强制休眠{}'.format(s))
def get_url(self): """ 获取当前页面的URL :return: """ currentURL = self.driver.current_url logger.info(f"获取当前url {currentURL}") return currentURL
def test_index(self, one_browser): """ 登录用列 :param one_browser: :return: """ with allure.step('登录用户'): logger.info('test_login') Baidu(one_browser).index()
def decide_appos(self): # 判断移动系统选择参数 if self.appos == 'ios': return IOS_CAPA elif self.appos == 'android': return ANDROID_CAPA else: logger.info('不支持此移动系统!') return None
def accept(self): """ 警告框处理 确认 :return: """ try: accept = self.driver.switch_to.alert.accept() logger.info('警告框已确认') return accept except Exception as e: logger.error("查找alert弹出框异常-> {0}".format(e))
def dismiss(self): """ 警告框处理 取消 :return: """ try: accept = self.driver.switch_to.alert.dismiss() logger.info('警告框已取消') return accept except Exception as e: logger.error("查找dismiss弹出框异常-> {0}".format(e))
def alertText(self): """ 警告框处理 提取警告框文本 :return: """ try: accept = self.driver.switch_to.alert.text logger.info(f'警告框文本信息为 {accept}') return accept except Exception as e: logger.error("查找alert弹出框异常-> {0}".format(e))
def element_hover(self, types, locate): """ 获取元素后悬停到元素位置 :param locatorType: 定位类型 :param locate: 定位器 :return: """ element = self.used_operate(types, locate) hover = ActionChains(self.driver).move_to_element(element).perform() logger.info(f"鼠标悬停位置{locate}") return hover
def web_scroll(self, direction): """ 网页滚动 :param direction: str up 向上 Down 向下 :return: """ if direction == "up": logger.info('滚动到顶部') self.driver.execute_script("window.scrollBy(0, -10000);") if direction == "down": logger.info('滚动到底部') self.driver.execute_script("window.scrollBy(0, 10000)")
def element_hover_clicks(self, types, locate): """ 获取元素后悬停到元素位置 后点击该元素 :param locatorType: 定位类型 :param locate: 定位器 :return: """ element = self.used_operate(types, locate) ActionChains(self.driver).move_to_element(element).perform() time.sleep(0.5) self.used_double_click(types=types, locate=locate) logger.info(f"鼠标悬停位置{locate}")
def switch_windows(self, index): """ 多窗口切换 :param index: 列表索引 all_handle的列表索引位置 :return: """ indexHandle = self.all_handle()[index] try: logger.info(f'窗口已经切换{indexHandle}') return self.driver.switch_to_window(indexHandle) except Exception as e: logger.error("查找窗口句柄handle异常-> {0}".format(e))
def swipe_down(self, swipe_times=1): """ 向下滑动 :param swipe_times: :return: """ logger.info("向下滑动" + str(swipe_times) + "次") size = self.driver.get_window_size() width = size["width"] height = size["height"] for i in range(0, swipe_times): self.driver.swipe(width * 0.5, height * 0.8, width * 0.5, height * 0.2) time.sleep(0.5)
def pHash(cls, img1: str, img2: str) -> int: """ 差值哈希算法对比 :param img1: 图片1 :param img2: 图片2 :return: """ try: hash1 = AlgorithmClassify.pHash(cv2.imread(img1)) hash2 = AlgorithmClassify.pHash(cv2.imread(img2)) n = cls.cmpHash(str(hash1), str(hash2)) logger.info(f'感知哈希算法相似度:{n}') return n except Exception as e: logger.error(f'对比差感知哈希错误:{e}')
def isElementExist(self, types, locate): """ 检查元素是否存在 :param types: 定位类型 used_operate 函数传递过来 :param locate: 定位器 :return: """ if self.waitForElement(types, locate): elementList = self.driver.find_elements(types, locate) if len(elementList) > 0: logger.info(f"找到元素 {locate}") return True else: logger.info("元素未找到") return False
def screen_shot(self, doc): """ 截取当前界面图片 :param doc: str 名称 :return: """ fileName = doc + "." + str(round(time.time() * 1000)) + ".png" if len(fileName) >= 200: fileName = str(round(time.time() * 1000)) + ".png" filePath = os.path.join(PRPORE_SCREEN_DIR, fileName) self.driver.save_screenshot(filePath) allure.attach(self.driver.get_screenshot_as_png(), name=fileName, attachment_type=allure.attachment_type.PNG) logger.info(f"截图成功已经存储在: {filePath}")
def waitForElement(self, types, locate): """ 等待元素被加载 配合 isElementExist 函数检查元素是否存在 :param types: 定位类型 used_operate 函数传递过来 :param locate: 定位器 :return: """ timeout = IMPLICITLY_WAIT_TIME poll = POLL_FREQUENCY try: wait = WebDriverWait(self.driver, timeout, poll_frequency=poll) element = wait.until(EC.presence_of_element_located((types, locate))) logger.info(f'等待页面元素 {locate} {types} 存在') return element except Exception as e: logger.error('等待元素错误,元素在等待时间内未出现!') logger.error(e)
def classify_hist_with_split(cls, image1: str, image2: str, size=(256, 256)) -> float: """ 通过得到RGB每个通道的直方图来计算相似度 将图像resize后,分离为RGB三个通道,再计算每个通道的相似值 :param image1: 图片1 :param image2: 图片2 :param size: :return: """ image1 = cv2.resize(cv2.imread(image1), size) image2 = cv2.resize(cv2.imread(image2), size) sub_image1 = cv2.split(image1) sub_image2 = cv2.split(image2) sub_data = 0 for im1, im2 in zip(sub_image1, sub_image2): sub_data += AlgorithmClassify.calculate(im1, im2) sub_data = sub_data / 3 logger.info(f'三直方图算法相似度:{sub_data}') return sub_data
def isElementDisplayed(self, types, locate): """ 检查元素是否可见 :param types:定位类型 :param locate: 定位器 :param element: :return: """ isDisplayed = False element = None if locate: element = self.used_operate(types, locate) if element is not None: isDisplayed = element.is_displayed() logger.info(f"Element is displayed with locate: {locate} and types: {types}") else: logger.error(f"Element is not displayed with locate: {locate} and types: {types}") return isDisplayed
def run_bebug(): """ bebug 时调试试验 :return: """ # 执行前检查是否清除报告 del_clean_report() pytest.main([ '-s', '-v', '-n=1', '--reruns=0', '-W', 'ignore:Module already imported:pytest.PytestWarning', '--alluredir', f'{PRPORE_JSON_DIR}', f'{CASE_DIR}' ]) os.system( f'allure generate {PRPORE_JSON_DIR} -o {PRPORE_ALLURE_DIR} --clean' ) logger.info('测试报告生成完成!')