class BasePage(object):
    """页面元素基本操作,page module"""
    web_case_num = 0  # web用例编号
    web_driver = None  # 用于标记web用例
    app_driver = None  # 用于标记APP用例
    current_driver = ""  # 用于标记当前是web端还是APP端的用例
    _instance_lock = threading.Lock()  # 设置单例锁

    def __new__(cls, *args, **kwargs):
        """单例模式(支持多线程)"""
        if not hasattr(cls, "_instance"):
            with cls._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self):
        try:
            self.config = ReadConfig()
            self.log = MyLog().get_log().logger
            self.common = Common()
        except Exception as e:
            self.log.error(e)
            raise Exception("出现异常!")

    def open_app(self):
        """打开app"""
        try:
            # 读取app参数
            system = self.config.get_app_param("system")  # 系统类型
            udid = self.config.get_app_param("udid")  # 手机udid
            version = self.config.get_app_param("version")  # 手机系统版本
            app_package = self.config.get_app_param("app_package")  # 待测app包名
            app_activity = self.config.get_app_param(
                "app_activity")  # 待测app的activity名
            # app_address = self.config.get_app_param("app_address")  # app安装包路径
            # android_process = self.config.get_app_param("androidProcess")  # 小程序线程名

            desired_caps = {
                'platformName': system,
                'platformVersion': version,
                'automationName': 'appium',
                'deviceName': 'udid',
                'udid': udid,
                'newCommandTimeout': 60,
                'appActivity': app_activity,
                'appPackage': app_package,
                'unicodeKeyboard': True,
                'resetKeyboard': True,
                'setWebContentsDebuggingEnabled': True,
                'recreateChromeDriverSessions': True,
                'noReset': True,
                # 'app': app_address,
                # 'chromeOptions': {'androidProcess': android_process}
            }
            # 操作APP端元素的webdriver实例
            self.app_driver = appium.webdriver.Remote(
                'http://127.0.0.1:4723/wd/hub', desired_caps)
            self.current_driver = "app_driver"  # 标记为APP端用例
            time.sleep(10)
            self.switch_context()  # H5时需要切换context
        except Exception as e:
            self.log.error(e)
            raise Exception("打开app时异常!")

    def open_browser(self, browser="chrome"):
        """打开浏览器"""
        try:
            if browser == "chrome" or browser == "Chrome":
                driver = selenium.webdriver.Chrome()
            elif browser == "firefox" or browser == "Firefox" or browser == "FireFox" or browser == "ff":
                driver = selenium.webdriver.Firefox()
            elif browser == "ie" or browser == "IE" or browser == "internet explorer":
                driver = selenium.webdriver.Ie()
            else:
                raise Exception(
                    self.log.error("没有找到浏览器 %s, 你可以输入'Chrome,Firefox or Ie'" %
                                   browser))

            self.web_driver = driver  # 操作web端元素的webdriver实例
            self.current_driver = "web_driver"  # 标记为web端用例
            self.web_driver.maximize_window()
        except Exception as e:
            self.log.error(e)
            raise Exception("打开%s浏览器时异常!" % browser)

    def get(self, url, element):
        """打开web端URL, element用于等待页面加载完成"""
        try:
            if url != "" or url != " ":
                self.web_driver.get(url)
                self.wait_elem(element)  # 等待元素出现
            else:
                raise Exception("URL地址错误!")
        except Exception as e:
            self.log.error(e)
            raise Exception("打开网址时异常!")

    def refresh(self):
        """刷新页面"""
        if self.current_driver == "web_driver":
            self.web_driver.refresh()
        else:
            self.swipe_down()

    def back(self):
        """返回上一页"""
        try:
            if self.current_driver == "web_driver":
                self.web_driver.back()
            else:
                self.app_driver.keyevent(4)
            time.sleep(0.5)
        except Exception as e:
            self.log.error(e)
            raise Exception("返回时异常!")

    def quit(self):
        """退出程序"""
        try:
            if self.current_driver == "web_driver":
                self.web_driver.quit()
            else:
                # H5时用
                self.app_driver.switch_to.context("NATIVE_APP")
                # input_name = self.app_driver.active_ime_engine
                # self.log.debug("当前输入法:%s" % input_name)
                input_list = self.app_driver.available_ime_engines
                # self.log.debug("现有输入法:%s" % input_list)
                self.app_driver.activate_ime_engine(input_list[0])
                # input_name = self.app_driver.active_ime_engine
                # self.log.debug("更改当前输入法为:%s" % input_name)
                self.app_driver.quit()
        except Exception as e:
            self.log.error(e)
            raise Exception("退出程序时异常!")

    def click_elem_tag(self, elements, tag=0, roll=False, t=1):
        """根据元素下标点击元素"""
        for i in range(1, 6, +1):  # 操作失败后重试
            try:
                if roll:  # 是否需要滚动页面
                    self.location(elements, tag)  # app端页面上下微调
                elem = self.find_elements_tag(elements, tag)
                elem.click()
                time.sleep(t)
                break
            except Exception as e:
                time.sleep(1)
                self.log.debug("等待 %s s %s" % (i, e))
        else:
            self.log.error("%s元素未出现!" % str(elements))
            raise Exception("点击元素时异常!")

    def input_tag(self, elements, text, tag=0, roll=False):
        """输入文本"""
        for i in range(1, 6, +1):  # 操作失败后重试
            try:
                if roll:  # 是否需要滚动页面
                    self.location(elements, tag)  # app端页面上下微调
                elem = self.find_elements_tag(elements, tag)
                elem.clear()
                elem.send_keys(text)
                break
            except Exception as e:
                time.sleep(1)
                self.log.debug("等待 %s s %s" % (i, e))
        else:
            self.log.error("%s元素未出现!" % str(elements))
            raise Exception("输入文本时异常!")

    def get_text(self, *args, text="", tag=0):
        """获取文本内容,可多条,text为获取内容的标题/属性"""
        value = ""  # 文本内容
        for param in args:
            for i in range(1, 6, +1):  # 操作失败后重试
                try:
                    elem = self.find_elements_tag(param, tag)
                    value = elem.text  # web端获取文本内容

                    if value == "":
                        value = elem.get_attribute("name")  # app获取文本内容

                    if value != "":
                        self.log.debug("%s%s" % (text, value))
                        break
                except Exception as e:
                    time.sleep(1)
                    self.log.debug("等待 %s s %s" % (i, e))
            else:
                self.log.error("%s元素未出现!" % str(param))
                raise Exception("获取元素文本时异常!")
        return value

    def switch_context(self, tag=1):
        """切换环境,tag=0时为android原生context"""
        try:
            contexts = self.app_driver.contexts  # 获取当前所有context
            self.log.debug("contexts:%s" % contexts)
            if len(contexts) != 1:  # 需要切换context
                self.app_driver.switch_to.context(contexts[tag])  # 切换context
                self.log.debug("切换context")
            context = self.app_driver.current_context  # 获取当前context
            self.log.debug("current_context: %s" % context)
        except Exception as e:
            self.log.error(e)
            raise Exception("切换context时异常!")

    def switch_handle(self, element):
        """切换句柄"""
        try:
            handles = self.app_driver.window_handles
            if len(handles) != 1:  # 需要切换句柄
                self.log.debug("handles:%s" % handles)
                self.app_driver.switch_to.window(handles[-1])
                if self.displayed(element):  # 判断该句柄下是否有该元素
                    self.log.debug("切换handle")
            return self.displayed(element)
        except Exception as e:
            self.log.error(e)
            raise Exception("切换handle时异常!")

    def home_page_to(self, module):
        """首页待办事项进入功能模块"""
        module_elem = ("xpath", "//span[contains(text(), '%s')]" % module
                       )  # 待办事项中的模块标签
        result = False
        try:
            if self.displayed(module_elem):
                self.log.debug("从首页的待办事项进入%s" % module)
                self.screen_shot()
                self.click_elem_tag(module_elem)
                self.screen_shot()
                result = True
            return result
        except Exception as e:
            self.log.error(e)
            raise Exception("从首页的待办事项进入%s时异常!" % module)

    def back_to(self, *args):
        """返回(首页)或指定元素页面(须该页独有元素)"""
        try:
            home_menu = ("css_selector", "span.tab-title.ng-binding"
                         )  # 首页底部menu
            username_input = ("css_selector", "input[type='text']"
                              )  # 用户名输入框定位信息

            menu_elem = ()
            if args != ():
                menu_elem = args[0]
            self.log.debug("返回")
            i = 1
            while i <= 5:  # 最多返回5级页面
                if self.displayed(username_input):  # 判断是否处于登录页
                    raise Exception("登录失败!")
                self.back()
                self.screen_shot()
                if args == ():  # 返回首页
                    if self.switch_handle(home_menu):
                        self.click_elem_tag(home_menu)
                        break
                elif args != ():  # 返回指定元素页面
                    if self.switch_handle(menu_elem):
                        break
                self.log.debug("返回:%s" % i)
                i += 1
            else:
                raise Exception("返回时异常!")
        except Exception as e:
            self.log.error(e)
            raise Exception("返回时异常!")

    def popup(self):
        """获取弹框信息,点击按钮"""
        popup_title = ("css_selector", "h3.popup-title.ng-binding")  # 提示框title
        popup_info = ("css_selector", "div.popup-body")  # 提示信息
        popup_ok_button = ("css_selector",
                           "button.button.ng-binding.button-calm")  # 确定按钮
        try:
            n = len(self.find_elements(popup_title))
            # self.log.debug("弹框数量:%s" % n)
            self.get_text(popup_title, popup_info, tag=n - 1)
            self.screen_shot()
            self.click_elem_tag(popup_ok_button, tag=n - 1)
            self.screen_shot()

        except Exception as e:
            self.log.error(e)
            raise Exception("操作弹框时异常!")

    def roll(self, elements):
        """web端页面下滑"""
        elem = self.find_elements_tag(elements)
        selenium.webdriver.ActionChains(
            self.web_driver).move_to_element(elem).perform()
        time.sleep(1)
        self.log.debug("滚动页面!")

    def screen_shot(self):
        """截图"""
        try:
            current_time = str(self.common.get_now_time())  # 获取当前时间
            func_name = sys._getframe().f_back.f_code.co_name  # 获取调用函数名
            line_number = sys._getframe().f_back.f_lineno  # 获取调用行号

            path = self.common.get_result_path(
                case_name,
                "%s %s %s.png" % (current_time, func_name, line_number))
            if self.current_driver == "web_driver":  # web端直接截图
                self.web_driver.get_screenshot_as_file(path)
            else:  # 移动端截图
                contexts = self.app_driver.contexts  # 获取所有的context
                current_context = self.app_driver.current_context  # 获取当前的context
                if current_context == contexts[0]:  # 如果是android原生环境直接截图
                    self.app_driver.get_screenshot_as_file(path)
                else:  # 如果是H5页面先切换到android原生环境再截图
                    self.app_driver.switch_to.context(contexts[0])
                    self.app_driver.get_screenshot_as_file(path)
                    self.app_driver.switch_to.context(
                        contexts[1])  # 截完图后回到原来的context
        except Exception as e:
            self.log.error(e)
            raise Exception("截图保存时异常!")

    def case_start(self, principal, api_case_name="", api_case_num=0):
        """用例开始,参数为负责人姓名,api测试名,api测试编号"""
        try:
            global case_name  # 获取调用函数名作为截图文件夹名
            if api_case_name == "" and api_case_num == 0:
                case_name = sys._getframe().f_back.f_code.co_name
                self.web_case_num += 1
                self.log.debug("web用例%s:%s,负责人:%s" %
                               (self.web_case_num, case_name, principal))
            else:
                case_name = api_case_name  # 将全局变量case_name重新赋值
                self.log.debug("api用例%s:%s,负责人:%s" %
                               (api_case_num, api_case_name, principal))
        except Exception as e:
            self.log.error(e)
            raise Exception("用例开始时异常!")

    def case_end(self):
        """用例结束"""
        self.log.debug("*" * 100 + "\n")  # "*"号不可改,用于提取用例失败的日志

    def case_pass(self):
        """用例通过"""
        self.log.debug("=" * 10 + "%s: pass!" % case_name + "=" * 10)

    def case_failed(self):
        """用例失败"""
        self.log.debug("=" * 10 + "%s: failed!" % case_name +
                       "=" * 10)  # "failed!"不可改,用于标记用例失败的日志

    def find_elements_tag(self, elements, tag=0):
        """查找元素(一个具体的元素点击和输入时定位)"""
        try:
            key = elements[0]  # 定位方式
            value = elements[1]  # 值

            if self.current_driver == "web_driver":  # web定位
                if key == "css_selector":
                    elem = self.web_driver.find_elements_by_css_selector(
                        value)[tag]
                elif key == "xpath":
                    elem = self.web_driver.find_elements_by_xpath(value)[tag]
                elif key == "id":
                    elem = self.web_driver.find_elements_by_id(value)[tag]
                elif key == "name":
                    elem = self.web_driver.find_elements_by_name(value)[tag]
                elif key == "class":
                    elem = self.web_driver.find_elements_by_class_name(
                        value)[tag]
                elif key == "link_text":
                    elem = self.web_driver.find_elements_by_link_text(value)
                elif key == "partial_link_text":
                    elem = self.web_driver.find_elements_by_partial_link_text(
                        value)
                elif key == "tag_name":
                    elem = self.web_driver.find_elements_by_tag_name(
                        value)[tag]
                else:
                    self.log.error("定位类型书写错误:%s" % str(elements))
                    raise Exception
                return elem
            else:  # app定位
                if key == "css_selector":
                    elem = self.app_driver.find_elements_by_css_selector(
                        value)[tag]
                elif key == "xpath":
                    elem = self.app_driver.find_elements_by_xpath(value)[tag]
                elif key == "accessibility_id":
                    elem = self.app_driver.find_elements_by_accessibility_id(
                        value)[tag]
                elif key == "id":
                    elem = self.app_driver.find_elements_by_id(value)[tag]
                elif key == "name":
                    elem = self.app_driver.find_elements_by_name(value)[tag]
                elif key == "class":
                    elem = self.app_driver.find_elements_by_class_name(
                        value)[tag]
                elif key == "link_text":
                    elem = self.app_driver.find_elements_by_link_text(value)
                elif key == "partial_link_text":
                    elem = self.app_driver.find_elements_by_partial_link_text(
                        value)
                elif key == "tag_name":
                    elem = self.app_driver.find_elements_by_tag_name(
                        value)[tag]
                else:
                    self.log.error("定位类型书写错误:%s" % str(elements))
                    raise Exception
                return elem
        except Exception as e:
            # self.log.debug("元素不存在:%s,%s" % (str(elements), e))
            raise Exception

    def find_elements(self, elements):
        """查找元素集合"""
        try:
            key = elements[0]
            value = elements[1]

            if self.current_driver == "web_driver":  # web查找元素
                if key == "css_selector":
                    elem = self.web_driver.find_elements_by_css_selector(value)
                elif key == "xpath":
                    elem = self.web_driver.find_elements_by_xpath(value)
                elif key == "id":
                    elem = self.web_driver.find_elements_by_id(value)
                elif key == "name":
                    elem = self.web_driver.find_elements_by_name(value)
                elif key == "class_name":
                    elem = self.web_driver.find_elements_by_class_name(value)
                elif key == "link_text":
                    elem = self.web_driver.find_elements_by_link_text(value)
                elif key == "partial_link_text":
                    elem = self.web_driver.find_elements_by_partial_link_text(
                        value)
                elif key == "tag_name":
                    elem = self.web_driver.find_element_by_tag_name(value)
                else:
                    self.log.error("函数类型书写错误:%s" % str(elements))
                    raise Exception
                return elem
            else:  # APP查找元素
                if key == "css_selector":
                    elem = self.app_driver.find_elements_by_css_selector(value)
                elif key == "xpath":
                    elem = self.app_driver.find_elements_by_xpath(value)
                elif key == "accessibility_id":
                    elem = self.app_driver.find_elements_by_accessibility_id(
                        value)
                elif key == "id":
                    elem = self.app_driver.find_elements_by_id(value)
                elif key == "name":
                    elem = self.app_driver.find_elements_by_name(value)
                elif key == "class":
                    elem = self.app_driver.find_elements_by_class_name(value)
                elif key == "link_text":
                    elem = self.app_driver.find_elements_by_link_text(value)
                elif key == "partial_link_text":
                    elem = self.app_driver.find_elements_by_partial_link_text(
                        value)
                elif key == "tag_name":
                    elem = self.app_driver.find_elements_by_tag_name(value)
                else:
                    self.log.error("函数类型书写错误:%s" % str(elements))
                    raise Exception
                return elem
        except Exception as e:
            # self.log.debug("元素不存在:%s,%s" % (str(elements), e))
            raise Exception

    def wait_elem(self, element):
        """等待元素出现"""
        key = element[0]
        value = element[1]
        locator = None

        try:
            if key == "css_selector":
                locator = (By.CSS_SELECTOR, value)
            elif key == "xpath":
                locator = (By.XPATH, value)
            elif key == "id":
                locator = (By.ID, value)
            elif key == "name":
                locator = (By.NAME, value)
            elif key == "class":
                locator = (By.CLASS_NAME, value)
            elif key == "link_text":
                locator = (By.LINK_TEXT, value)
            elif key == "partial_link_text":
                locator = (By.PARTIAL_LINK_TEXT, value)
            elif key == "tag_name":
                locator = (By.TAG_NAME, value)

            if self.current_driver == "web_driver":
                WebDriverWait(self.web_driver, 20, 0.5).until(
                    ec.presence_of_element_located(locator),
                    "%s元素未出现!" % str(element))
            else:
                WebDriverWait(self.app_driver, 20, 0.5).until(
                    ec.presence_of_element_located(locator),
                    "%s元素未出现!" % str(element))
        except Exception as e:
            self.log.error(e)
            raise Exception("等待元素出现时异常!")

    # def judgment(self, elements, tag=0):
    #     """判断元素是否存在"""
    #     for i in range(1, 6, +1):
    #         time.sleep(1)
    #         try:
    #             self.find_elements_tag(elements, tag)
    #             return True
    #         except Exception as e:
    #             return False

    # 方式二(速度较慢):
    # key = elements[0]
    # value = elements[1]
    # locator = None
    #
    # if key == "css_selector":
    #     locator = (By.CSS_SELECTOR, value)
    # elif key == "xpath":
    #     locator = (By.XPATH, value)
    # elif key == "id":
    #     locator = (By.ID, value)
    # elif key == "name":
    #     locator = (By.NAME, value)
    # elif key == "class":
    #     locator = (By.CLASS_NAME, value)
    # elif key == "link_text":
    #     locator = (By.LINK_TEXT, value)
    # elif key == "partial_link_text":
    #     locator = (By.PARTIAL_LINK_TEXT, value)
    # elif key == "tag_name":
    #     locator = (By.TAG_NAME, value)
    #
    # if current_driver == "web_driver":
    #     try:
    #         WebDriverWait(self.web_driver, 20, 0.5).until(lambda x: x.find_element(*locator))
    #         return True
    #     except:
    #         return False
    # else:
    #     try:
    #         WebDriverWait(self.app_driver, 20, 0.5).until(lambda x: x.find_element(*locator))
    #         return True
    #     except:
    #         return False

    def displayed(self, elements, tag=0):
        """判断元素是否可见"""
        try:
            elem = self.find_elements_tag(elements, tag)
            return elem.is_displayed()  # 元素可见为True,隐藏为False
        except Exception as e:
            return False  # 没有找到元素

    def swipe_up(self, x=0.5, y1=0.85, y2=0.15, t=500):
        """屏幕向上滑动"""
        try:
            self.swipe(x, y1, y2, t)
            self.log.debug("上滑")
        except Exception as e:
            self.log.error(e)
            raise Exception("屏幕向上滑动时异常!")

    def swipe_down(self, x=0.5, y1=0.15, y2=0.85, t=500):
        """屏幕向下滑动"""
        try:
            self.swipe(x, y1, y2, t)
            self.log.debug("下滑")
        except Exception as e:
            self.log.error(e)
            raise Exception("屏幕向下滑动时异常!")

    def swipe(self, x, y1, y2, t):
        """上下滑动"""
        try:
            coordinate_x = self.app_driver.get_window_size()['width']  # 获取屏幕宽度
            coordinate_y = self.app_driver.get_window_size()[
                'height']  # 获取屏幕高度
            x1 = int(coordinate_x * x)  # x坐标
            y1 = int(coordinate_y * y1)  # 起始y坐标
            y2 = int(coordinate_y * y2)  # 终点y坐标
            self.app_driver.swipe(x1, y1, x1, y2, t)
            time.sleep(1)
        except Exception as e:
            raise Exception(e)

    def location(self, element, tag=0):
        """屏幕内容上下微调"""
        current_context = ""
        try:
            elem = self.find_elements_tag(
                element, tag)  # css_selector不能在android环境下定位,所以定位完成后再切换环境
            y1 = elem.location["y"]  # 获取元素y坐标
            # self.log.debug(y1)

            contexts = self.app_driver.contexts  # 获取所有的context
            current_context = self.app_driver.current_context  # 获取当前的context
            if current_context != contexts[
                    0]:  # 当前为非android环境时需要切换为APP_context才能进行滑动操作
                self.app_driver.switch_to.context(contexts[0])

            y2 = self.app_driver.get_window_size()['height']  # 获取屏幕高度
            # self.log.debug(y2)

            while y1 + 200 > y2 or y1 < 100:  # 判断是否需要滑动
                if y1 + 200 > y2:
                    self.swipe(x=0.02, y1=0.85, y2=0.45, t=500)  # 向上滑
                    self.screen_shot()

                n = y1

                if current_context == contexts[
                        1]:  # 当前为非H5环境时需要切换为H5环境才能获取元素坐标
                    self.app_driver.switch_to.context(contexts[1])
                y1 = elem.location["y"]
                # self.log.debug(y1)
                if current_context != contexts[
                        0]:  # 当前为非android环境时需要切换为APP_context才能进行滑动操作
                    self.app_driver.switch_to.context(contexts[0])

                if y1 < 100:
                    self.swipe(x=0.02, y1=0.60, y2=0.75, t=500)  # 向下滑
                    self.screen_shot()

                if n == y1:
                    break
        except Exception as e:
            self.log.error(e)
            raise Exception("位置调整时异常!")
        finally:
            self.app_driver.switch_to.context(current_context)  # 微调完成后切换为原来的环境
Exemple #2
0
                                                       top_level_dir=None)
        return discover

    # 方式二:
    def run_cases(self, case):
        """执行用例并生成报告"""
        result = BeautifulReport(case)
        result.report(log_path=self.path,
                      filename="NT_测试报告.html",
                      description='NT自动化测试')


if __name__ == "__main__":
    run = Run()
    common = Common()
    start_time = common.get_now_time()
    # 方式一:
    run.add_api_test()
    # run.add_ui_test()
    BeautifulReport(run.suit).report(log_path=run.path,
                                     filename="NT_测试报告.html",
                                     description='NT自动化测试')

    #  方式二:
    # cases = run.add_cases()
    # run.run_cases(cases)

    run.my_log.extraction_error_log()  # 提取错误日志
    run.send_email.with_zip()  # 发送email
    end_time = common.get_now_time()
    run.log.debug("耗时:%s s" % common.interval(start_time, end_time))