mozilla / geckodriver

WebDriver for Firefox
https://firefox-source-docs.mozilla.org/testing/geckodriver/
Mozilla Public License 2.0
7.03k stars 1.51k forks source link

Facing 'could not be scrolled into view' when testing on geckodriver with firefox, using driver.execute_script("arguments[0].scrollIntoView(true);", ele) or driver.execute_script("arguments[0].click()", ele) #2143

Closed bigbang007jay closed 2 months ago

bigbang007jay commented 6 months ago

System

Testcase

Below is the code:

@allure.feature("Frontend-My Space")
@allure.story("My Space-Green Test")
@pytest.mark.cmp_green
@pytest.mark.cmp_myspace
# @pytest.mark.skip(reason="test")
@pytest.mark.parametrize("menuPermission",
                         [("My Space/Participant Info/Home"),
                          ("My Space/Participant Info/Profile"),
                          ("My Space/Participant Info/Bank Info"),
                          ("My Space/Security Setting/Security Center"),
                          ("My Space/Operator Mgt/Menu Permission"),
                          ("My Space/Operator Mgt/User Mgt"),
                          ("My Space/Work Logs/Operation logs"),
                          ("My Space/Work Logs/Login logs"),
                          ("My Space/Message Center/Message center list")])
@pytest.mark.parametrize("user_login", [("****", "******")], indirect=True)
def test_green(self, user_login, menuPermission):
  navigate_to_page(menuPermission)

def navigate_to_page(pages): wait = WebDriverWait(driver, timeout=15) pages = pages.split("/") for i, page in enumerate(pages): page_xpath = "//*[text()=\"" + page + "\"]" if page == "Portfolio" or page == "Market Place": eles = wait.until(method=lambda x: driver.find_elements(By.XPATH, page_xpath)) eles[i].click() else: ele = wait.until(EC.presence_of_element_located((By.XPATH, page_xpath))) ele.click()

def click(self):
    ele = self.wait.until(EC.presence_of_element_located((self.ele_type, self.ele_loc)), EC.staleness_of((self.ele_type, self.ele_loc)))
 driver.execute_script("arguments[0].scrollIntoView(true);", ele)
 driver.execute_script("arguments[0].click()", ele)

I'm using this to test Edge and Chrome and it works out everytime. However, when tesing on Firefox , sometimes it throw the exceptions as below:

Stacktrace

confest.py.pytesthook1 Test Failed: tests/test_cmp_myspace.py::TestCmpMySpace::test_green[user_login0-My Space/Security Setting/Security Center] Reason: ElementNotInteractableException, Message: Element could not be scrolled into view Stacktrace: RemoteError@chrome://remote/content/shared/RemoteError.sys.mjs:8:8 WebDriverError@chrome://remote/content/shared/webdriver/Errors.sys.mjs:189:5 ElementNotInteractableError@chrome://remote/content/shared/webdriver/Errors.sys.mjs:349:5 webdriverClickElement@chrome://remote/content/marionette/interaction.sys.mjs:166:11 interaction.clickElement@chrome://remote/content/marionette/interaction.sys.mjs:135:11 clickElement@chrome://remote/content/marionette/actors/MarionetteCommandsChild.sys.mjs:202:29 receiveMessage@chrome://remote/content/marionette/actors/MarionetteCommandsChild.sys.mjs:84:31

@allure.feature("Frontend-My Space")
@allure.story("My Space-Green Test")
@pytest.mark.cmp_green
@pytest.mark.cmp_myspace
# @pytest.mark.skip(reason="test")
@pytest.mark.parametrize("menuPermission",
                         [("My Space/Participant Info/Home"),
                          ("My Space/Participant Info/Profile"),
                          ("My Space/Participant Info/Bank Info"),
                          ("My Space/Security Setting/Security Center"),
                          ("My Space/Operator Mgt/Menu Permission"),
                          ("My Space/Operator Mgt/User Mgt"),
                          ("My Space/Work Logs/Operation logs"),
                          ("My Space/Work Logs/Login logs"),
                          ("My Space/Message Center/Message center list")])
