SeldomQA / poium

Page Objects design pattern test library, support selenium、appium、playwright, etc
https://pypi.org/project/poium
Apache License 2.0
404 stars 138 forks source link

execut_script() TypeError:Object of type Element is not json serializable #26

Open lileeves opened 3 years ago

lileeves commented 3 years ago

申明一个元素 class xxPage(Page): xxbtn = Element(xpath="~") 在测试类中: class TestXX: def test_xxpage(browser): page = xxPage(broeser) page.execut_script("arguments[0].click()", page.xxbtn) 在selenium 中都可以通过 driver.execut_script("arguments[0].click()", xxbtn) 执行,但是poium 报错TypeError:Object of type Element is not json serializable,是什么问题呢?

YunJing-P commented 2 years ago

原因(应该是这样)

page.execute_script("arguments[0].click()", page.xxbtn)

Element类是描述符类,走的是__get__方法,返回self(应该是当前实例),并不是elem(元素),所以报错了 看page_objects.py 130行

class Element(object):
    """
    Returns an element object
    """

    def __init__(self, timeout=5, describe="undefined", index=0, **kwargs):
        self.timeout = timeout
        self.index = index
        self.desc = describe
        if not kwargs:
            raise ValueError("Please specify a locator")
        if len(kwargs) > 1:
            raise ValueError("Please specify only one locator")
        self.kwargs = kwargs
        self.k, self.v = next(iter(kwargs.items()))

        if self.k not in LOCATOR_LIST.keys():
            raise FindElementTypesError("Element positioning of type '{}' is not supported.".format(self.k))

    def __get__(self, instance, owner):
        if instance is None:
            return None

        Browser.driver = instance.driver
        return self

    def __set__(self, instance, value):
        self.__get__(instance, instance.__class__)
        self.send_keys(value)

    ...

    def __get_element(self, by, value):
        """
        Judge element positioning way, and returns the element.
        """

        # selenium
        if by == "id_":
            self.__find_element((By.ID, value))
            elem = Browser.driver.find_elements_by_id(value)[self.index]
        elif by == "name":
            self.__find_element((By.NAME, value))
            elem = Browser.driver.find_elements_by_name(value)[self.index]
        elif by == "class_name":
            self.__find_element((By.CLASS_NAME, value))
            elem = Browser.driver.find_elements_by_class_name(value)[self.index]
        elif by == "tag":
            self.__find_element((By.TAG_NAME, value))
            elem = Browser.driver.find_elements_by_tag_name(value)[self.index]
        elif by == "link_text":
            self.__find_element((By.LINK_TEXT, value))
            elem = Browser.driver.find_elements_by_link_text(value)[self.index]
        elif by == "partial_link_text":
            self.__find_element((By.PARTIAL_LINK_TEXT, value))
            elem = Browser.driver.find_elements_by_partial_link_text(value)[self.index]
        elif by == "xpath":
            self.__find_element((By.XPATH, value))
            elem = Browser.driver.find_elements_by_xpath(value)[self.index]
        elif by == "css":
            self.__find_element((By.CSS_SELECTOR, value))
            elem = Browser.driver.find_elements_by_css_selector(value)[self.index]

        # appium
        elif by == "ios_uiautomation":
            self.__find_element((MobileBy.IOS_UIAUTOMATION, value))
            elem = Browser.driver.find_elements_by_ios_uiautomation(value)[self.index]
        elif by == "ios_predicate":
            self.__find_element((MobileBy.IOS_PREDICATE, value))
            elem = Browser.driver.find_elements_by_ios_predicate(value)[self.index]
        elif by == "ios_class_chain":
            self.__find_element((MobileBy.IOS_CLASS_CHAIN, value))
            elem = Browser.driver.find_elements_by_ios_class_chain(value)[self.index]
        elif by == "android_uiautomator":
            self.__find_element((MobileBy.ANDROID_UIAUTOMATOR, value))
            elem = Browser.driver.find_elements_by_android_uiautomator(value)[self.index]
        elif by == "android_viewtag":
            self.__find_element((MobileBy.ANDROID_VIEWTAG, value))
            elem = Browser.driver.find_elements_by_android_viewtag(value)[self.index]
        elif by == "android_data_matcher":
            self.__find_element((MobileBy.ANDROID_DATA_MATCHER, value))
            elem = Browser.driver.find_elements_by_android_data_matcher(value)[self.index]
        elif by == "accessibility_id":
            self.__find_element((MobileBy.ACCESSIBILITY_ID, value))
            elem = Browser.driver.find_elements_by_accessibility_id(value)[self.index]
        elif by == "android_view_matcher":
            self.__find_element((MobileBy.ANDROID_VIEW_MATCHER, value))
            elem = Browser.driver.find_elements_by_android_view_matcher(value)[self.index]
        elif by == "windows_uiautomation":
            self.__find_element((MobileBy.WINDOWS_UI_AUTOMATION, value))
            elem = Browser.driver.find_elements_by_windows_uiautomation(value)[self.index]
        elif by == "image":
            self.__find_element((MobileBy.IMAGE, value))
            elem = Browser.driver.find_elements_by_image(value)[self.index]
        elif by == "custom":
            self.__find_element((MobileBy.CUSTOM, value))
            elem = Browser.driver.find_elements_by_custom(value)[self.index]
        else:
            raise FindElementTypesError(
                "Please enter the correct targeting elements")
        if Browser.show is True:
            try:
                style_red = 'arguments[0].style.border="2px solid #FF0000"'
                style_blue = 'arguments[0].style.border="2px solid #00FF00"'
                style_null = 'arguments[0].style.border=""'

                for _ in range(2):
                    Browser.driver.execute_script(style_red, elem)
                    sleep(0.1)
                    Browser.driver.execute_script(style_blue, elem)
                    sleep(0.1)
                Browser.driver.execute_script(style_blue, elem)
                sleep(0.5)
                Browser.driver.execute_script(style_null, elem)
            except WebDriverException:
                pass

        return elem
...

其实__elements()返回的才是page.execute_script("arguments[0].click()", elems)应该接受的参数

但虫师大佬把这个方法私有化了,不能在外部调用了(如果有误我道歉)

我的处理方法是

"""
InheritElement.py
"""
from poium.config import Browser
from poium.common import logging
from poium import Element

class InheritElement(Element):
    def click_js(self):
        Browser.driver.execute_script("arguments[0].click();", self._Element__get_element(self.k, self.v))
        logging.info("🖱 click element by click_js: {}".format(self.desc))

我的处理方法是再封装一层类,强制调用私有方法(但这种做法不是很符合规范)