I have some E2E tests for a cross platform Ionic application. The tests are written in Ruby and they use appium_capybara, capybara and selenium-webdriver.
The Ionic application has an async javascript function that can be used from the developer console to sign in to the app. I want to invoke this from the tests and I managed to do this from Ruby with a function called evaluate_async_script.
However, while I have proof that the async function is actually executed, i get this error from Ruby:
Timed out waiting for asyncrhonous script result after 10033 ms (Selenium::WebDriver::Error::ScriptTimeoutError)
Why does this happen? Any suggestions on how to make it work?
The async function looks something like this:
export async function signin(user: string, pass: string,
callback: (result: object) => object):
Promise<object> {
if (typeof(window.authService) === 'object') {
const credentials: ICredentials = {
email: user,
password: pass,
rememberMe: false
};
await window.authService.authenticate(credentials);
const result = { message: 'YES!!' };
callback(result);
return result;
}
return { message: 'EMPTY STRING!!' };
}
The call from Ruby looks like this:
result = Capybara.current_session
.evaluate_async_script("window.signin('#{user.email}',
'#{user.password}', (result) => { return result; })
.then(function(value) { alert(value.message); })")
The alert pops up showing the 'YES!!' message, so I know that the function is executed. But for some reason Ruby never notices that the function has finished.
From the docs for Session#evaluate_async_script
- https://www.rubydoc.info/gems/capybara/Capybara/Session#evaluate_async_script-instance_method - the important part is "from a callback function which will be passed as the last argument to the script". Your script isn't calling the callback function which would be available as arguments[0]
so Capybara has no way of knowing it is done.
For it to work you would need something along the lines of (untested)
result = Capybara.current_session.evaluate_async_script("
var cb = arguments[0];
window.signin('#{user.email}','#{user.password}', cb);")
You could also pass the email and password in as arguments too if desired
result = Capybara.current_session.evaluate_async_script('
var cb = arguments[2];
window.signin(arguments[0], arguments[1], cb);', user.email, user.password)
Note this moves the callback function to arguments[2]
, since you're passing 2 other arguments first.
The bigger question is whether or not you even need to be using evaluate_async_script
. It's only needed if you actually need the response from the async function, if not you can just call it with execute_script
and wait for the visible page changes as normal.