Using Selenium WebDriver 4.0 BiDirectional API
Phil
Posted on January 16, 2023
I had mentioned in an earlier post that checking on throbbers is critical to ensuring that tests are stepping forward the right way, which avoids flaky tests and headaches.
I ran into a recent case where I was not able to depend on a throbber, newly enabled element, or any other changes in the DOM that would let me know that it was safe to proceed in our test. I did open up a new Jira ticket to address this. After all, if a machine/bot/whatever is able to produce a bug from moving "too fast", then perhaps there are some UX updates that should be made. Ultimately I did expose a race condition that is reproduced under very poor latency conditions.
I decided to look into Selenium's BiDi API. For one, it looks like testing stacks involving Cypress and Playwright are already trying to incorporate more support at the devtools level. So it's good to see that the dinosaur that is Selenium is keeping pace with those newer Node stacks.
I'll be speaking specifically about the Network Interception functionality. To me, that is the one with the most utility in the context of "throbbers and waiters".
From what I understood, it looks as though the BiDi API is designed in such a way that an async function / block is called. From there, any WebDriver actions allow for that function / block to intercept network requests and at a minimum, allow for the developer to output request URLs, responses, response statuses, and even potentially manipulate incoming responses for the browser to act upon. Super cool!
In the end, I wrote a helper method to ultimately do what I needed it to do: Do a thing in the UI, make sure that the frontend is sending a specific network request, and making sure that the browser is getting a 200 back. So without further ado:
def wait_on_request
@request_found = false
page.driver.browser.intercept do |request, &continue|
if request.method == 'GET' && request.url.match?(/tables/)
puts 'Checking for OK response code for %s' % request.url
continue.call(request) do |response|
response.code.should == 200
@request_found = true
puts 'OK response code found'
end
else
continue.call(request)
end
end
yield
attempts = 0
while !@request_found
puts 'Waiting for request to be sent by frontend'
sleep 3
attempts += 1
fail 'Request was not sent by the frontend within 30 seconds' if attempts == 10
end
end
In this block of code, our app under test uses various REST endpoints that include the path /table
and is hard coded here to account for that request. We are using the yield
keyword that allows passing of another block that is expected to trigger the request. The while
loop is triggered to "monitor" whether the request was actually sent. Tests are designed to fail if the request is never sent by the frontend, or if the request returns anything other than a 200.
So there you have it. It is very, very cool seeing the advancements in WebDriver over just the past year especially since the release of Selenium 4.0. Historically, they were always such a stickler to "we only support things that a user in a browser would do", but are now bending more towards "we support things that a developer in a browser would do". I'm excited to to see what's in store for future releases.
Something I found last minute: Unfortunately this is not supported by AWS Device Farm and it's not clear when it will ever be. This means that this is only supported locally, or for any remote/grid servers that have built-in support for it. I happen to run into the error DevTools is not supported by the Remote Server
. If anyone has any solutions or workarounds for this, I'm all ears!
Posted on January 16, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.