예제 #1
0
class QQDriver(object):
    def initDriver(self):
        if self._hasInit:
            return

        self._urlFetcher = QQWebSocketDebugUrlFetcher(device=self._device)
        url = self._urlFetcher.fetchWebSocketDebugUrl()
        self._vmShutdownHandler.registerToVM()
        UncaughtExceptionHandler.init()
        UncaughtExceptionHandler.registerUncaughtExceptionCallback(
            self._onUncaughtException)

        self._selectDevice()

        self._webSocketDataTransfer = WebSocketDataTransfer(url=url)
        self._networkHandler = ShortLiveWebSocket(self._webSocketDataTransfer,
                                                  self._executor, self)
        self._networkHandler.connect()
        self._executor.put(self._networkHandler.receive)

        self.initPageDisplayData()
        self.logger = Log().getLogger()
        self._hasInit = True

    def __init__(self, device=None):
        self._device = device
        self._urlFetcher = None
        self._webSocketDataTransfer = None
        self._vmShutdownHandler = VMShutDownHandler()
        self._networkHandler = None
        self._executor = SingleThreadExecutor()

        self._pageOperator = H5PageOperator()

        self._hasInit = False

        self.d = uiautomator.Device(self._device)
        self.html = None

    def getDriverType(self):
        return constant.QQDRIVER

    def initPageDisplayData(self):
        driverInfo = self.d.info
        displayDp = driverInfo.get('displaySizeDpY')
        displayPx = driverInfo.get('displayHeight')
        self.scale = (displayPx - 0.5) / displayDp
        windowHeight = self.getWindowHeight()
        self.appTitleHeight = displayDp - windowHeight

    def changeDp2Px(self, xDp, yDp):
        xPx, yPx = self._pageOperator.changeDp2Px(xDp, yDp, self.scale,
                                                  self.appTitleHeight)
        return xPx, yPx

    def needSwitchNextPage(self):
        return self._urlFetcher.needSwitchNextPage()

    def _selectDevice(self):
        devicesList = AdbHelper.listDevices(ignoreUnconnectedDevices=True)
        # 假如没有指定设备,那么判断当前机器是否连接1个设备
        if self._device is None:
            devicesCount = len(devicesList)
            errorMsg = None

            if devicesCount <= 0:
                errorMsg = ErrorMsgManager().errorCodeToString(
                    ERROR_CODE_DEVICE_NOT_CONNECT)

            elif devicesCount == 1:
                self._device = devicesList[0]

            else:
                errorMsg = ErrorMsgManager().errorCodeToString(
                    ERROR_CODE_MULTIPLE_DEVICE)

            if errorMsg is not None:
                raise RuntimeError(errorMsg)

    def switchToNextPage(self):
        """
        需要重新获取当前页面的websocket的url
        """
        self._networkHandler.disconnect()
        url = self._urlFetcher.fetchWebSocketDebugUrl(refetch=True)
        self.logger.debug('url -> ' + url)
        self._webSocketDataTransfer.setUrl(url)
        self._networkHandler.connect()
        self.wait(WAIT_REFLESH_05_SECOND)

    def returnLastPage(self):
        self.logger.info('')
        self.wait(WAIT_REFLESH_1_SECOND)
        self._networkHandler.disconnect()
        self.switchToNextPage()
        self._networkHandler.disconnect()
        self.d.press('back')
        self.wait(WAIT_REFLESH_1_SECOND)
        self.switchToNextPage()

    def getDocument(self):
        """
        获得getHtml中需要的nodeId
        在调用getHtml之前必须先调用这个方法
        """
        sendStr = self._pageOperator.getDocument()
        return self._networkHandler.send(sendStr)

    def getHtml(self, nodeId=1):
        """
        获得指定nodeId的Html代码。在一条websocket连接中,方法只能够执行一次。
        :param nodeId: getDocument方法返回的nodeId,当为1时,返回整个body的代码
        """
        self.logger.info('')
        self.switchToNextPage()
        self.getDocument()
        sendStr = self._pageOperator.getHtml(nodeId)
        self.html = self._networkHandler.send(
            sendStr).getResponse()[0]['result']['outerHTML']
        return self.html

    def isElementExist(self, xpath):
        self.logger.info('xpath ---> ' + xpath)
        getExistCmd = self._pageOperator.isElementExist(xpath)
        resultValueDict = self._networkHandler.send(
            getExistCmd).getResponse()[0]
        resultType = resultValueDict['result']['result']['subtype']
        if resultType == 'null':
            self.wait(WAIT_REFLESH_3_SECOND)
            self.switchToNextPage()
            getExistCmd = self._pageOperator.isElementExist(xpath)
            resultValueDict = self._networkHandler.send(
                getExistCmd).getResponse()[0]
            resultType = resultValueDict['result']['result']['subtype']
        self.logger.debug('isElementExist Response --> ' +
                          str(resultValueDict))
        return resultType != 'null'

    def getElementTextByXpath(self, xpath):
        time.sleep(WAIT_REFLESH_1_SECOND)
        self.logger.info('xpath ---> ' + xpath)
        self.switchToNextPage()
        if self.isElementExist(xpath):
            getTextCmd = self._pageOperator.getElementTextByXpath(xpath)
            resultValueDict = self._networkHandler.send(
                getTextCmd).getResponse()[0]
            resultValue = resultValueDict['result']['result']['value'].encode(
                "utf-8")
        else:
            resultValue = None
        return resultValue

    def getElementSrcByXpath(self, xpath):
        time.sleep(WAIT_REFLESH_1_SECOND)
        self.logger.info('xpath ---> ' + xpath)
        self.switchToNextPage()
        if self.isElementExist(xpath):
            getSrcCmd = self._pageOperator.getElementSrcByXpath(xpath)
            resultValueDict = self._networkHandler.send(
                getSrcCmd).getResponse()[0]
            resultValue = resultValueDict['result']['result']['value'].encode(
                "utf-8")
        else:
            resultValue = None
        return resultValue

    def getElementByXpath(self, xpath):
        """
        :param:目标的xpath
        :return:返回lxml包装过的element对象,可以使用lxml语法获得对象的信息
        例如:可以使用element.get("attrs")来拿到属性的数据
        可以用element.text拿到它的文字
        当element中含有列表时,使用for循环读取每一个item
        """
        time.sleep(WAIT_REFLESH_1_SECOND)
        self.logger.info('xpath ---> ' + xpath)
        htmlData = self.getHtml()
        if htmlData is not None:
            html = etree.HTML(htmlData)
            elementList = html.xpath(xpath)
            if len(elementList) != 0:
                return elementList[0]
            else:
                self.logger.info('找不到xpath为: ' + xpath + ' 的控件')
                return ''
        else:
            self.logger.info('获取到的html为空')
            return ''

    def textElementByXpath(self, xpath, text):
        """
               先获取输入框的焦点, 再使用Chrome debug协议的输入api,再输入文字内容
               :param xpath:输入框的xpath
               :parm text:要输入的文字
               """
        self.logger.info('xpath ---> ' + xpath + ' text ---> ' + text)
        self.focusElementByXpath(xpath)
        sendStrList = self._pageOperator.textElementByXpath(text)

        for command in sendStrList:
            self._networkHandler.send(command)
        self.wait(WAIT_REFLESH_05_SECOND)

    def clickElementByXpath(self,
                            xpath,
                            visibleItemXpath=None,
                            byUiAutomator=False):
        """
        默认滑动点为屏幕的中心,且边距为整个屏幕。当有container时,传入container中任意一个当前可见item的xpath,之后会将目标滑到该可见item的位置
        :param xpath: 要滑动到屏幕中控件的xpath
        :param visibleItemXpath: container中当前可见的一个xpath
        :return:
        """
        self.logger.info('xpath ---> ' + xpath)
        self.scrollToElementByXpath(xpath, visibleItemXpath)

        sendStr = self._pageOperator.getElementRect(xpath)
        self._networkHandler.send(sendStr)

        x = self._getRelativeDirectionValue('x')
        y = self._getRelativeDirectionValue('y')
        self.logger.debug('clickElementByXpath x:' + str(x) + '  y:' + str(y))

        if not byUiAutomator:
            clickCommand = self._pageOperator.clickElementByXpath(x, y)
            return self._networkHandler.send(clickCommand)
        else:
            xPx, yPx = self.changeDp2Px(x, y)
            self.d.click(xPx, yPx)

    def clickFirstElementByText(self,
                                text,
                                visibleItemXpath=None,
                                byUiAutomator=False):
        """
        通过text来搜索,点击第一个text相符合的控件。参数同clickElementByXpath()
        """
        self.clickElementByXpath('.//*[text()="' + text + '"]',
                                 visibleItemXpath, byUiAutomator)

    def focusElementByXpath(self, xpath):
        """
        调用目标的focus()方法。
        :param xpath:目标的xpath
        """
        executeCmd = self._pageOperator.focusElementByXpath(xpath)
        self._networkHandler.send(executeCmd)

    def getElementCoordinateByXpath(self, elementXpath):
        '''
        获得Element的坐标
        :param elementXpath:待获取坐标的元素的xpath
        :return:element相对于整个屏幕的x、y坐标,单位为px
        '''
        self.logger.info('elementXpathXpath ---> ' + elementXpath)

        sendStr = self._pageOperator.getElementRect(elementXpath)
        self._networkHandler.send(sendStr)
        x = self._getRelativeDirectionValue('x')
        y = self._getRelativeDirectionValue('y')
        xPx, yPx = self.changeDp2Px(x, y)
        return xPx, yPx

    def scrollToElementByXpath(self, xpath, visibleItemXpath=None, speed=600):
        """
        默认滑动点为屏幕的中心,且边距为整个屏幕。当有container时,传入container中任意一个当前可见item的xpath,之后会将目标滑到该可见item的位置
        :param xpath: 要滑动到屏幕中控件的xpath
        :param visibleItemXpath: container中当前可见的一个xpath
        :return:
        """
        self.logger.info('xpath ---> ' + xpath)
        sendStr = self._pageOperator.getElementRect(xpath)
        self._networkHandler.send(sendStr)

        top = self._getRelativeDirectionValue('topp')
        bottom = self._getRelativeDirectionValue('bottom')
        left = self._getRelativeDirectionValue('left')
        right = self._getRelativeDirectionValue('right')
        self.logger.debug('scrollToElementByXpath -> top:bottom:left:right = ' + str(top) + " :" + str(bottom) \
                          + " :" + str(left) + " :" + str(right))

        if visibleItemXpath is None:
            endTop = 0
            endLeft = 0
            endBottom = self.getWindowHeight()
            endRight = self.getWindowWidth()
        else:
            sendStr = self._pageOperator.getElementRect(visibleItemXpath)
            self._networkHandler.send(sendStr)

            endTop = self._getRelativeDirectionValue('topp')
            endBottom = self._getRelativeDirectionValue('bottom')
            endLeft = self._getRelativeDirectionValue('left')
            endRight = self._getRelativeDirectionValue('right')
            self.logger.debug(
                'scrollToElementByXpath -> toendTop:endBottom:endLeft:endRight = ' + str(endTop) + " :" + str(
                    endBottom) \
                + " :" + str(endLeft) + " :" + str(endRight))
        '''
        竖直方向的滑动
        '''
        if endBottom < bottom:
            scrollYDistance = endBottom - bottom
        elif top < 0:
            scrollYDistance = -(top - endTop)
        else:
            scrollYDistance = 0

        if scrollYDistance != 0:
            self.scrollWindow(int((endLeft + endRight) / 2),
                              int((endTop + endBottom) / 2), 0,
                              scrollYDistance, speed)
        else:
            self.logger.debug('y方向不需要滑动')
        '''
        水平方向的滑动
        '''
        if right > endRight:
            scrollXDistance = endRight - right
        elif left < 0:
            scrollXDistance = -(left - endLeft)
        else:
            scrollXDistance = 0

        if scrollXDistance != 0:
            self.scrollWindow(int((endLeft + endRight) / 2),
                              int((endTop + endBottom) / 2), scrollXDistance,
                              0, speed)
        else:
            self.logger.debug('x方向不需要滑动')

    def scrollWindow(self, x, y, xDistance, yDistance, speed=800):
        self.logger.info('')
        sendStr = self._pageOperator.scrollWindow(x, y, xDistance, yDistance,
                                                  speed)
        return self._networkHandler.send(sendStr)

    def _getRelativeDirectionValue(self, directionKey='topp'):
        '''
        获取相关的方向数据参数值
        :param directionKey: key值
        :return:
        '''
        directionCommand = self._pageOperator.getJSValue(directionKey)
        directionResponse = self._networkHandler.send(
            directionCommand).getResponse()[0]
        directionValue = directionResponse['result']['result']['value']
        return directionValue

    '''
    性能数据
    '''

    def getMemoryInfo(self):
        '''
        获得小程序进程占用内存信息
        :return: 小程序进程占用内存信息
        '''
        self.logger.info('')
        ADB_SHELL = 'adb shell '
        if self._device:
            ADB_SHELL = ADB_SHELL.replace("adb", "adb -s %s" % self._device)
        GET_MEMORY_INFO = ADB_SHELL + 'dumpsys meminfo com.tencent.mm:appbrand0'
        return commandHelper.runCommand(GET_MEMORY_INFO)

    def getCPUInfo(self):
        '''
        获得小程序进程占用CPU信息
        :return: 小程序进程占用CPU信息
        '''
        self.logger.info('')
        ADB_SHELL = 'adb shell '
        if self._device:
            ADB_SHELL = ADB_SHELL.replace("adb", "adb -s %s" % self._device)
        GET_CPU_INFO = ADB_SHELL + ' top -n 1 | grep com.tencent.mm:appbrand0'
        return commandHelper.runCommand(GET_CPU_INFO)

    def getPageHeight(self):
        getPageHeightCmd = self._pageOperator.getPageHeight()
        resultValueDict = self._networkHandler.send(
            getPageHeightCmd).getResponse()[0]
        resultValue = resultValueDict['result']['result']['value']
        return resultValue

    def getWindowWidth(self):
        '''
        :return:手机屏幕的宽度
        '''
        getWindowWidthCmd = self._pageOperator.getWindowWidth()
        resultValueDict = self._networkHandler.send(
            getWindowWidthCmd).getResponse()[0]
        resultValue = resultValueDict['result']['result']['value']
        return resultValue

    def getWindowHeight(self):
        '''
        :return:手机屏幕的高度
        '''
        getWindowHeightCmd = self._pageOperator.getWindowHeight()
        resultValueDict = self._networkHandler.send(
            getWindowHeightCmd).getResponse()[0]
        resultValue = resultValueDict['result']['result']['value']
        return resultValue

    def wait(self, seconds):
        time.sleep(seconds)

    def addShutDownHook(self, func, *args, **kwargs):
        '''
        添加当程序正常关闭时的操作,可以进行一些环境清理操作等
        '''
        self._vmShutdownHandler.registerVMShutDownCallback(
            func, *args, **kwargs)

    def _onUncaughtException(self, exctype, value, tb):
        self.close()

    def close(self):
        if self._networkHandler is not None:
            self._networkHandler.quit()

        if self._executor is not None:
            self._executor.shutDown()

        UncaughtExceptionHandler.removeHook()

    def setDebugLogMode(self):
        '''
        开启debug的日志模式
        '''
        Log().setDebugLogMode()
