接上文https://testerhome.com/topics/7658

由于项目特点,无法通过 ID/NAME 等方式查找 element 并进行相关操作,但又不想用 XPATH(使用 XPATH 定位使得脚本可读性变低,加之页面布局经常改动,使得使用 XPATH 的维护量比较大)。因此,写了通用方法(by_accessibility_id)来查找页面 element。另外,还写了 wait_string 的方法来判断页面中是否有特定的字符串。

使用举例:

class MyCarPage(BasePage):
    @teststep
    def click_add_car(self):
        self.click_element_by_accessibility_id('添加车辆')
class InviteFriendsPage(BasePage):
    @teststep
    def wait_page(self):
        if self.wait_string_use_and('福利', '活动规则'):
            return True
        else:
            return False

源码如下:
1、其中的_wait_element 是一个通用的内部方法,大家可以用这个方法构造出其他的定位方式,如 self._wait_element('content-desc', value=value, max_time=max_time) 中,using='content-desc'就是通过'content-desc'查找 element。
2、大家有更好的实现方式可以一同讨论

from xml.etree.ElementTree import ElementTree

class BasePage(object):
    @classmethod
    def set_driver(cls, dri):
        cls.driver = dri

    def get_driver(self):
        return self.driver

    def wait_string(self, string, max_time=10):
        """
        wait string between the gaven time
        :param string: string
        :param max_time: time that wait the gaven strings
        :return: True or False
        """
        times = max_time

        for i in range(times):
            time.sleep(1)

            source = self.driver.source
            if string in source:
                return True

            if i == times - 1:
                return False

    def wait_string_use_and(self, *args, max_time=10):
        """
        wait all strings between the gaven time
        :param args: strings
        :param max_time: time that wait the gaven strings
        :return: True or False
        """
        times = max_time

        for i in range(10):
            time.sleep(1)

            source = self.driver.source
            previous = False
            for j in range(len(args)):
                if args[j] in source:
                    if j != 0 and not previous:
                        return False

                    previous = True

                    if j == len(args) - 1:
                        return True
                else:
                    if previous:
                        return False

            if i == times - 1:
                return False

    def wait_string_use_or(self, *args, max_time=10):
        """
        wait anyone string between the gaven time
        :param args: strings
        :param max_time: time that wait the gaven strings
        :return: True or False
        """
        times = max_time

        for i in range(10):
            time.sleep(1)

            source = self.driver.source
            for string in args:
                if string in source:
                    return True

            if i == times - 1:
                return False

    def _find_element_in_nodes(self, nodes, using, value):
        for node in nodes:
            result = self._find_element_in_node(node, using, value)
            if result is not None:
                return result

    def _find_element_in_node(self, node, using, value):
        if node.get(using) == value:
            return node

        child_nodes = node.findall('node')
        if len(child_nodes):
            return self._find_element_in_nodes(child_nodes, using, value)
        else:
            return None

    @staticmethod
    def _parse_bounds(bounds_str):
        bounds = bounds_str.split(',')
        x_left = int(bounds[0].strip('['))
        y_up = int(bounds[1].split(']')[0])
        x_right = int(bounds[1].split(']')[1].strip('['))
        y_down = int(bounds[2].strip(']'))

        x_center = (x_left + x_right) / 2
        y_center = (y_up + y_down) / 2

        return x_center, y_center

    @staticmethod
    def _delete_file(filename):
        if os.path.exists(filename):
            os.remove(filename)

    def _wait_element(self, using, value, max_time=10):
        times = max_time

        stability_flag = ''
        for i in range(times):
            time.sleep(1)

            file = 'source.xml'
            with open(file, 'wt', encoding='utf-8') as f:
                f.write(self.driver.source)

            try:
                tree = ElementTree()
                tree.parse(file)
                root = tree.getroot()

                nodes = root.findall('node')
                result = self._find_element_in_nodes(nodes, using, value)
                if result is not None:
                    bounds_str = result.get('bounds')
                    if stability_flag != bounds_str:
                        stability_flag = bounds_str
                    else:
                        self._delete_file(file)
                        return result

                if i == times - 1:
                    self._delete_file(file)
                    return None
            except Exception:
                self._delete_file(file)
                raise Exception

    def wait_element_by_accessibility_id(self, value, max_time=10):
        """
        wait element by accessibility id. in android, wait content-desc
        :param value: value
        :param max_time: time that wait the gaven value of accessibility id
        :return: True or False
        """
        result = self._wait_element('content-desc', value=value, max_time=max_time)
        if result is not None:
            return True
        else:
            return False

    def click_element_by_accessibility_id(self, value, max_time=10):
        """
        click element by accessibility id. in android, wait content-desc
        :param value: value
        :param max_time: time that wait the gaven value of accessibility id
        :return:

        raise:
            WebDriverException
        """
        result = self._wait_element('content-desc', value=value, max_time=max_time)
        if result is not None:
            x, y = self._parse_bounds(result.get('bounds'))
            self._tap(x, y)
        else:
            raise WebDriverException('No such accessibility id that value was you gaven.')


↙↙↙阅读原文可查看相关链接,并与作者交流