@pytest.mark.parametrize("user_login", [("****", "******")], indirect=True)
def test_green(self, user_login, menuPermission):
  navigate_to_page(menuPermission)

test_cmp_myspace.py:56:


..\src\toolkit\common.py:59: in navigate_to_page ele.click() ..\venv\lib\site-packages\selenium\webdriver\remote\webelement.py:88: in click self._execute(Command.CLICK_ELEMENT) ..\venv\lib\site-packages\selenium\webdriver\remote\webelement.py:396: in _execute return self._parent.execute(command, params) ..\venv\lib\site-packages\selenium\webdriver\remote\webdriver.py:429: in execute self.error_handler.check_response(response)


self = <selenium.webdriver.remote.errorhandler.ErrorHandler object at 0x000001CC2F7F6A30> response = {'status': 400, 'value': '{"value":{"error":"element not interactable","message":"Element <a href=\"https://sitcmpucw...sys.mjs:202:29\\nreceiveMessage@chrome://remote/content/marionette/actors/MarionetteCommandsChild.sys.mjs:84:31\\n"}}'}

def check_response(self, response: Dict[str, Any]) -> None:
    """
    Checks that a JSON response from the WebDriver does not have an error.

    :Args:
     - response - The JSON response from the WebDriver server as a dictionary
       object.

    :Raises: If the response contains an error message.
    """
    status = response.get('status', None)
    if not status or status == ErrorCode.SUCCESS:
        return
    value = None
    message = response.get("message", "")
    screen: str = response.get("screen", "")
    stacktrace = None
    if isinstance(status, int):
        value_json = response.get('value', None)
        if value_json and isinstance(value_json, str):
            import json
            try:
                value = json.loads(value_json)
                if len(value.keys()) == 1:
                    value = value['value']
                status = value.get('error', None)
                if not status:
                    status = value.get("status", ErrorCode.UNKNOWN_ERROR)
                    message = value.get("value") or value.get("message")
                    if not isinstance(message, str):
                        value = message
                        message = message.get('message')
                else:
                    message = value.get('message', None)
            except ValueError:
                pass

    exception_class: Type[WebDriverException]
    if status in ErrorCode.NO_SUCH_ELEMENT:
        exception_class = NoSuchElementException
    elif status in ErrorCode.NO_SUCH_FRAME:
        exception_class = NoSuchFrameException
    elif status in ErrorCode.NO_SUCH_SHADOW_ROOT:
        exception_class = NoSuchShadowRootException
    elif status in ErrorCode.NO_SUCH_WINDOW:
        exception_class = NoSuchWindowException
    elif status in ErrorCode.STALE_ELEMENT_REFERENCE:
        exception_class = StaleElementReferenceException
    elif status in ErrorCode.ELEMENT_NOT_VISIBLE:
        exception_class = ElementNotVisibleException
    elif status in ErrorCode.INVALID_ELEMENT_STATE:
        exception_class = InvalidElementStateException
    elif status in ErrorCode.INVALID_SELECTOR \
            or status in ErrorCode.INVALID_XPATH_SELECTOR \
            or status in ErrorCode.INVALID_XPATH_SELECTOR_RETURN_TYPER:
        exception_class = InvalidSelectorException
    elif status in ErrorCode.ELEMENT_IS_NOT_SELECTABLE:
        exception_class = ElementNotSelectableException
    elif status in ErrorCode.ELEMENT_NOT_INTERACTABLE:
        exception_class = ElementNotInteractableException
    elif status in ErrorCode.INVALID_COOKIE_DOMAIN:
        exception_class = InvalidCookieDomainException
    elif status in ErrorCode.UNABLE_TO_SET_COOKIE:
        exception_class = UnableToSetCookieException
    elif status in ErrorCode.TIMEOUT:
        exception_class = TimeoutException
    elif status in ErrorCode.SCRIPT_TIMEOUT:
        exception_class = TimeoutException
    elif status in ErrorCode.UNKNOWN_ERROR:
        exception_class = WebDriverException
    elif status in ErrorCode.UNEXPECTED_ALERT_OPEN:
        exception_class = UnexpectedAlertPresentException
    elif status in ErrorCode.NO_ALERT_OPEN:
        exception_class = NoAlertPresentException
    elif status in ErrorCode.IME_NOT_AVAILABLE:
        exception_class = ImeNotAvailableException
    elif status in ErrorCode.IME_ENGINE_ACTIVATION_FAILED:
        exception_class = ImeActivationFailedException
    elif status in ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS:
        exception_class = MoveTargetOutOfBoundsException
    elif status in ErrorCode.JAVASCRIPT_ERROR:
        exception_class = JavascriptException
    elif status in ErrorCode.SESSION_NOT_CREATED:
        exception_class = SessionNotCreatedException
    elif status in ErrorCode.INVALID_ARGUMENT:
        exception_class = InvalidArgumentException
    elif status in ErrorCode.NO_SUCH_COOKIE:
        exception_class = NoSuchCookieException
    elif status in ErrorCode.UNABLE_TO_CAPTURE_SCREEN:
        exception_class = ScreenshotException
    elif status in ErrorCode.ELEMENT_CLICK_INTERCEPTED:
        exception_class = ElementClickInterceptedException
    elif status in ErrorCode.INSECURE_CERTIFICATE:
        exception_class = InsecureCertificateException
    elif status in ErrorCode.INVALID_COORDINATES:
        exception_class = InvalidCoordinatesException
    elif status in ErrorCode.INVALID_SESSION_ID:
        exception_class = InvalidSessionIdException
    elif status in ErrorCode.UNKNOWN_METHOD:
        exception_class = UnknownMethodException
    else:
        exception_class = WebDriverException
    if not value:
        value = response['value']
    if isinstance(value, str):
        raise exception_class(value)
    if message == "" and 'message' in value:
        message = value['message']

    screen = None  # type: ignore[assignment]
    if 'screen' in value:
        screen = value['screen']

    stacktrace = None
    st_value = value.get('stackTrace') or value.get('stacktrace')
    if st_value:
        if isinstance(st_value, str):
            stacktrace = st_value.split('\n')
        else:
            stacktrace = []
            try:
                for frame in st_value:
                    line = frame.get("lineNumber", "")
                    file = frame.get("fileName", "<anonymous>")
                    if line:
                        file = f"{file}:{line}"
                    meth = frame.get('methodName', '<anonymous>')
                    if 'className' in frame:
                        meth = "{}.{}".format(frame['className'], meth)
                    msg = "    at %s (%s)"
                    msg = msg % (meth, file)
                    stacktrace.append(msg)
            except TypeError:
                pass
    if exception_class == UnexpectedAlertPresentException:
        alert_text = None
        if 'data' in value:
            alert_text = value['data'].get('text')
        elif 'alert' in value:
            alert_text = value['alert'].get('text')
        raise exception_class(message, screen, stacktrace, alert_text)  # type: ignore[call-arg]  # mypy is not smart enough here
  raise exception_class(message, screen, stacktrace)

