SeleniumHQ / selenium

A browser automation framework and ecosystem.
https://selenium.dev
Apache License 2.0
30.5k stars 8.15k forks source link

Returning const in execute_script results in Exception #9468

Closed picciama closed 3 years ago

picciama commented 3 years ago

🐛 Bug Report

Global objects that are accessible via Firefox Console cannot be accessed with selenium. This only affects const it seems, i.e. var are visible to selenium. When trying to return these values using the execute_script function, a JavascriptException is thrown. The issue is specific to using the geckodriver. It works as expected using the chromedriver.

To Reproduce

  1. In Python, create a driver instance and navigate to any website.

    from selenium import webdriver
    driver = webdriver.Firefox()
    driver.get(https://google.com)
  2. Add const via Firefox console:

    const test_val = 'test'
    function return_test_val() {
    return test_val;
    }
  3. In Python, execute the following:

    driver.execute_script("return test_val;")   # This will result in a JavaScriptException
    driver.execute_script("function return_test_val() {return test_val} return return_test_val;")  # This will result in the same Exception
    driver.execute_script("return return_test_val();")  # This is working (function defined in Firefox console)

The exception raised:

---------------------------------------------------------------------------
JavascriptException                       Traceback (most recent call last)
<ipython-input-35-aa126bcc64f7> in <module>
----> 1 driver.execute_script('return test_val;')

~/miniconda3/envs/AutoTest/lib/python3.9/site-packages/selenium/webdriver/remote/webdriver.py in execute_script(self, script, *args)
    632             command = Command.EXECUTE_SCRIPT
    633 
--> 634         return self.execute(command, {
    635             'script': script,
    636             'args': converted_args})['value']

~/miniconda3/envs/AutoTest/lib/python3.9/site-packages/selenium/webdriver/remote/webdriver.py in execute(self, driver_command, params)
    319         response = self.command_executor.execute(driver_command, params)
    320         if response:
--> 321             self.error_handler.check_response(response)
    322             response['value'] = self._unwrap_value(
    323                 response.get('value', None))

~/miniconda3/envs/AutoTest/lib/python3.9/site-packages/selenium/webdriver/remote/errorhandler.py in check_response(self, response)
    240                 alert_text = value['alert'].get('text')
    241             raise exception_class(message, screen, stacktrace, alert_text)
--> 242         raise exception_class(message, screen, stacktrace)
    243 
    244     def _value_or_default(self, obj, key, default):

JavascriptException: Message: ReferenceError: test_val is not defined

Implications:

Expected behavior

driver.execute_script("return test_val") should return the const test_val. It should not result in an exception.

Environment

OS: Debian Buster Browser: Firefox Browser version: 78.10.0esr (64-bit) Browser Driver version: GeckoDriver 0.29.0 Language Bindings version: Python 3.9.4, selenium 3.141.0 selenium-wire 4.2.4 Selenium Grid version (if applicable): -

AutomatedTester commented 3 years ago

I think that this issue is more to do with how the Firefox console and the page interact as I have tried to recreate this in the pure page or pure JS sense. see e9c738de8a587dc6bc86b55949c57b8574ec68bb

AutomatedTester commented 3 years ago

@whimboo do you know what could cause this issue?

picciama commented 3 years ago

I think that this issue is more to do with how the Firefox console and the page interact as I have tried to recreate this in the pure page or pure JS sense. see e9c738d

Does that mean you were able to reproduce it? I was able to reproduce it using your example. Maybe sth. specific related to Firefox on Debian? I'll come back to you after trying to reproduce it on a Windows machine.

AutomatedTester commented 3 years ago

I wasn't able to reproduce it... I wonder If it is because of an issue with that version of Firefox as it is really really old (release branch from Mozilla is on 88)

picciama commented 3 years ago

I can reproduce this with Windows 10 and Firefox 88.

I noticed though that we might have a misunderstanding here: While you created a global const cheese, you didn't call it as you created a local const within the scope of execute_script: https://github.com/SeleniumHQ/selenium/commit/e9c738de8a587dc6bc86b55949c57b8574ec68bb#diff-6d75ecf37273e7973aeac782de0ca2876d6cfaa7b36971f811b385cc7dcb7a61R288 This is working as expected but it doesn't access the global const.

Your second attempt in which you call the global function makeMeA is working too, as it is a globally defined function that's already in the document. If it isn't your website though, you annot just modify the served HTML of course an add this function.

What doesn't but should work is this: driver.execute_script("return cheese") which should return the global const cheese, I'm very sure you will be able to reproduce it.

I found a workaround though:

driver.execute_script("""
  let scr = document.createElement('script');
  scr.id='injected_function';
  scr.innerHTML='function makeMeA(sandwich){return cheese + sandwich}';
  document.head.appendChild(scr);
"""
)
driver.execute_script("return makeMeA('sandwich');")

This way, I can inject javascript by adding a child script to the document head. This is equivalent to your example in which this function is already defined in the HTML.

What remains though are the following questions:

  1. Why doesn't driver.execute_script("return cheese") work (and why does it in chromedriver)?
  2. Why doesn't it work if the above javascript isn't explicitly added to a new script element in the document (i.e. only defining it within the enclosing scope of execute_script?
  3. Why does this affect only consts but not var or function?
  4. Could it be that is sth. to do with the read only property of const?

As I see it, global consts cannot be accessed directly from within execute_script at the moment, even with the newest Firefox and geckodriver versions.

AutomatedTester commented 3 years ago

So after speaking to the Mozilla team there appears to be https://github.com/mozilla/geckodriver/issues/1067 that is raised for this issue. There are links to Bugzilla from there so there is not much we can do from the Selenium side.