예제 #2
0
class WxDriver(object):
    def initDriver(self):
        if self._hasInit:
            return

        self._urlFetcher = WxWebSocketDebugUrlFetcher(device=self._device)
        url = self._urlFetcher.fetchWebSocketDebugUrl()

        dirPath = os.path.split(os.path.realpath(__file__))[0]
        PLUG_SRC = os.path.join(dirPath, 'apk', 'inputPlug.apk')
        if not AdbHelper.hasApkInstalled(packageName='com.tencent.fat.wxinputplug'):
            AdbHelper.installApk(PLUG_SRC, device=self._device, installOverride=True)
            self.logger.info('install ---> ' + PLUG_SRC)
        self._vmShutdownHandler.registerToVM()
        UncaughtExceptionHandler.init()
        UncaughtExceptionHandler.registerUncaughtExceptionCallback(self._onUncaughtException)

        self._selectDevice()

        self._webSocketDataTransfer = WebSocketDataTransfer(url=url)
        self._networkHandler = ShortLiveWebSocket(self._webSocketDataTransfer, self._executor, self)
        self._networkHandler.connect()
        self._executor.put(self._networkHandler.receive)

        self.initPageDisplayData()
        self._hasInit = True

    # 为了输入中文要先安装一个插件,./wx/apk/inputPlug.apk
    def __init__(self, d,device=None):
        self._device = device
        self._urlFetcher = None
        self._webSocketDataTransfer = None
        self._vmShutdownHandler = VMShutDownHandler()
        self._networkHandler = None
        self._executor = SingleThreadExecutor()
        self.logger = Log().getLogger()

        self._pageOperator = WxPageOperator()

        self._hasInit = False

        self.d = d
        self.html = None

    def getDriverType(self):
        return constant.WXDRIVER

    def initPageDisplayData(self):
        driverInfo = self.d.info
        displayDp = driverInfo.get('displaySizeDpY')
        displayPx = driverInfo.get('displayHeight')
        self.scale = (displayPx - 0.5) / displayDp
        windowHeight = self.getWindowHeight()
        self.appTitleHeight = displayDp - windowHeight

    def changeDp2Px(self, xDp, yDp):
        xPx, yPx = self._pageOperator.changeDp2Px(xDp, yDp, self.scale, self.appTitleHeight)
        return xPx, yPx

    def needSwitchNextPage(self):
        return self._urlFetcher.needSwitchNextPage()

    def _selectDevice(self):
        devicesList = AdbHelper.listDevices(ignoreUnconnectedDevices=True)
        # 假如没有指定设备,那么判断当前机器是否连接1个设备
        if self._device is None:
            devicesCount = len(devicesList)
            errorMsg = None

            if devicesCount <= 0:
                errorMsg = ErrorMsgManager().errorCodeToString(ERROR_CODE_DEVICE_NOT_CONNECT)

            elif devicesCount == 1:
                self._device = devicesList[0]

            else:
                errorMsg = ErrorMsgManager().errorCodeToString(ERROR_CODE_MULTIPLE_DEVICE)

            if errorMsg is not None:
                raise RuntimeError(errorMsg)

    def switchToNextPage(self):
        """
        需要重新获取当前页面的websocket的url
        """
        self._networkHandler.disconnect()
        url = self._urlFetcher.fetchWebSocketDebugUrl(refetch=True)
        self.logger.debug('url -> ' + url)
        self._webSocketDataTransfer.setUrl(url)
        self._networkHandler.connect()
        self.wait(WAIT_REFLESH_05_SECOND)

    def returnLastPage(self):
        self.wait(WAIT_REFLESH_05_SECOND)
        self.logger.info('')
        self._networkHandler.disconnect()
        self.switchToNextPage()
        self.wait(WAIT_REFLESH_05_SECOND)
        self._networkHandler.disconnect()
        self.d.press('back')
        self.wait(WAIT_REFLESH_05_SECOND)
        self.switchToNextPage()

    def getDocument(self):
        """
        获得getHtml中需要的nodeId
        在调用getHtml之前必须先调用这个方法
        """
        sendStr = self._pageOperator.getDocument()
        return self._networkHandler.send(sendStr)

    def getHtml(self, nodeId=1):
        """
        获得指定nodeId的Html代码。在一条websocket连接中,方法只能够执行一次。
        :param nodeId: getDocument方法返回的nodeId,当为1时,返回整个body的代码
        """
        self.logger.info('')
        self.switchToNextPage()
        self.getDocument()
        sendStr = self._pageOperator.getHtml(nodeId)
        self.html = self._networkHandler.send(sendStr).getResponse()[0]['result']['outerHTML']
        return self.html

    def isElementExist(self, xpath):
        self.logger.info('xpath ---> ' + xpath)
        getExistCmd = self._pageOperator.isElementExist(xpath)
        response = self._networkHandler.send(getExistCmd).getResponse()
        self.logger.debug(response)
        resultValueDict = response[0]
        resultType = resultValueDict['result']['result']['subtype']
        num = 0
        while resultType == 'null' and num < 3:
            self.wait(WAIT_REFLESH_3_SECOND)
            self.switchToNextPage()
            getExistCmd = self._pageOperator.isElementExist(xpath)
            resultValueDict = self._networkHandler.send(getExistCmd).getResponse()[0]
            resultType = resultValueDict['result']['result']['subtype']
            num = num + 1
        self.logger.debug('isElementExist Response --> ' + str(resultValueDict))
        return resultType != 'null'

    def getElementTextByXpath(self, xpath):
        time.sleep(WAIT_REFLESH_1_SECOND)
        self.logger.info('xpath ---> ' + xpath)
        self.switchToNextPage()
        if self.isElementExist(xpath):
            getTextCmd = self._pageOperator.getElementTextByXpath(xpath)
            resultValueDict = self._networkHandler.send(getTextCmd).getResponse()[0]
            resultValue = resultValueDict['result']['result']['value'].encode("utf-8")
        else:
            resultValue = None
        return resultValue

    def getElementSrcByXpath(self, xpath):
        time.sleep(WAIT_REFLESH_1_SECOND)
        self.logger.info('xpath ---> ' + xpath)
        self.switchToNextPage()
        if self.isElementExist(xpath):
            getSrcCmd = self._pageOperator.getElementSrcByXpath(xpath)
            resultValueDict = self._networkHandler.send(getSrcCmd).getResponse()[0]
            resultValue = resultValueDict['result']['result']['value'].encode("utf-8")
        else:
            resultValue = None
        return resultValue

    def getElementByXpath(self, xpath):
        """
        :param:目标的xpath
        :return:返回lxml包装过的element对象,可以使用lxml语法获得对象的信息
        例如:可以使用element.get("attrs")来拿到属性的数据
        可以用element.text拿到它的文字
        当element中含有列表时,使用for循环读取每一个item
        """
        time.sleep(WAIT_REFLESH_1_SECOND)
        self.logger.info('xpath ---> ' + xpath)
        htmlData = self.getHtml()
        if htmlData is not None:
            html = etree.HTML(htmlData)
            elementList = html.xpath(xpath)
            if len(elementList) != 0:
                return elementList[0]
            else:
                self.logger.info('找不到xpath为: ' + xpath + ' 的控件')
                return ''
        else:
            self.logger.info('获取到的html为空')
            return ''

    def textElementByXpath(self, xpath, text, needClick=False):
        """
        :param needClick:如果为true,会先对控件进行一次点击,以获得焦点

        输入前会先保存当前默认的输入法
        然后将输入法切换到adb插件
        输入中文,输入后讲输入法转换回默认输入发
        needClick代表是否需要先对xpath的控件进行一次点击
        """
        self.logger.info('xpath ---> ' + xpath + ' text ---> ' + text)
        if needClick:
            self.clickElementByXpath(xpath, byUiAutomator=True)

        if self.isElementExist(xpath):
            ADB_SHELL = 'adb shell '
            self.logger.debug('textElementByXpath xpath exist')

            if self._device:
                ADB_SHELL = ADB_SHELL.replace("adb", "adb -s %s" % self._device)
            GET_DEFAULT_INPUT_METHOD = ADB_SHELL + 'settings get secure default_input_method'
            SET_INPUT_METHOD = ADB_SHELL + 'ime set {0}'
            CHINESE_INPUT_METHOD = 'com.tencent.fat.wxinputplug/.XCXIME'
            INPUT_TEXT = ADB_SHELL + "am broadcast -a INPUT_TEXT --es TEXT '{0}'"

            DEFAULT_INPUT_METHOD = commandHelper.runCommand(GET_DEFAULT_INPUT_METHOD)[0].replace("\r\n", " ")
            osName = OS.getPlatform()
            INPUT_TEXT_CMD = {
                "Darwin": INPUT_TEXT.format(text),
                "Windows": INPUT_TEXT.format(text).decode('utf-8').replace(u'\xa0', u' ').encode('GBK'),
                "Linux": INPUT_TEXT.format(text)
            }

            self.logger.debug(SET_INPUT_METHOD.format(CHINESE_INPUT_METHOD))
            commandHelper.runCommand(SET_INPUT_METHOD.format(CHINESE_INPUT_METHOD))
            self.wait(WAIT_REFLESH_05_SECOND)
            self.logger.debug(INPUT_TEXT_CMD.get(osName))
            commandHelper.runCommand(INPUT_TEXT_CMD.get(osName))
            self.logger.debug(SET_INPUT_METHOD.format(DEFAULT_INPUT_METHOD))
            commandHelper.runCommand(SET_INPUT_METHOD.format(DEFAULT_INPUT_METHOD))
            self.wait(WAIT_REFLESH_05_SECOND)

    def clickElementByXpath(self, xpath, visibleItemXpath=None, byUiAutomator=False):
        """
        默认滑动点为屏幕的中心,且边距为整个屏幕。当有container时,传入container中任意一个当前可见item的xpath,之后会将目标滑到该可见item的位置
        :param xpath: 要滑动到屏幕中控件的xpath
        :param visibleItemXpath: container中当前可见的一个xpath
        :return:
        """
        self.logger.info('xpath ---> ' + xpath)
        if self.isElementExist(xpath):
            self.scrollToElementByXpath(xpath, visibleItemXpath)

            sendStr = self._pageOperator.getElementRect(xpath)
            self._networkHandler.send(sendStr)

            x = self._getRelativeDirectionValue('x')
            y = self._getRelativeDirectionValue('y')
            self.logger.debug('clickElementByXpath x:' + str(x) + '  y:' + str(y))

            if not byUiAutomator:
                clickCommand = self._pageOperator.clickElementByXpath(x, y)
                return self._networkHandler.send(clickCommand)
            else:
                xPx, yPx = self.changeDp2Px(x, y)
                self.d.click(xPx, yPx)

    def clickFirstElementByText(self, text, visibleItemXpath=None, byUiAutomator=False):
        """
        通过text来搜索,点击第一个text相符合的控件。参数同clickElementByXpath()
        """
        self.clickElementByXpath('.//*[text()="' + text + '"]', visibleItemXpath, byUiAutomator)

    def getElementCoordinateByXpath(self, elementXpath):
        '''
        获得Element的坐标
        :param elementXpath:待获取坐标的元素的xpath
        :return:element相对于整个屏幕的x、y坐标,单位为px
        '''
        self.logger.info(
            'elementXpathXpath ---> ' + elementXpath)
        if self.isElementExist(elementXpath):
            sendStr = self._pageOperator.getElementRect(elementXpath)
            self._networkHandler.send(sendStr)
            x = self._getRelativeDirectionValue('x')
            y = self._getRelativeDirectionValue('y')
            xPx, yPx = self.changeDp2Px(x, y)
            return xPx, yPx

    def scrollToElementByXpath(self, xpath, visibleItemXpath=None, speed=600):
        """
        默认滑动点为屏幕的中心,且边距为整个屏幕。当有container时,传入container中任意一个当前可见item的xpath,之后会将目标滑到该可见item的位置
        :param xpath: 要滑动到屏幕中控件的xpath
        :param visibleItemXpath: container中当前可见的一个xpath
        :return:
        """
        self.logger.info('xpath ---> ' + xpath)
        sendStr = self._pageOperator.getElementRect(xpath)
        self._networkHandler.send(sendStr)

        top = self._getRelativeDirectionValue('topp')
        bottom = self._getRelativeDirectionValue('bottom')
        left = self._getRelativeDirectionValue('left')
        right = self._getRelativeDirectionValue('right')
        self.logger.debug('scrollToElementByXpath -> top:bottom:left:right = ' + str(top) + " :" + str(bottom) \
                          + " :" + str(left) + " :" + str(right))

        if visibleItemXpath is None:
            endTop = 0
            endLeft = 0
            endBottom = self.getWindowHeight()
            endRight = self.getWindowWidth()
        else:
            sendStr = self._pageOperator.getElementRect(visibleItemXpath)
            self._networkHandler.send(sendStr)

            endTop = self._getRelativeDirectionValue('topp')
            endBottom = self._getRelativeDirectionValue('bottom')
            endLeft = self._getRelativeDirectionValue('left')
            endRight = self._getRelativeDirectionValue('right')
            self.logger.debug(
                'scrollToElementByXpath -> toendTop:endBottom:endLeft:endRight = ' + str(endTop) + " :" + str(
                    endBottom) \
                + " :" + str(endLeft) + " :" + str(endRight))

        '''
        竖直方向的滑动
        '''
        if endBottom < bottom:
            scrollYDistance = endBottom - bottom
        elif top < 0:
            scrollYDistance = -(top - endTop)
        else:
            scrollYDistance = 0

        if scrollYDistance < 0:
            self.scrollWindow(int((endLeft + endRight) / 2), int((endTop + endBottom) / 2), 0, scrollYDistance - 80,
                              speed)
        elif scrollYDistance > 0:
            self.scrollWindow(int((endLeft + endRight) / 2), int((endTop + endBottom) / 2), 0, scrollYDistance + 80,
                              speed)
        else:
            self.logger.debug('y方向不需要滑动')
        '''
        水平方向的滑动
        '''
        if right > endRight:
            scrollXDistance = endRight - right
        elif left < 0:
            scrollXDistance = -(left - endLeft)
        else:
            scrollXDistance = 0

        if scrollXDistance != 0:
            self.scrollWindow(int((endLeft + endRight) / 2), int((endTop + endBottom) / 2), scrollXDistance, 0,
                              speed)
        else:
            self.logger.debug('x方向不需要滑动')

    def scrollWindow(self, x, y, xDistance, yDistance, speed=800):
        self.logger.info('')
        sendStr = self._pageOperator.scrollWindow(x, y, xDistance, yDistance, speed)
        return self._networkHandler.send(sendStr)

    def _getRelativeDirectionValue(self, directionKey='topp'):
        '''
        获取相关的方向数据参数值
        :param directionKey: key值
        :return:
        '''
        directionCommand = self._pageOperator.getJSValue(directionKey)
        directionResponse = self._networkHandler.send(directionCommand).getResponse()[0]
        directionValue = directionResponse['result']['result']['value']
        return directionValue

    '''
    性能数据
    '''

    def getMemoryInfo(self):
        '''
        获得小程序进程占用内存信息
        :return: 小程序进程占用内存信息
        '''
        self.logger.info('')
        ADB_SHELL = 'adb shell '
        if self._device:
            ADB_SHELL = ADB_SHELL.replace("adb", "adb -s %s" % self._device)
        GET_MEMORY_INFO = ADB_SHELL + 'dumpsys meminfo com.tencent.mm:appbrand0'
        return commandHelper.runCommand(GET_MEMORY_INFO)

    def getCPUInfo(self):
        '''
        获得小程序进程占用CPU信息
        :return: 小程序进程占用CPU信息
        '''
        self.logger.info('')
        ADB_SHELL = 'adb shell '
        if self._device:
            ADB_SHELL = ADB_SHELL.replace("adb", "adb -s %s" % self._device)
        GET_CPU_INFO = ADB_SHELL + ' top -n 1 | grep com.tencent.mm:appbrand0'
        return commandHelper.runCommand(GET_CPU_INFO)

    def getPageHeight(self):
        getPageHeightCmd = self._pageOperator.getPageHeight()
        resultValueDict = self._networkHandler.send(getPageHeightCmd).getResponse()[0]
        resultValue = resultValueDict['result']['result']['value']
        return resultValue

    def getWindowWidth(self):
        '''
        :return:手机屏幕的宽度
        '''
        getWindowWidthCmd = self._pageOperator.getWindowWidth()
        resultValueDict = self._networkHandler.send(getWindowWidthCmd).getResponse()[0]
        resultValue = resultValueDict['result']['result']['value']
        return resultValue

    def getWindowHeight(self):
        '''
        :return:手机屏幕的高度
        '''
        getWindowHeightCmd = self._pageOperator.getWindowHeight()
        resultValueDict = self._networkHandler.send(getWindowHeightCmd).getResponse()[0]
        resultValue = resultValueDict['result']['result']['value']
        return resultValue

    def wait(self, seconds):
        time.sleep(seconds)

    def addShutDownHook(self, func, *args, **kwargs):
        '''
        添加当程序正常关闭时的操作,可以进行一些环境清理操作等
        '''
        self._vmShutdownHandler.registerVMShutDownCallback(func, *args, **kwargs)

    def _onUncaughtException(self, exctype, value, tb):
        self.close()

    def close(self):
        if self._networkHandler is not None:
            self._networkHandler.quit()

        if self._executor is not None:
            self._executor.shutDown()

        UncaughtExceptionHandler.removeHook()

    def setDebugLogMode(self):
        '''
        开启debug的日志模式
        '''
        Log().setDebugLogMode()