E selenium.common.exceptions.ElementNotInteractableException: Message: Element could not be scrolled into view E Stacktrace: E RemoteError@chrome://remote/content/shared/RemoteError.sys.mjs:8:8 E WebDriverError@chrome://remote/content/shared/webdriver/Errors.sys.mjs:189:5 E ElementNotInteractableError@chrome://remote/content/shared/webdriver/Errors.sys.mjs:349:5 E webdriverClickElement@chrome://remote/content/marionette/interaction.sys.mjs:166:11 E interaction.clickElement@chrome://remote/content/marionette/interaction.sys.mjs:135:11 E clickElement@chrome://remote/content/marionette/actors/MarionetteCommandsChild.sys.mjs:202:29 E receiveMessage@chrome://remote/content/marionette/actors/MarionetteCommandsChild.sys.mjs:84:31

..\venv\lib\site-packages\selenium\webdriver\remote\errorhandler.py:243: ElementNotInteractableException

Please help check if something wrong? Thanks.

whimboo commented 6 months ago

Could you please attach a trace-level log from geckodriver? You can strip it down to just include the error and some lines before to hide privacy related details. Also when you run the test please set the following Firefox preference: "remote.log.truncate"=false to prevent truncation of log entries.

As it looks like in some circumstances the element that you want to click is not directly reachable. But it's hard to say given the amount of details so far.

