Reputation: 33
Given the following HTML:
<html>
Log in:
<iframe id="secure_iframe">
<input id="user_login" type="text">
<input id="user_password" type="password">
<input id="login_dialog_submit" type="submit" name="commit" class="form-button">
</iframe>
</html>
And the following page class code, using the page-object gem:
class LoginPage
include PageObject
in_iframe(id: 'secure_iframe') do |login_iframe|
text_field(:email_field, id: 'user_login', frame: login_iframe)
text_field(:password_field, id: 'user_password', frame: login_iframe)
button(:login, id: 'login_dialog_submit', frame: login_iframe)
end
def login_as(account_object)
wait_until { self.email_field_element.visible? } # ERROR happens here
self.email_field = account_object.email
self.password_field = account_object.password
login
end
end
I get the following error:
Selenium::WebDriver::Error::StaleElementReferenceError: Element belongs to a different frame than the current one - switch to its containing frame to use it
Anyone else having the same issue? Thanks in advance for any and all help.
Upvotes: 2
Views: 1705
Reputation: 338
It seems you want to make sure the email_field is shown before filling in the text_fields?
If so, an approach I use in my PageObjects is to use the initialize_page
method:
class LoginPage
include PageObject
def initialize_page
email_field_element.when_present
password_field_element.when_present
end
in_iframe(id: 'secure_iframe') do |login_iframe|
text_field(:email_field, id: 'user_login', frame: login_iframe)
text_field(:password_field, id: 'user_password', frame: login_iframe)
button(:login, id: 'login_dialog_submit', frame: login_iframe)
end
def login_as(account_object)
self.email_field = account_object.email
self.password_field = account_object.password
login
# optionally, you can return another PageObject here:
PageOnceLoggedIn.new(@browser)
end
end
The initialize_page method gets called when you use the visit(LoginPage)
or on(LoginPage)
from the PageObject::PageFactory
module (thus this module needs to be included!)
Additional information
Upvotes: 0
Reputation: 46836
The exception is an issue with how the page object handles elements in a frame or iframe. Basically, when using the "[name]_element" method, the page object is not switching to the frame before interacting with the element. This is a bug that exists as Issue 224. The problem only occurs when using Selenium-Webdriver as the platform (ie does not occur with Watir-Webdriver).
In this case, since you are just checking if the element is visible, you can avoid using the "[name]_element" method (and therefore the exception) by using the "[name]?" method instead.
def login_as(account_object)
wait_until { self.email_field? }
self.email_field = account_object.email
self.password_field = account_object.password
login
end
Upvotes: 1
Reputation: 33
After spending time reading all the documentation I could get my eyes on, I figured out how to get the code to wait for an element inside an iframe:
With the login_as method looking like:
def login_as(account_type)
show_login_dialog
in_iframe(id: 'secure_login_iframe') do |iframe|
wait_until { text_field_element(id: 'user_login', frame: iframe) }
end
self.email_field = account_object.email
self.password_field = account_object.password
self.login
end
Note that the wait_until method is inside the in_iframe method, which passes the iframe into the text_field_element method as the last argument.
I don't like this solution because it breaks DRY - I'm defining the user_login
field twice (and potentially more than twice) - once at the top of the page class and another time, dynamically, in the login_as method.
Upvotes: 1