Pageobject with Ruby - Set Text in a Text Field Only Works in the Main File

Passing value to one Page Object from Another Page Object

The current approach will determine the coverage when the class is compiled. I think you want to determine the value when the object is initialized.

I would initialize a SectionA instance within SectionB:

class SectionB
include PageObject

def coverage
SectionA.new(root.browser).coverage_value
end

def element_method
element('element', id: coverage)
end
end

Cheezy's Page Object, get all text_fields defined inside Page

The names (:first_name, :last_name, :birth_date) are only used to generate the method names such as first_name=, last_name= and birth_date=. The name is not stored or retained for later use.

That said, you could iterate through the page's instance methods to find the text fields. The following text_fields method will:

  1. Get all of the class instance methods.
  2. Find the methods that end with "_element".
  3. Create an array that includes the element names and element.

The page object would be:

class SomePage
include PageObject
text_field :first_name, :name => "fname"
text_field :last_name, :name => "lname"
text_field :birth_date, :name => "birthday"
button :submit, :type => "submit"

def text_fields
self.class.instance_methods(false)
.grep(/_element$/)
.map { |m|
element = self.send(m)
[m[/(.+)_element$/, 1].to_sym, element] if element.kind_of?(PageObject::Elements::TextField)
}.compact
end
end

You could then iterate through the text fields with access to their name (or attribute) and the TextField element:

page = SomePage.new(browser)
page.text_fields.each do |attribute, text_field|
text_field.value = @user[attribute]
end

SyntaxError on pageobject while accessing a select_list within fieldset

Solution

The accessor you want for the select list is:

select_list(:payer_insurance){ element(:fieldset, :class => "dartPayer-Insurance").select_list_element(:id => "dartPayer-Payer") }

Problem

You were getting the syntax error due to the following part:

element(:class => "dartPayer-Insurance")

In the API docs for element, you can see that method definition is:

(Object) element(tag, identifier = {:index => 0})

Finds an element

Parameters:
the (Symbol) — name of the tag for the element
identifier (Hash) (defaults to: {:index => 0}) — how we find an element. You can use a multiple paramaters by combining of any of the following except xpath

The original code was missing the tag parameter, which caused the exception.

Note that the select list id was also incorrect - using dartPayer-PayerList instead of dartPayer-Payer.

how to use text_field_value_set method in pageobject

Get text_field_value_set to work

The error occurs because of the way parameters are being sent to the text_field_value_set method.

The method expects two parameters - text_field_value_set(identifier, value). Notice that you are sending trying to send a hash to the first parameter (ie identifier) and a string to the second parameter (value). It is not clear to Ruby where the hash ends. Skorks has a nice article about the niceness and problems with using hashes as method arguments.

Ultimately it comes down to, if the hash is not the last argument, you need to use an explicit hash - eg putting curly braces around the inputs. This would look like:

text_field_value_set({name: 'keyword'}, "#{job}")

This will address the exception you are seeing, however you will get another exception about the text_field_value_set method not existing. It is a method of the Platform class, which your page is not. However, your page can access the platform with a platform method.

Putting it all together, the method will work as:

def search(job)
search_job
wait_element.when_not_visible
platform.text_field_value_set({name: 'keyword'}, "#{job}")
add_element.click
end

Better solution

That said, I do not think using text_field_value_set is the right approach. This is really more of an internal method that is used by the text field accessors. As well, it defeats the purpose of having the page object accessor methods.

Given that you had a keyword_element.send_keys, I assume you have already defined an accessor for the text field:

text_field(:keyword, :name => 'keyword')

This would have created a method for your page object that allows setting of the text field with the following. This method calls the text_field_value_set method, which ultimately calls the desired Watir set method.

keyword=(value)

Your method could be written as:

def search(job)
search_job
wait_element.when_not_visible
self.keyword = job
add_element.click
end

Note that because the keyword= method is being used within the class, we need to use self so that it is clear we want to make a method call rather than create a local variable.

Getting text all all links on a page.

You could have the menu_options method:

  1. Get all of the links instead of the list items.
  2. Map the text of each element directly rather than mapping them to a MenuOption.

The method would be:

def menu_options
@element.right_click
options = @page.all('div.dropdown-menu li a')
options.map{ |option| option.text }
end

How to extend page-object to include non-standard HTML Elements

I'm not an Angular person, so I stole a clickable mat-chip example from https://stackoverflow.com/a/47962183/1200545. Depending on your implementation, you may need to tweak the code below.

First off, Page-Object has a limitation when defining widgets that use a custom tag name - ie "mat-chip". To get around this, we will define an associated method/locator in Watir:

module Watir
module Container
def mat_chip(*args)
HTMLElement.new(self, extract_selector(args).merge(tag_name: "mat-chip"))
end
def mat_chips(*args)
HTMLElementCollection.new(self, extract_selector(args).merge(tag_name: "mat-chip"))
end
end
end

We can then create a widget and accessors in Page-Object:

class MatChip < PageObject::Elements::Element
def self.accessor_methods(widget, name)
widget.send('define_method', "check_#{name}") do
self.send("#{name}_element").check
end

widget.send('define_method', "uncheck_#{name}") do
self.send("#{name}_element").uncheck
end

widget.send('define_method', "#{name}_checked?") do
self.send("#{name}_element").checked?
end
end

def set(bool = true)
click unless set? == bool
end
alias check set

def clear
set(false)
end
alias uncheck clear

def set?
attribute_value('aria-selected') == 'true'
end
alias checked? set?

PageObject.register_widget :mat_chip, self, :mat_chip
end

You can then use the accessor, named "mat_chip", the same was as a checkbox:

class MyPage
include PageObject

mat_chip('mychip', index: 1)
end

page = MyPage.new(browser)
page.check_mychip
p page.mychip_checked?
#=> true
page.uncheck_mychip
p page.mychip_checked?
#=> false

Alternatively, you can use the mat-chip directly as a nested element:

page.mat_chip_element.check
p page.mat_chip_element.checked?
#=> true
page.mat_chip_element.uncheck
p page.mat_chip_element.checked?
#=> false

PageObject wait element method don't work

I think Sheg is giving some good advice here although I would tend to do it slightly different:

wait_until do
billing_element.visible?
end

Another thing you can do that is very similar is to replace the code in your method with this:

def go_to_billing
billing_element.when_present.click
JobMyroomBilling.new @browser
end

In this case we are waiting until the link is present and when it is the when_present method returns the element and we simply click on it.

Another thing I might suggest is using watir-webdriver as the driver instead of selenium-webdriver. watir-webdriver is built on top of selenium-webdriver but seems the have far better handling of waiting for items to actually be loaded on the DOM. If your link is being added dynamically using Ajax then you will have to write some code to wait until it is there before actually interacting with it. The only thing you will have to do to use the other gem is change the code in your Before block to this:

Before do
@browser = Watir::Browser.new :firefox
end

Another pointer I would give you is to not have one page object return an instance of the next page object. I am eating my own words here because a few years ago this is how I did it but I have found over time that this is a brittle approach. Instead, I would use the PageObject::PageFactory to manage the creation of the correct pages and when you have a step that is a generic step you can simply use the @current_page instance variable.

The last pointer I will give you is to remove the assertions from your page object. I would have the page object simply provide the abstraction and perform all verification in the step definitions. This is a much cleaner separation of concerns and will make maintaining your tests easier over time.



Related Topics



Leave a reply



Submit