Getting deprecation warnings when calling methods on elements - Could use some advice - ruby

I am getting deprecation warnings pretty frequently when calling methods on page object elements. This signals to me that I may not be using the gem as intended yet. I could use some help, could anyone recommend a better way to handle something like this?
I am working with a page containing a list of divs that each hold a checkbox element. In other words, something like this:
<div class="item-checkbox"><input type="checkbox"></div>
<div class="item-checkbox"><input type="checkbox"></div>
<div class="item-checkbox"><input type="checkbox"></div>
I would like to access a checkbox by index, and then check it when needed. Here is what I currently have:
def select_checkbox(index)
fail "Nothing in list" unless checkboxes.length > 0
checkbox = self.checkboxes[index].checkbox_element
if checkbox.exists?
checkbox.set
else
fail "could not select a checkbox at index #{index} - check that it exists"
end
end
protected
def checkboxes
div_elements(:class=> 'item-checkbox')
end
This works, however I get a deprecation warning on line 5, checkbox.set. Changing it to checkbox.click clears it up. Nevertheless, I am not convinced that I am doing this the "page-object gem" way. Using watir-webdriver, divs gives me an array to work with, and I can accomplish the same thing in a similar way. Has anyone done anything like this using the gem?

What you have above looks fine except the set method does not exist on the CheckBox element. Instead, there are check, uncheck, and checked? methods. I think you can safely change your above method to this:
def select_checkbox(index)
fail "Nothing in list" unless checkboxes
checkbox = self.checkboxes[index].checkbox_element
if checkbox.exists?
checkbox.check
else
fail "could not select a checkbox at index #{index} - check that it exists"
end
end
protected
def checkboxes
div_elements(:class=> 'item-checkbox')
end
Another way to possibly do this is to declare the divs in the class like this:
class MyPage
include PageObject
divs(:checkbox, :class => 'item-checkbox')
def select_checkbox(index)
fail "Nothing in list" unless checkboxes
checkbox_elements[index].checkbox_element.check
end
end
In this case you are still checking to see if there are any checkbox divs on the page. In the second line of the method you will fail if the checkbox does not exist so it takes care of the condition you guarded against.
-Cheezy

You need to use checkbox.check, not checkbox.set.
Here is a similar SO thread for reference:
How to avoid page-object deprecated for checkbox

Related

how to call clear method on the element object

In my project I have located the text_Field via some other element(via label), something like
element.following_sibling(tag_name: 'input').send_keys 'something'
But now it's typing into the text_field without any problem but I am missing clear method of text_field as it combines both clear and type together. Now I have to call clear method on element.following_sibling(tag_name: 'input') but since it's returning element object, I couldn't do that. Is there any way I can call clear method here? Or can I pass this object by some way to the text_field() method? Any help appreciated.
I know it can be called by converting the watir element into selenium element as given below
element.following_sibling(tag_name: 'input').wd.clear
But here I am missing WATIR waiting time for an element.
This appears to be a limitation in Watir's adjacent methods:
klass = if !plural && opt[:tag_name]
Watir.element_class_for(opt[:tag_name])
elsif !plural
HTMLElement
elsif opt[:tag_name]
Object.const_get("#{Watir.element_class_for(opt[:tag_name])}Collection")
else
HTMLElementCollection
end
Notice that only the :tag_name is used for determining the element's class. For input elements, we need to also consider the type attribute so that we can the right sub-class.
We should fix this in Watir (logged as Issue 878), but in the mean time, you can manually correct the class (ie Watir::TextField) using #to_subtype:
element.following_sibling(tag_name: 'input').to_subtype.clear
I got an answer to my question now.
Here is the answer.
b.text_field(element: element.following_sibling(tag_name: 'input').wd).set 'something'
If anybody has any better idea, please write your answer.Thanks.

capybara acceptance test - how to select the right element from inspecting the UI

