def find(self, by, locator=None, timeout=20, *args, **kargs) -> WebElement: ''' 判断元素是否可点击 :param by: :param locator: :return: ''' logger.debug(f'查找元素:({by},{locator})') # time.sleep(0.5) # 手动延时,避免点击过快 if locator is None: try: result = WebDriverWait(self.driver, timeout).until( expected_conditions.element_to_be_clickable(*by)) except Exception: result = WebDriverWait(self.driver, timeout).until( expected_conditions.presence_of_element_located(*by)) else: try: result = WebDriverWait(self.driver, timeout).until( expected_conditions.element_to_be_clickable((by, locator))) except Exception: result = WebDriverWait(self.driver, timeout).until( expected_conditions.presence_of_element_located( (by, locator))) logger.debug(f'查找元素结果:{result}') return result
def server(self): """ netstat -ano | findstr xxxx :return: """ self.kill_server() # 循环每一个手机配置 for device in self.devices.get(self.device_type): # print(device) # 处理异常手机名称无法生成log的问题 flag = self.tool.exce_device_name(device.get('deviceName')) if flag == 0: # 真机增加以手机名 命名的log地址 device.update({'log_path': os.path.join(LOGPATH, device.get('deviceName') + '.log')}) else: # 夜游神虚拟机更改log名称问题 device.update({'log_path': os.path.join(LOGPATH, flag + '.log')}) # 提取校验服务启动成功的端口 self.ports.append(device.get('port')) logger.debug('配置参数:%s' % device) # 启动多线程开启服务 t = threading.Thread(target=self.server_command, kwargs=device) self.threads_server.append(t) t.start() for t in self.threads_server: t.join() return None
def open_by_yaml(self, path, url_name, func_name='url'): """ 打开连接 Args: path:yml文件路径 url_name:路径名 func_name:路径所在的方法名 Returns: """ env = self.get_env with open(path, encoding='UTF-8') as f: datas = yaml.safe_load(f) data_env = datas['env'] if env in data_env: url_base = data_env[env] logger.debug(f'在配置中找到环境{env},开始运行') else: logger.error(f'在配置中未找到环境{env},请配置该环境活动确认环境是否正确') raise Exception('环境有误!') steps = datas[func_name] for step in steps: if url_name in step: url_relative = step[url_name] url = url_base + url_relative logger.debug(f'打开链接:{url}') return self.driver.get(url) else: logger.error(f'链接打开失败,请检查链接名{url_name}是否正确!') raise Exception(f'链接打开失败,请检查链接名{url_name}是否正确!')
def open(self, url): ''' 打开连接 :param url: :return:url_name ''' logger.debug(f'打开链接{url}') self.driver.get(url)
def sendMail(): content = ''' hi ALL : UI自动化执行完成,请见报告。 tester ''' config = Configure().read(ConfType.MAIL) title = '主流程回归' # 发送的文件 filepath = getError.getpng() # 邮件发送者 sender = config.get('sender') # 邮件发送者用户名 senderName = config.get('username') # 邮件发送者密码 senderPwd = config.get('passwd') # 邮件接收人 receiverList = eval(config.get('receiverlist')) # 发送时间 timeStr = time.strftime("%Y%m%d%H%M%S", time.localtime()) # 标题 subject = u"【UI自动化测试报告】" + title + "_" + timeStr # 邮件对象 msg = MIMEMultipart() # 你所发的文字信息将以html形式呈现 part = MIMEText(content, _subtype='html', _charset="utf-8") msg.attach(part) for v in filepath: if v[0].endswith('.html'): logger.debug('打开的文件: %s' % v[1]) part = MIMEText(open(v[1], 'r', encoding='utf8').read()) part["Content-Type"] = 'application/octet-stream' part["Content-Disposition"] = 'attachment; filename="%s"' % v[0] # 这里的filename可以任意写,写什么名字,邮件中显示什么名字 msg.attach(part) elif v[0].endswith('.png'): part = MIMEImage(open(v[1], 'rb').read()) part["Content-Type"] = 'application/octet-stream' part["Content-Disposition"] = 'attachment; filename="%s"' % v[0] # 这里的filename可以任意写,写什么名字,邮件中显示什么名字 msg.attach(part) msg['Subject'] = Header(subject, 'utf-8') # 来自... msg['From'] = sender # 给谁... if len(receiverList) > 1: msg['To'] = ";".join(receiverList) else: msg['To'] = receiverList[0] logger.info('邮件接收LIST: %s' % msg['To']) try: smtp = smtplib.SMTP() smtp.connect(config.get('smtpserver'), 25) # 连接至邮件服务器 smtp.login(senderName, senderPwd) # 登录邮件服务器 smtp.sendmail(sender, receiverList, msg.as_string()) # 发送邮件 except Exception as e: print(e)
def js(self, script): ''' 执行js脚本. Usage: driver.js("window.scrollTo(200,1000);") ''' logger.debug(f'执行脚本:{script}') self.driver.execute_script(script)
def parse(self, steps, *args, **kargs): ''' 操作元素 :param steps: :param args: :param kargs: :return: ''' for step in steps: by = step['by'] locator = step['locator'] action = step['action'] skip = step.get("skip") logger.debug(f'对元素({by},{locator}),进行{action}操作') action = action.split(',') try: if 'clear' in action: # 清除输入框 self.find(by, locator, *args, **kargs).clear() if 'click' in action: # 点击元素 self.find(by, locator, *args, **kargs).click() if 'send_keys' in action: # 输入内容 self.find(by, locator, *args, **kargs).send_keys( self.function_analysis(step['context'])) if 'right_click' in action: # 右击 el = self.find(by, locator, *args, **kargs) ActionChains(self.driver).context_click(el).perform() if 'double_click' in action: # 双击 el = self.find(by, locator, *args, **kargs) ActionChains(self.driver).double_click(el).perform() if 'drag_and_drop' in action: # 拖拽 el = self.find(by, locator, *args, **kargs) target = self.find(step['by'], step['locator_target'], *args, **kargs) ActionChains(self.driver).drag_and_drop(el, target).perform() if 'clicks' in action: # 点击所有元素 eles = self.finds(by, locator, *args, **kargs) # 对元素集合分别做点击操作 for ele in eles: # 部分元素无法点击时,不报错 try: ele.click() except Exception: pass except TimeoutException: if skip: logger.info(f"元素集({by},{locator})为空,跳过不处理") else: raise TimeoutException( f"元素集({by},{locator})为空,无法跳过,请检查元素是否存在") except Exception as e: logger.debug(f'对元素({by},{locator}),进行{action}操作时出现错误:{e}') raise e
def parse_yaml(self, path, func_name, *args, **kargs): ''' 解析yml文件数据 :param path: :param func_name: :param args: :param kargs: :return: ''' logger.debug(f'解析yml文件的路径:{path},方法名:{func_name}') with open(path, encoding='UTF-8') as f: datas = yaml.safe_load(f) self.parse(datas[func_name], *args, **kargs)
def search(self): results = {} if self._modules: for name, module in self._modules: try: poc_info = module.MyPoc.poc_info except Exception: logger.debug('Get "%s" poc_info failed' % name) continue poc_desc = poc_info['poc'] if re.search(self.keyword, poc_desc['name'], re.I): results[name] = poc_desc['name'] return results
def search(self): results = {} if self._modules: for name, module in self._modules: try: poc_info = module.MyPoc.poc_info except Exception: logger.debug('Get "%s" poc_info failed' % name) continue poc_desc = poc_info['poc'] if re.search(self.keyword, poc_desc['name'], re.I): results[name] = poc_desc['name'] return results
def find(self, by, locator=None, timeout=10, *args, **kargs) -> WebElement: ''' 判断元素是否可点击 :param by: :param locator: :return: ''' logger.debug(f'find.by:{by}') logger.debug(f'find.locator:{locator}') if locator is None: result = WebDriverWait(self.driverriver, timeout=timeout).until( expected_conditions.element_to_be_clickable(*by)) else: result = WebDriverWait(self.driverriver, timeout=timeout).until( expected_conditions.element_to_be_clickable((by, locator))) return result
def case(): # 通过导入测试类来实现生成测试集 suite = unittest.TestLoader().loadTestsFromTestCase(AppDemo) # 实例化结果对象 # 生成一个空的结果集 local_case.r = Result() # 运行case,并更新结果集,记录正确的case 失败的case res = suite.run(local_case.r) # 将结果通过测试手机名称分割 logger.debug('当前线程的的名字:%s'%threading.current_thread().getName()) res = {threading.current_thread().getName(): res} # 处理夜游神名称问题 直接从源头解决 在配置devicesname时如果为127.0.0.1:62001 则直接update名字 for deviceName, result in res.items(): html = HTMLTestAppRunner.HTMLTestRunner(stream=open(APPREPORT + '{}.html'.format(deviceName), "wb"), verbosity=2, title='测试') # 这个方法就是生成报告的主要函数 html.generateReport('', result)
def finds(self, by, locator=None, timeout=20, *args, **kargs) -> list[WebElement]: ''' 判断元素是否可点击 :param by: :param locator: :return: ''' logger.debug(f'查找元素集:({by},{locator})') if locator is None: elements: list[WebElement] = WebDriverWait( self.driver, timeout).until(lambda x: x.find_elements(*by)) else: elements: list[WebElement] = WebDriverWait( self.driver, timeout).until(lambda x: x.find_elements(by, locator)) logger.debug(f'查找元素结果集:{elements}') return elements
def kill_server(): """ 夜游神模拟器如果重启 adb服务后将不被 adb devices所查找 :return: """ if sys.platform == 'win': logger.debug('执行[KILL SERVER]操作:%s' % subprocess.getoutput("taskkill /F /IM node.exe /t")) else: logger.debug('执行[KILL SERVER]操作:%s' % subprocess.getoutput("pkill -9 node")) logger.debug('重启ADB服务!%s' % subprocess.getoutput("adb kill-server"))
def get_env(self): # 获取测试环境 try: env = os.environ["env"] logger.debug(f'本次UI自动化运行环境为{env}环境') except KeyError: env = self.env if env is None: env = 'prod' logger.debug(f'没有配置测试环境, 默认在生产环境{env}进行自动化测试') else: logger.debug(f'本次UI自动化运行环境为{env}环境') return env
def get_remote(self): # 获取远程节点 remote_url = None try: remote = self.remote if remote: remote_url = self.remote_url if remote_url is None: remote_url = os.environ.get("remote") logger.debug(f'获取到远程节点{remote_url}') else: remote = os.environ.get("remote") if remote: remote_url = os.environ["remote_url"] logger.debug(f'获取到远程节点{remote_url}') else: logger.debug('不启用远程节点配置') except Exception: logger.debug('获取远程节点出现未知异常!') raise Exception return remote_url
def __init__(self, driver: WebElement = None, browser='chrome', env=None, remote=False, remote_url=None): ''' 运行类初始化方法,默认使用来Chrome浏览器。当然,你也可以传递参数为其他浏览器。 ''' self.env = env self.remote = remote self.remote_url = remote_url self.browser = browser browser = self.get_browser if driver == None: remote_url = self.get_remote if remote_url is not None: driver = webdriver.Remote(remote_url) elif browser == "firefox" or browser == "ff": driver = webdriver.Firefox() elif browser == "chrome": option = webdriver.ChromeOptions() option.add_argument("--start-maximized") driver = webdriver.Chrome(chrome_options=option) elif browser == "internet explorer" or browser == "ie": driver = webdriver.Ie() elif browser == "opera": driver = webdriver.Opera() elif browser == "phantomjs": driver = webdriver.PhantomJS() elif browser == 'edge': driver = webdriver.Edge() try: if remote: logger.debug(f'本次使用远程hub节点{remote_url}运行行自动化测试') else: logger.debug(f'本次使用{browser}浏览器进行自动化测试') self.driver = driver self.driver.maximize_window() except Exception: logger.debug( "Not found %s browser,You can enter 'ie', 'ff', 'opera', 'phantomjs', 'edge' or 'chrome'." % browser) raise NameError( "Not found %s browser,You can enter 'ie', 'ff', 'opera', 'phantomjs', 'edge' or 'chrome'." % browser) else: self.driver = driver
def test_server(self): while True: for port in self.ports: # 通过查看是否有返回值来确定是否启动 if sys.platform == 'win': test_out_put = subprocess.getoutput("netstat -ano | findstr %s" % port) else: test_out_put = subprocess.getoutput("netstat -an | grep %s" % port) # 如果有 则从list中删除整个端口 直到这个list为空时 代表启动成功 跳出循环 if test_out_put: logger.debug('检验服务启动:%s' % test_out_put) self.ports.remove(port) else: logger.debug('端口 [%s] 服务启动失败5秒钟后尝试' % port) if not self.ports: break time.sleep(5) logger.debug('全部服务启动成功!') return True