Thanks.

whimboo commented 6 months ago

The error is still truncated: Element <a href=\"https://..com/user/account_safe_center.htm\"> could not be scrolled into view (see the 2 dots) which means that the firefox preference has not been correctly set.

whimboo commented 6 months ago

@bigbang007jay as it looks like something went wrong with uploading the logs. The link just references this issue.

whimboo commented 6 months ago

Great. That's indeed better and I can actually see the problem now. It's by surprise not the click on Message center list which indeed raises a element not interactable on purpose because it's not visible. The real problem is the click on Message center:

1703485200208   webdriver::server   DEBUG   -> POST /session/db9f6acc-8d0a-416b-9ba9-3477eb7e20cd/element {"using": "xpath", "value": "//*[text()=\"Message Center\"]"}
1703485200208   Marionette  DEBUG   0 -> [0,24,"WebDriver:FindElement",{"using":"xpath","value":"//*[text()=\"Message Center\"]"}]
1703485200209   Marionette  TRACE   [13] MarionetteCommands actor created for window id 6442450950
1703485200210   Marionette  DEBUG   0 <- [1,24,null,{"value":{"element-6066-11e4-a52e-4f735466cecf":"5544281a-8067-40f2-ae38-e686f9cbc832"}}]
1703485200210   webdriver::server   DEBUG   <- 200 OK {"value":{"element-6066-11e4-a52e-4f735466cecf":"5544281a-8067-40f2-ae38-e686f9cbc832"}}
1703485200211   webdriver::server   DEBUG   -> POST /session/db9f6acc-8d0a-416b-9ba9-3477eb7e20cd/element/5544281a-8067-40f2-ae38-e686f9cbc832/click {"id": "5544281a-8067-40f2-ae38-e686f9cbc832"}
1703485200213   Marionette  DEBUG   0 -> [0,25,"WebDriver:ElementClick",{"id":"5544281a-8067-40f2-ae38-e686f9cbc832"}]
1703485200716   Marionette  WARN    TimedPromise timed out after 500 ms: stacktrace:
TimedPromise/<@chrome://remote/content/marionette/sync.sys.mjs:219:24
TimedPromise@chrome://remote/content/marionette/sync.sys.mjs:203:10
interaction.flushEventLoop@chrome://remote/content/marionette/interaction.sys.mjs:466:10
webdriverClickElement@chrome://remote/content/marionette/interaction.sys.mjs:219:33
send@https://sitcmpucws.qme.com/scripts/jQuery/jquery-3.6.4.min.js:2:82989
ajax@https://sitcmpucws.qme.com/scripts/jQuery/jquery-3.6.4.min.js:2:78566
loginSuccessTouch@https://sitcmpucws.qme.com/index/default.htm?code=myspace:451:16
@https://sitcmpucws.qme.com/index/default.htm?code=myspace:419:13
e@https://sitcmpucws.qme.com/scripts/jQuery/jquery-3.6.4.min.js:2:30158
Deferred/then/l/</t<@https://sitcmpucws.qme.com/scripts/jQuery/jquery-3.6.4.min.js:2:30460
1703485200922   Marionette  TRACE   Canceled page load listener because no navigation has been detected
1703485200922   Marionette  DEBUG   0 <- [1,25,null,{"value":null}]

Could it be that this link is running some JS to expend the menu? As seen above it at least doesn't trigger a navigation as it looks like. If that is the case your test potentially should wait until Message center list is actually displayed before continuing with the click on it. Without a HTML testcase I cannot say more here.

whimboo commented 2 months ago

Closing as incomplete because of a missing reply from the reporter, but happy to reopen if more details are provided, which demonstrate that this is an issue with geckodriver or Firefox.