In this crud test I create a log entry with #notes and will try to update the log by replacing #notes with #updated_notes.
#notes = Faker::Crypto.md5
#updated_notes = Faker::Crypto.sha256
This block of code to create the log entry works. I used within and the id's of divs in the source code with inspect.
it 'User can update manpower log entry' do
# create a new entry
within '#manpower_log_div' do
find('#manpower_log_notes').send_keys(#notes)
click_button "+ Add"
expect(page.has_css?('td', #notes)).to be true
end
Here I try to click the already existing notes on the page, which lets me edit them.
# click the already existing notes to be able to edit them
within '#manpower_log_div' do
find('#inline_edit').click
end
The error received is
Capybara::ElementNotFound:
Unable to find css "#inline_edit"
Inspecting the element gives us this, but notice the id of the object is too specific: data-object_id="11747753". What element can I place in find that I can use every time I run this test?
<span textarea_cols="50" class="inline_textarea_edit inline_editable" data-object_field="notes" data-object_id="11747753" data-object_class="ManpowerLog" data-custom_callback="" id="ManpowerLog-11747753-notes" data-value_required="false">a5c3e556f108fd29b00150ca736c82d6</span>
You can find the element by any valid CSS selector that would match it. In your example you could use class or data attribute - or a combination of both.
find('span.inline_textarea_edit[data-object_field="notes"]').click()
In your code find('#inline_edit') is looking for an element with id inline_edit. As Thomas Walpole mentioned you can find your button using css selector for example by class:
find('.inline_textarea_edit')
or
find('.inline_editable')
or
find('.inline_textarea_edit.inline_editable')
Make sure that class is uniq for that element. If not, you'll need to use something else then class or something else together with class, you need to look for uniq attribute of that element.
Also make sure that your element is within element with ID manpower_log_div as you are using within '#manpower_log_div'
You can find more info about css selectors here: http://www.w3schools.com/cssref/css_selectors.asp

Overwriting Capybara methods to add logging

I am using Capybara-Selenium in Ruby to run some automated tests and I want to add logging to the Capybara actions.
To do this I have overwritten the basic methods such as click_on and has_text?
These methods now look like this:
def has_text? text, options = {}
if page.has_text? text, options
#step.log :pass, "Looking for text \"#{text}\""
true
else
#step.log :fail, "Looking for text \"#{text}\""
false
end
end
def has_field? locator, options = {}
if page.has_field? locator, options
#step.log :pass, "Looking for field \"#{locator}\""
true
else
#step.log :fail, "Looking for field \"#{locator}\""
false
end
end
This works fine. However these methods are only useful when the web element being used has an ID. When there is no ID the element is found using the find method and then manipulated directly such as:
find(:css, 'some.locator').click
I want to add logging to these events as well. I could overwrite find and add generic logging, but that wont be very useful if I don't know what the element was used for after being found. I could add another parameter to my find method, but it would be clunky to write and read.
find(:css, 'some.locator', 'click').click
Before I succumb to that solution, I wanted to check if there was any easier or better implementation I could be using? I was thinking along the lines of some kind of sudo-class that replaced the object being returned by find, and had logging on all its methods, or even better if I could attach an event of some kind to various methods that would tell my logger when it needs to add something... I have no idea if these approaches are possible, and/or feasible.

What can I put as my watir-webdriver page "element" in a condition where it's not there?

I'm testing a nightmarish website that in most situations sticks all the important stuff in an iframe.
However, there are other common situations where the system will, annoyingly, open a page in a new tab, but not wrapped in the iframe.
I'm trying to figure out a conditional method that will check for the existence of the iframe and use it, otherwise not.
Here's what I've come up with, so far:
# The browser object...
#br = Watir::Browser.new
"frm" is the conditional method I'm trying to get working...
# Just an example element definition...
def click_my_button
#br.frm.button(id: "button").click
end
I define it in Watir's Container module, like so:
module Watir
module Container
def frm
if frame(id: "iframeportlet").exist?
frame(id: "iframeportlet")
else
# This is the part that I can't figure out.
end
end
end
end
That works fine when the iframe is there, but not surprisingly I get a NilClass error when it's not.
So, my question is: what can go into the else clause to make it work? More broadly, is there perhaps a better way to accomplish this? As you can imagine, I really want to avoid having to define every element in the web site twice.
I figured it out, and it's quite simple. The frm method's else clause just needs a "self"...
else
self
end
That's it. I'd love to know if there are any hidden pitfalls with this approach, though.

PageObject with Ruby - set text in a text field only works in the main file

I'm automating a site that has a page with a list of options selected by a radio button. When selecting one of the radios, a text field and a select list are presented.
I created a file (test_contracting.rb) that is the one through which I execute the test (ruby test_contracting.rb) and some other classes to represent my page.
On my class ContractPage, I have the following element declaration:
checkbox(:option_sub_domain, :id => "option_sub_domain")
text_field(:domain, :id => "domain_text")
select_list(:tld, :id => "domain_tld")
I've created in the ContractPage a method that sets the configuration of the domain like this:
def configure_domain(config={})
check_option_sub_domain
domain = config[:domain]
tld = config[:tld]
end
When I call the method configure_domain from the test_contracting.rb, it selects the radio button, but it doesn't fill the field with the values. The params are getting into the method correctly. I've checked it using "puts". Even if I change the params to a general string like "bla" it doesnt work. The annoying point is that if on test_contracting.rb I call the exact same components, it works.
my_page_instance = ContractPage.new(browser)
my_page_instance.domain = "bla"
my_page_instance.tld = ".com"
What I found to work was to in the configure_domain method, implement the following:
domain_element.value = config[:domain]
tld_element.send_keys config[:locaweb_domain]
Then it worked.
The documentation for the PageObjects module that I'm using as reference can be found here: http://rubydoc.info/github/cheezy/page-object/master/PageObject/Accessors#select_list-instance_method
Do you guys have any explation on why the method auto generated by the pageobject to set the value of the object didnt work in this scope/context ?
By the way, a friend tried the same thing with Java and it failed as well.
In ruby all equals methods (methods that end with the = sign) need to have a receiver. Let me show you some code that will demonstrate why. Here is the code that sets a local variable to a value:
domain = "blah"
and here is the code that calls the domain= method:
domain = "blah"
In order for ruby to know that you are calling a method instead of setting a local variable you need to add a receiver. Simply change your method above to this and it will work:
def configure_domain(config={})
check_option_sub_domain
self.domain = config[:domain]
self.tld = config[:tld]
end
I'm pretty new to this world of Selenium and page objects but maybe one of my very recent discoveries might help you.
I found that that assignment methods for the select_list fields only worked for me once I started using "self" in front. This is what I have used to access it within my page object code. e.g., self.my_select_list="my select list value"
Another note - The send_keys workaround you mention is clever and might do the trick for a number of uses, but in my case the select list values are variable and may have several options starting with the same letter.
I hope something in here is useful to you.
UPDATE (Jan 3/12)
On diving further into the actual Ruby code for the page object I discovered that the select_list set is also using send_keys, so in actuality I still have the same limitation here as the one I noted using the send_keys workaround directly. sigh So much to learn, so little time!

Resources