예제 #3
0
class H5Driver(object):
    def __init__(self, d, device=None):
        self._device = device
        self._urlFetcher = None
        self._webSocketDataTransfer = None
        self._vmShutdownHandler = VMShutDownHandler()
        self._networkHandler = None

        self._pageOperator = H5PageOperator()
        self.d = d
        self._hasInit = False

        self.html = None
        self.bodyNode = None

    def initDriver(self):
        if self._hasInit:
            return
        self._executor = SingleThreadExecutor()
        self._vmShutdownHandler.registerToVM()
        UncaughtExceptionHandler.init()
        UncaughtExceptionHandler.registerUncaughtExceptionCallback(
            self._onUncaughtException)

        self._selectDevice()
        self._urlFetcher = H5WebSocketDebugUrlFetcher(device=self._device)
        url = self._urlFetcher.fetchWebSocketDebugUrl()

        self._webSocketDataTransfer = WebSocketDataTransfer(url=url)
        self._networkHandler = ShortLiveWebSocket(self._webSocketDataTransfer,
                                                  self._executor, self)
        self._networkHandler.connect()

        self._executor.put(self._networkHandler.receive)
        self.logger = Log().getLogger()
        self.initPageDisplayData()
        self._hasInit = True
        self.logger.info('url ----> ' + url)

    def _selectDevice(self):
        devicesList = AdbHelper.listDevices(ignoreUnconnectedDevices=True)
        # 假如没有指定设备,那么判断当前机器是否连接1个设备
        if self._device is None:
            devicesCount = len(devicesList)
            errorMsg = None

            if devicesCount <= 0:
                errorMsg = ErrorMsgManager().errorCodeToString(
                    ERROR_CODE_DEVICE_NOT_CONNECT)

            elif devicesCount == 1:
                self._device = devicesList[0]

            else:
                errorMsg = ErrorMsgManager().errorCodeToString(
                    ERROR_CODE_MULTIPLE_DEVICE)

            if errorMsg is not None:
                raise RuntimeError(errorMsg)

    def wait(self, seconds=1):
        time.sleep(seconds)

    def _onUncaughtException(self, exctype, value, tb):
        self.close()

    def close(self):
        if self._networkHandler is not None:
            self._networkHandler.quit()

        if self._executor is not None:
            self._executor.shutDown()

        self._hasInit = False

        UncaughtExceptionHandler.removeHook()

    def addShutDownHook(self, func, *args, **kwargs):
        self._vmShutdownHandler.registerVMShutDownCallback(
            func, *args, **kwargs)

    def getDriverType(self):
        return constant.H5DRIVER

    def switchToNextPage(self):
        """
        把之前缓存的html置为none
        再重新连接websocket
        """
        self.logger.debug('')
        self.html = None
        self._networkHandler.disconnect()
        self._networkHandler.connect()

    def initPageDisplayData(self):
        driverInfo = self.d.info
        displayDp = driverInfo.get('displaySizeDpY')
        displayPx = driverInfo.get('displayHeight')
        self.scale = (displayPx - 0.5) / displayDp
        windowHeight = self.getWindowHeight()
        self.appTitleHeight = displayDp - windowHeight

    def changeDp2Px(self, xDp, yDp):
        xPx, yPx = self._pageOperator.changeDp2Px(xDp, yDp, self.scale,
                                                  self.appTitleHeight)
        return xPx, yPx

    def clickElementByXpath(self,
                            xpath,
                            visibleItemXpath=None,
                            duration=50,
                            tapCount=1):
        """
        默认滑动点为屏幕的中心,且边距为整个屏幕。当有container时,传入container中任意一个当前可见item的xpath,之后会将目标滑到该可见item的位置
        :param xpath: 要滑动到屏幕中控件的xpath
        :param visibleItemXpath: container中当前可见的一个xpath
        :return:
        """
        self.logger.info('xpath ---> ' + xpath)
        # 防止websocket未失效但页面已经开始跳转
        self.wait(WAIT_REFLESH_05_SECOND)

        if self.isElementExist(xpath):
            self.scrollToElementByXpath(xpath, visibleItemXpath)

            sendStr = self._pageOperator.getElementRect(xpath)
            self._networkHandler.send(sendStr)

            x = self._getRelativeDirectionValue("x")
            y = self._getRelativeDirectionValue("y")

            self.logger.debug('clickElementByXpath --> x:' + str(x) + '   y:' +
                              str(y))
            clickCommand = self._pageOperator.clickElementByXpath(
                x, y, duration, tapCount)
            return self._networkHandler.send(clickCommand)
        else:
            raise TypeError('未找到控件')

    def clickFirstElementByText(self,
                                text,
                                visibleItemXpath=None,
                                duration=50,
                                tapCount=1):
        """
        通过text来搜索,点击第一个text相符合的控件。参数同clickElementByXpath()
        """
        self.clickElementByXpath('.//*[text()="' + text + '"]',
                                 visibleItemXpath, duration, tapCount)

    def longClickElementByXpath(self,
                                xpath,
                                visibleItemXpath=None,
                                duration=2000,
                                tapCount=1):
        self.clickElementByXpath(xpath, visibleItemXpath, duration, tapCount)

    def repeatedClickElementByXpath(self,
                                    xpath,
                                    visibleItemXpath=None,
                                    duration=50,
                                    tapCount=2):
        self.clickElementByXpath(xpath, visibleItemXpath, duration, tapCount)

    def scrollToElementByXpath(self, xpath, visibleItemXpath=None, speed=400):
        """
        滑动屏幕,使指定xpath的控件可见
        默认滑动点为屏幕的中心,且边距为整个屏幕。当有container时,传入container中任意一个当前可见item的xpath,之后会将目标滑到该可见item的位置
        :param xpath: 要滑动到屏幕中控件的xpath
        :param visibleItemXpath: container中当前可见的一个xpath
        """
        self.logger.info('xpath ---> ' + xpath)
        sendStr = self._pageOperator.getElementRect(xpath)
        self._networkHandler.send(sendStr)
        top = self._getRelativeDirectionValue("topp")
        bottom = self._getRelativeDirectionValue("bottom")
        left = self._getRelativeDirectionValue("left")
        right = self._getRelativeDirectionValue("right")

        if visibleItemXpath is None:
            endTop = 0
            endLeft = 0
            endBottom = self.getWindowHeight()
            endRight = self.getWindowWidth()
        else:
            containerSendStr = self._pageOperator.getElementRect(
                visibleItemXpath)
            self._networkHandler.send(containerSendStr)

            endTop = self._getRelativeDirectionValue("topp")
            endBottom = self._getRelativeDirectionValue("bottom")
            endLeft = self._getRelativeDirectionValue("left")
            endRight = self._getRelativeDirectionValue("right")
        '''
        竖直方向的滑动
        '''
        if bottom > endBottom:
            scrollYDistance = endBottom - bottom
        elif top < 0:
            scrollYDistance = -(top - endTop)
        else:
            scrollYDistance = 0

        if scrollYDistance < 0:
            self.scrollWindow(int((endLeft + endRight) / 2),
                              int((endTop + endBottom) / 2), 0,
                              scrollYDistance - 80, speed)
        elif scrollYDistance > 0:
            self.scrollWindow(int((endLeft + endRight) / 2),
                              int((endTop + endBottom) / 2), 0,
                              scrollYDistance + 80, speed)
        else:
            self.logger.debug('y方向不需要滑动')
        '''
        水平方向的滑动
        '''
        if right > endRight:
            scrollXDistance = endRight - right
        elif left < 0:
            scrollXDistance = -(left - endLeft)
        else:
            scrollXDistance = 0

        if scrollXDistance != 0:
            self.scrollWindow(int((endLeft + endRight) / 2),
                              int((endTop + endBottom) / 2), scrollXDistance,
                              0, speed)
        else:
            self.logger.debug('y方向不需要滑动')

    def getElementCoordinateByXpath(self, elementXpath):
        '''
        获得Element的坐标
        :param elementXpath:待获取坐标的元素的xpath
        :return:element相对于整个屏幕的x、y坐标,单位为px
        '''
        self.logger.info('elementXpathXpath ---> ' + elementXpath)
        # 防止websocket未失效但页面已经开始跳转
        self.wait(WAIT_REFLESH_05_SECOND)
        if self.isElementExist(elementXpath):
            sendStr = self._pageOperator.getElementRect(elementXpath)
            self._networkHandler.send(sendStr)
            x = self._getRelativeDirectionValue("x")
            y = self._getRelativeDirectionValue("y")
            x, y = self.changeDp2Px(x, y)
            return x, y
        errorMessage = ErrorMsgManager().errorCodeToString(
            ERROR_CODE_GETCOORDINATE)
        raise RuntimeError(errorMessage)

    def clearInputTextByXpath(self, xpath):
        '''
        清空输入框的文字
        :param xpath:input框的xpath
        '''
        self.logger.info('xpath ---> ' + xpath)
        clearInputTextSendStr = self._pageOperator.clearInputTextByXpath(xpath)
        self._networkHandler.send(clearInputTextSendStr)

    def getWindowHeight(self):
        '''
        :return:手机屏幕的高度
        '''
        getWindowHeightCmd = self._pageOperator.getWindowHeight()
        resultValueDict = self._networkHandler.send(
            getWindowHeightCmd).getResponse()[0]
        resultValue = resultValueDict['result']['result']['value']
        return resultValue

    def getWindowWidth(self):
        '''
        :return:手机屏幕的宽度
        '''
        getWindowWidthCmd = self._pageOperator.getWindowWidth()
        resultValueDict = self._networkHandler.send(
            getWindowWidthCmd).getResponse()[0]
        resultValue = resultValueDict['result']['result']['value']
        return resultValue

    def scrollWindow(self, x, y, xDistance, yDistance, speed=800):
        """
        通过坐标来滑动(屏幕的左上角为(0,0),向下和向右坐标逐渐增大)
        :param x: 滑动的起始X点坐标
        :param y: 滑动的起始Y点坐标
        :param xDistance: X方向滑动的距离
        :param yDistance: Y方向滑动的距离
        :param speed: 滑动的速度
        """
        sendStr = self._pageOperator.scrollWindow(x, y, xDistance, yDistance,
                                                  speed)
        return self._networkHandler.send(sendStr)

    def _getRelativeDirectionValue(self, directionKey='topp', contextId=None):
        '''
        获取相关的方向数据参数值
        :param directionKey: 获取的方向
        :return:
        '''
        directionCommand = self._pageOperator.getJSValue(
            directionKey, contextId)
        directionResponse = self._networkHandler.send(
            directionCommand).getResponse()[0]
        directionValue = directionResponse['result']['result']['value']
        return directionValue

    def textElementByXpath(self, xpath, text):
        """
        先获取输入框的焦点, 再使用Chrome debug协议的输入api,再输入文字内容
        :param xpath:输入框的xpath
        :parm text:要输入的文字
        """
        self.logger.info('xpath ---> ' + xpath + ' text ---> ' + text)
        self.focusElementByXpath(xpath)
        sendStrList = self._pageOperator.textElementByXpath(text)

        for command in sendStrList:
            self._networkHandler.send(command)
        self.wait(WAIT_REFLESH_05_SECOND)

    def focusElementByXpath(self, xpath):
        """
        调用目标的focus()方法。
        :param xpath:目标的xpath
        """
        executeCmd = self._pageOperator.focusElementByXpath(xpath)
        self._networkHandler.send(executeCmd)

    def getHtml(self, nodeId=1):
        """
        获得指定nodeId的Html代码。在一条websocket连接中,方法只能够执行一次。
        :param nodeId: getDocument方法返回的nodeId,当为1时,返回整个body的代码
        """
        self.logger.info('')
        if self.html is not None:
            self.switchToNextPage()

        self.getDocument()

        sendStr = self._pageOperator.getHtml(nodeId)
        self.html = self._networkHandler.send(
            sendStr).getResponse()[0]['result']['outerHTML']
        return self.html

    def getDocument(self):
        """
        获得getHtml中需要的nodeId
        在调用getHtml之前必须先调用这个方法
        """
        sendStr = self._pageOperator.getDocument()
        return self._networkHandler.send(sendStr)

    def closeWindow(self):
        """
        关闭整个h5页面
        """
        sendStr = self._pageOperator.closeWindow()
        return self._networkHandler.send(sendStr)

    def returnLastPage(self):
        """
        返回上一页
        """
        self.logger.info('')
        self.wait(WAIT_REFLESH_05_SECOND)
        sendStr = self._pageOperator.returnLastPage()
        self._networkHandler.send(sendStr)
        self.wait(WAIT_REFLESH_05_SECOND)

    def scrollPickerByXpath(self, xpath):
        """
        滑动选择picker的选项
        要获取四个变量,所以发送了四次消息。
        1.先定位到要点击的item
        2.找到它的parent(即整个picker的content区域),并且获得要点击item的index,和总共item的个数
        3.获得picker的BoundingClientRect。再计算每个item的位置,要滑动的距离
        4.因为picker窗口弹出时,默认选择上一次的item,所以先进行一次滑动到顶端。
        (为了避免滑动时的惯性,需要设置一个speed属性,控制速度)
        :param xpath: 要选择的item
        """
        self.logger.info('xpath ---> ' + xpath)

        sendStr = self._pageOperator.getPickerRect(xpath)
        self._networkHandler.send(sendStr)

        startScrollX = self._getRelativeDirectionValue("start_scroll_x")
        startScrollY = self._getRelativeDirectionValue("start_scroll_y")

        # 获取整个列表的高度
        height = self._getRelativeDirectionValue("height")

        # 获取要滑动的距离
        dex = self._getRelativeDirectionValue("dex")

        scrollWindowCmd = self._pageOperator.scrollWindow(
            startScrollX, startScrollY, startScrollX, height)
        self._networkHandler.send(scrollWindowCmd)

        scrollWIndowWithSpeedCmd = self._pageOperator.scrollWindow(
            startScrollX, startScrollY, startScrollX, -dex, speed=72)
        self._networkHandler.send(scrollWIndowWithSpeedCmd)

    def getPageHeight(self):
        getPageHeightCmd = self._pageOperator.getPageHeight()
        resultValueDict = self._networkHandler.send(
            getPageHeightCmd).getResponse()[0]
        resultValue = resultValueDict['result']['result']['value']
        return resultValue

    def getElementTextByXpath(self, xpath):
        '''
        :param xpath: 目标的xpath
        :return: 获取到的目标text内容
        '''
        self.logger.info('xpath ---> ' + xpath)
        if self.isElementExist(xpath):
            getTextCmd = self._pageOperator.getElementTextByXpath(xpath)
            resultValueDict = self._networkHandler.send(
                getTextCmd).getResponse()[0]
            resultValue = resultValueDict['result']['result']['value'].encode(
                "utf-8")
        else:
            resultValue = None
        return resultValue

    def getElementSrcByXpath(self, xpath):
        """
        :param xpath: 目标的xpath
        :return: 获取到的目标src内容
        """
        self.logger.info('xpath ---> ' + xpath)
        if self.isElementExist(xpath):
            getSrcCmd = self._pageOperator.getElementSrcByXpath(xpath)
            resultValueDict = self._networkHandler.send(
                getSrcCmd).getResponse()[0]
            resultValue = resultValueDict['result']['result']['value'].encode(
                "utf-8")
        else:
            resultValue = None
        return resultValue

    def getElementClassNameByXpath(self, xpath):
        '''
        :param xpath:目标的xpath
        :return: 目标的className
        '''
        self.logger.info('xpath ---> ' + xpath)
        if self.isElementExist(xpath):
            getClassNameCmd = self._pageOperator.getElementClassNameByXpath(
                xpath)
            resultValueDict = self._networkHandler.send(
                getClassNameCmd).getResponse()[0]
            resultValue = resultValueDict['result']['result']['value'].encode(
                "utf-8")
        else:
            resultValue = None
        return resultValue

    def getElementByXpath(self, xpath):
        """
        :param:目标的xpath
        :return:返回lxml包装过的element对象,可以使用lxml语法获得对象的信息
        例如:可以使用element.get("attrs")来拿到属性的数据
        可以用element.text拿到它的文字
        当element中含有列表时,使用for循环读取每一个item
        """
        self.logger.info('xpath ---> ' + xpath)
        html = etree.HTML(self.getHtml())
        return html.xpath(xpath)[0]

    def isElementExist(self, xpath, contextId=None):
        """
        :param xpath: 目标的xpath
        :return: 返回一个boolean,该Element是否存在
        """
        self.logger.info('xpath ---> ' + xpath)
        getExistCmd = self._pageOperator.isElementExist(xpath, contextId)
        resultValueDict = self._networkHandler.send(
            getExistCmd).getResponse()[0]
        resultType = resultValueDict['result']['result']['subtype']
        num = 0
        while resultType == 'null' and num < 3:
            self.wait(WAIT_REFLESH_2_SECOND)
            getExistCmd = self._pageOperator.isElementExist(xpath, contextId)
            resultValueDict = self._networkHandler.send(
                getExistCmd).getResponse()[0]
            resultType = resultValueDict['result']['result']['subtype']
            num = num + 1
        return resultType != 'null'

    def navigateToPage(self, url):
        """
        跳转到指定url,在某些微信版本上不生效
        :param url: 要跳转的url
        """
        self.logger.info('url ---> ' + url)
        navigateCmd = self._pageOperator.navigateToPage(url)
        self._networkHandler.send(navigateCmd)

    def executeScript(self, script):
        """
        手动发送js命令并执行
        :param script:要执行的js指令
        :return: 执行结果
        """
        executeCmd = self._pageOperator.executeScript(script)
        resultValueDict = self._networkHandler.send(
            executeCmd).getResponse()[0]

        return resultValueDict

    def getCurrentPageUrl(self):
        """
        获得当前页面的url
        :return:
        """
        executeCmd = self._pageOperator.getCurrentPageUrl()
        resultValueDict = self._networkHandler.send(
            executeCmd).getResponse()[0]
        resultValue = resultValueDict['result']['result']['value'].encode(
            "utf-8")
        return resultValue

    '''
    针对跨域IFrame进行的操作
    '''

    def _getAllNodeId(self):
        '''
        获得body标签中所有的包含IFrame数据的NodeId
        :return:
        '''
        nodeIdList = []
        nodesList = self.getBodyNode()['params']['nodes']
        for node in nodesList:
            if node.get('contentDocument') is not None:
                nodeIdList.append(node.get('contentDocument').get('nodeId'))
        return nodeIdList

    def getBodyNode(self):
        '''
        :return:获得body中所有node的frameId
        '''
        self.switchToNextPage()
        self.wait(WAIT_REFLESH_2_SECOND)
        self.getDocument()
        executeCmd = self._pageOperator.getBodyNode()
        resultValueDict = self._networkHandler.send(
            executeCmd).getResponse()[0]
        return resultValueDict

    def requestChildNodes(self, nodeId=5):
        '''
        :param nodeId:指定的nodeId
        :return:
        '''
        self.switchToNextPage()
        self.wait(WAIT_REFLESH_2_SECOND)
        self.getDocument()
        executeCmd = self._pageOperator.requestChildNodes(nodeId)
        resultValueDict = self._networkHandler.send(
            executeCmd).getResponse()[0]
        return resultValueDict

    def getIFrameContextId(self):
        '''
        当body标签中只存在一个IFrame调用
        :return:ContextId
        '''
        time.sleep(5)
        frameIdList = self.getAllIFrameNode()
        contextList = self.getAllContext()
        if len(frameIdList) == 1:
            for contextInfo in contextList:
                if contextInfo["frameId"] == frameIdList[0]:
                    return contextInfo["contextId"]

    def getAllContext(self):
        '''
        获得所有的Context 
        :return: 所有的ContextId,域以及FrameId
        '''
        executeCmd = self._pageOperator.getAllContext()
        resultValueDict = self._networkHandler.send(executeCmd).getResponse()
        resultDictList = []
        for dict in resultValueDict:
            if dict.get('result') is None:
                resultDict = {}
                if dict.get('params').get('context') is not None:
                    context = dict.get('params').get('context')
                    origin = context.get('origin')
                    contextId = context.get('id')
                    frameId = context.get('auxData').get('frameId')
                    resultDict['origin'] = origin
                    resultDict['contextId'] = contextId
                    resultDict['frameId'] = frameId
                    resultDictList.append(resultDict)
        return resultDictList

    def getAllIFrameNode(self):
        '''
        获得body标签中所有的IFrameNode
        :return:
        '''
        try:
            iFrameNodeList = []
            nodesList = self.getBodyNode()['params']['nodes']
            for node in nodesList:
                if node['nodeName'] == 'IFRAME':
                    iFrameNodeList.append(node)
            frameIdList = []
            for iFrameNode in iFrameNodeList:
                frameIdList.append(iFrameNode['frameId'])
            return frameIdList
        except:
            errorMessage = ErrorMsgManager().errorCodeToString(
                ERROR_CODE_SETUP_FRAME_PAGE)
            raise RuntimeError(errorMessage)

    def getIFrameNodeId(self):
        '''
        当body标签中只存在一个IFrame调用
        :return:IFrame的NodeId,可以通过它获得html
        '''
        nodeIdList = self._getAllNodeId()
        if len(nodeIdList) == 1:
            return nodeIdList[0]

    def getIFrameElementCoordinateByXpath(self, elementXpath, iFrameXpath,
                                          contextId):
        '''
        获得IFrame中元素的坐标
        :param elementXpath:待获取坐标的元素的xpath
        :param iFrameXpath: 外层iFrame的xpath
        :param contextId: iFrame的ContextId
        :return:element相对于整个屏幕的x、y坐标,单位为px
        '''
        self.logger.info('elementXpathXpath ---> ' + elementXpath +
                         ' iFrameXpath ---> ' + iFrameXpath +
                         ' contextId ---> ' + str(contextId))
        # 防止websocket未失效但页面已经开始跳转
        self.wait(WAIT_REFLESH_05_SECOND)
        if self.isElementExist(iFrameXpath):
            sendStr = self._pageOperator.getElementRect(iFrameXpath)
            self._networkHandler.send(sendStr)
            iframeLeft = self._getRelativeDirectionValue("left")
            iframeTop = self._getRelativeDirectionValue("topp")
            if self.isElementExist(elementXpath, contextId):
                sendStr = self._pageOperator.getElementRect(
                    elementXpath, contextId)
                self._networkHandler.send(sendStr)
                x = self._getRelativeDirectionValue("x", contextId)
                y = self._getRelativeDirectionValue("y", contextId)
                x = iframeLeft + x
                y = iframeTop + y
                x, y = self.changeDp2Px(x, y)
                return x, y
        errorMessage = ErrorMsgManager().errorCodeToString(
            ERROR_CODE_GETCOORDINATE)
        raise RuntimeError(errorMessage)

    def getIFrameHtml(self, nodeId=None):
        '''
        :param nodeId:
        :return: 获得指定nodeId的Html
        '''

        self.logger.info('')

        self.switchToNextPage()

        self.getDocument()

        self.wait(WAIT_REFLESH_05_SECOND)
        sendStr = self._pageOperator.requestChildNodes()
        self._networkHandler.send(sendStr)

        if nodeId is None:
            nodeId = self.getIFrameNodeId()
        sendStr = self._pageOperator.getHtml(nodeId)

        result = self._networkHandler.send(sendStr).getResponse()[0]
        self.html = result.get('result').get('outerHTML')
        return self.html

    def getIFrameElementByXpath(self, xpath, nodeId):
        '''
        :param xpath:element的Xpath
        :param nodeId: element所在页面的nodeId
        :return: lxml的ELement对象
        '''
        self.logger.info('xpath ---> ' + xpath)
        html = etree.HTML(self.getIFrameHtml(nodeId))
        self.logger.info(etree.tostring(html))
        return html.xpath(xpath)[0]

    '''
    性能数据
    '''

    def getMemoryInfo(self):
        '''
        获得H5进程占用内存信息
        :return: H5进程占用内存信息
        '''
        self.logger.info('')
        ADB_SHELL = 'adb shell '
        if self._device:
            ADB_SHELL = ADB_SHELL.replace("adb", "adb -s %s" % self._device)
        GET_MEMORY_INFO = ADB_SHELL + 'dumpsys meminfo com.tencent.mm:tools'
        return commandHelper.runCommand(GET_MEMORY_INFO)

    def getCPUInfo(self):
        '''
        获得H5进程占用CPU信息
        :return: H5进程占用CPU信息
        '''
        self.logger.info('')
        ADB_SHELL = 'adb shell '
        if self._device:
            ADB_SHELL = ADB_SHELL.replace("adb", "adb -s %s" % self._device)
        GET_CPU_INFO = ADB_SHELL + ' top -n 1 | grep com.tencent.mm:tools'
        return commandHelper.runCommand(GET_CPU_INFO)

    def setDebugLogMode(self):
        Log().setDebugLogMode()