I've been using selenium
(with python bindings and through protractor
mostly) for a rather long time and every time I needed to execute a javascript code, I've used execute_script()
method. For example, for scrolling the page (python):
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
Or, for infinite scrolling inside an another element (protractor):
var div = element(by.css('div.table-scroll')); var lastRow = element(by.css('table#myid tr:last-of-type')); browser.executeScript("return arguments[0].offsetTop;", lastRow.getWebElement()).then(function (offset) { browser.executeScript('arguments[0].scrollTop = arguments[1];', div.getWebElement(), offset).then(function() { // assertions }); });
Or, for getting a dictionary of all element attributes (python):
driver.execute_script('var items = {}; for (index = 0; index < arguments[0].attributes.length; ++index) { items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value }; return items;', element)
But, WebDriver API also has execute_async_script()
which I haven't personally used.
What use cases does it cover? When should I use execute_async_script()
instead of the regular execute_script()
?
The question is selenium-specific, but language-agnostic.
For an executeAsyncScript method, JavaScript Executor runs an asynchronous part of JavaScript with the reference to the present selected window or frame. In contrast to executeScript, the scripts which run with executeAsyncScript method, should be completed by invoking the given callback.
What is JavascriptExecutor in Selenium? In simple words, JavascriptExecutor is an interface that is used to execute JavaScript with Selenium. To simplify the usage of JavascriptExecutor in Selenium, think of it as a medium that enables the WebDriver to interact with HTML elements within the browser.
executeScript() Injects a script into a target context. The script is run at document_idle by default. Note: This method is available in Manifest V3 or higher in Chrome and Firefox 101.
Yes, you can do that. In general, JavascriptExecutor accept element as argument not locator. so no matter you use xpath or css selector to locate the element.
When should I use
execute_async_script()
instead of the regularexecute_script()
?
When it comes to checking conditions on the browser side, all checks you can perform with execute_async_script
can be performed with execute_script
. Even if what you are checking is asynchronous. I know because once upon a time there was a bug with execute_async_script
that made my tests fail if the script returned results too quickly. As far as I can tell, the bug is gone now so I've been using execute_async_script
but for months beforehand, I used execute_script
for tasks where execute_async_script
would have been more natural. For instance, performing a check that requires loading a module with RequireJS to perform the check:
driver.execute_script(""" // Reset in case it's been used already. window.__selenium_test_check = undefined; require(["foo"], function (foo) { window.__selenium_test_check = foo.computeSomething(); }); """) result = driver.wait(lambda driver: driver.execute_script("return window.__selenium_test_check;"))
The require
call is asynchronous. The problem with this though, besides leaking a variable into the global space, is that it multiplies the network requests. Each execute_script
call is a network request. The wait
method works by polling: it runs the test until the returned value is true. This means one network request per check that wait
performs (in the code above).
When you test locally it is not a big deal. If you have to go through the network because you are having the browsers provisioned by a service like Sauce Labs (which I use, so I'm talking from experience), each network request slows down your test suite. So using execute_async_script
not only allows writing a test that looks more natural (call a callback, as we normally do with asynchronous code, rather than leak into the global space) but it also helps the performance of your tests.
result = driver.execute_async_script(""" var done = arguments[0]; require(["foo"], function (foo) { done(foo.computeSomething()); }); """)
The way I see it now is that if a test is going to hook into asynchronous code on the browser side to get a result, I use execute_async_script
. If it is going to do something for which there is no asynchronous method available, I use execute_script
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With