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

considering this simple Page Object:
require 'watir-webdriver'
require 'page-object'
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"
end
browser = Watir::Browser.new
page = SomePage.new(browser)
is there a way to iterate over all the text fields (or any elements) to access their "identifier" (i.e. :username, :password or :birth)?
something like:
page.text_fields.each do |text_field|
puts text_field.identifier.inspect
end
=> :first_name
=> :last_name
=> :birth_date
I'm just looking to see if I could turn this:
page.first_name = #user.first_name
page.last_name = #user.last_name
etc...
into this:
page.text_fields.each do |text_field|
attribute = text_field.attribute
text_field = #user[attribute]
end
Anybody knows what I mean?

The answers already provided definitely will suite your needs, but I would like to add one additional option that uses a feature built into the page-object gem.
It's call populate_page_with() in page_populator.rb. It's used like this so.
Start with a hash that has the element names(I use a CSV file that is loaded into a hash), as defined on your page object, in the keys. The value of the hash contains the value you wish to populate each element with.
form_data = {first_name: #user.first_name, last_name: #user.last_name, birth_date: #user.birth_date}
#page.populate_page_with form_data
That's it. The populate_page method will find the right elements on the page and populate them with whatever value you have set in your source hash. This works for checkbox, radio buttons and text.
This is a very nice time saving method that Cheezy put in for us!
Thanks Cheezy!

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:
Get all of the class instance methods.
Find the methods that end with "_element".
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

I do exactly the "opposite" :
#user.each do | key, value |
unless value.empty?
browser.text_field(label: key).set value
end
end
I make the job done for the datas I have, and not the fields. It allows to test form fill with only some fields.

Related

Parsexml into ruby objects and save

I'm using happy mapper for object mapping in ruby , then parse the xml data in the .xml files i obtain from the api.
I get a zip file in the api response and extract it to get 5-6 files with same xml format of data.
The data in each file is around 2-3 mb.
I want to save this data in files keeping in mind that i should be able to perform search operations over it.
I don't want to use relational db rather would be looking to save the data in files.
What should be the better approach to save the data which will be efficient enough for the later search operations to be performed on that data.
require 'json'
require 'happymapper'
file_contents = File.read('/home/GhostRider/x.xml')
class Message
include HappyMapper
tag 'Message'
element :color, String, :tag => 'Colour'
element :bg_color, String, :tag => 'BgColour'
end
class Status
include HappyMapper
tag 'Status'
element :text, String, :tag => 'Text'
element :color, String, :tag => 'Colour'
element :bg_color, String, :tag => 'BgColour'
has_one :message, Message
end
class Line
include HappyMapper
tag 'Line' # if you put class in module you need tag
element :name, String, :tag => 'Name'
element :color, String, :tag => 'Colour'
element :bg_color, String, :tag => 'BgColour'
element :url, String, :tag => 'Url'
has_one :status, Status
end
class Lines
include HappyMapper
tag 'Lines' # if you put class in module you need tag
has_many :lines, Line
end
item = Lines.parse(file_contents, :single => true)
item.lines.each do |i|
puts i.name, i.color, i.url, i.status.text, i.status.message.color
end
I need to save this data obtained.
The better approach is to parse the xml using xml selectors like nokogiri or xml simple or default Hash.to_xml(xml file) from rails. Then define the ruby classes separately which helps in keeping better control over the classes and perform the operations.

How do you pass method params in Ruby which can be used as the name of existing methods?

Background: I'm using a DSL for automated UI testing in Ruby called Watir-Webdriver.
I want to write a very re-usable method that passes or fails when a specific HTML element is present. Here is what I have so far:
require 'watir-webdriver'
require 'rspec'
b = Watir::Browser.new
def display_check(element_type,unique_element,expectation)
if expectation == "yes"
b.send(element_type).((:id or :class or :name or :value),/#{Regexp.escape(unique_element)}/).exists?.should == true
else
b.send(element_type).((:id or :class or :name or :value),/#{Regexp.escape(unique_element)}/).exists?.should == false
end
end
I can understand that "div" in this example is a string passed as a method argument. But in the context of the dsl, "div" (minus the quotes) is also a Watir-webdriver method. So I guess I need to somehow convert the string to an eligible watir-webdriver method
I basically want to do the following to determine if an element exists.
display_check("div","captcha","no")
Since I'll be looking for select_lists, divs, radio buttons etc, it would be very useful to specify the element type as an option instead of having it hard coded to the method.
When you use send, the first parameter is the method name and the following parameters are the parameters to pass to the method. See doc.
So your b.send should be more like:
b.send(element_type, :id, /#{Regexp.escape(unique_element)}/).exists?
To find an element where one of the attributes (id, class, etc) is a certain value, you can try the following. Basically it iterates through each of the attributes until an element is found.
def display_check(b, element_type, unique_element, expectation)
element_exists = false
[:id, :class, :name, :value].each do |attribute|
if b.send(element_type, attribute, /#{Regexp.escape(unique_element)}/).exists?
element_exists = true
break
end
end
if expectation == "yes"
element_exists.should == true
else
element_exists.should == false
end
end

How to show model title instead on #<Mymodel:0x000000...> in activeadmin dropdown menus?

I've created an association where Project has_many Tasks and Task belongs_to Project.
I've created the form in admin/tasks.rb
form do |f|
f.inputs "Details" do
f.input :title
f.input :project
end
f.buttons
end
Now in the edit Task page I hahe a dropdown menu where I can choose the project, but the entry are #<Project:0x00000...>.
How can I customize the entries in the dropdown to show the Project title field instead?
I'm a Rails newbie.
Active admin makes use of formtastic, under the hood formtastic loops through your model searching for a method like name, to_s, value, title, that returns a string.
At the moment you see the data entry itself, if you want formtastic to show the name, make sure you put something like
def name
return self.what_key_you_want_to_use
end
in your Project.rb model.
That should let formtastic show the name action instead of the model .to_s!
This solved it for me:-
In project.rb (Model) to make ActiveAdmin display properly in select dropdown use alias_attribute.
alias_attribute :name, :project_name (or whatever you named the field in your database)
tldr: You can define or alias :to_label on your model to customize the label used:
def to_label
"#{name} - (#{id})"
end
alias_attribute :to_label, :name
The library used by Rails: Formtastic, (or an alternative: Simple Form) uses the collection_label_methods to configure which fields are checked for deriving a label for your model.
Formastic defaults are: "to_label", "display_name", "full_name", "name", "title", "username", "login", "value", "to_s"
Simple Form defaults are: :to_label, :name, :title, :to_s
As most of these fields might already be used in your model, to_label or display_name seems to be the good candidates. I prefer to_label.
You can create a proc like such :
f.input :your_field, member_label: Proc.new { |p| "#{p.name}"}

How do you handle serialized edit fields in an Active Admin resource?

I have a model, Domain, which has a text field, names.
> rails g model Domain names:text
invoke active_record
create db/migrate/20111117233221_create_domains.rb
create app/models/domain.rb
> rake db:migrate
== CreateDomains: migrating ==================================================
-- create_table(:domains)
-> 0.0015s
== CreateDomains: migrated (0.0066s) =========================================
I set this field as serialized into an array in the model.
# app/models/domain.rb
class Domain < ActiveRecord::Base
serialize :names, Array
end
Create the ActiveAdmin resource for this model
> rails g active_admin:resource Domain
create app/admin/domains.rb
then, in the app/admin/domains.rb, I setup the various blocks to handle the serialized field as such
# app/admin/domains.rb
ActiveAdmin.register Domain do
index do
id_column
column :names do |domain|
"#{domain.names.join( ", " ) unless domain.names.nil?}"
end
default_actions
end
show do |domain|
attributes_table do
row :names do
"#{domain.names.join( ", " ) unless domain.names.nil?}"
end
end
end
form do |f|
f.inputs "Domain" do
f.input :names
end
f.buttons
end
# before we save, take the param[:domain][:name] parameter,
# split and save it to our array
before_save do |domain|
domain.names = params[:domain][:names].split(",") unless params[:domain].nil? or params[:domain][:names].nil?
end
end
Nearly everything works great -- my names are displayed as comma separated in the index and show views. When I update a record with my names field set to "a,b,c", the before_save works to turn that into an array that is then saved via the ActiveRecord serialize.
What I can not solve is how to make the edit form put in a comma-separated list into the text field. I tried using a partial and using formtastic syntax directly as well as trying to make it work via the active_admin DLS syntax. Does anyone know how to make this work?
Specifically, if I have the following array saved in my domain.names field:
# array of names saved in the domain active_record
domain.names = ["a", "b", "c"]
how to change:
form do |f|
f.inputs "Domain" do
f.input :names
end
f.buttons
end
so that when the edit form is loaded, in the text field instead of seeing abc, you see a,b,c.
Here is a summary of how I handled this situation. I added an accessor to the model which can turn the Array into a string joined by a linefeed and split it back to an Array.
# app/models/domain.rb
class Domain < ActiveRecord::Base
serialize :names, Array
attr_accessor :names_raw
def names_raw
self.names.join("\n") unless self.names.nil?
end
def names_raw=(values)
self.names = []
self.names=values.split("\n")
end
end
then, in my admin resource for domain, instead of using the :names field, I used the :names_raw field. setting this value would save the names Array with the new values.
# app/admin/domains.rb
form do |f|
f.inputs "Domain" do
f.input :names_raw, :as => :text
end
f.buttons
end
Stumbled on this question looking for something to have access to a serialized Hash's YAML. I used this solution on Rails 3.2:
def target_raw
#attributes['target'].serialized_value
end
def target_raw=(new_value)
#attributes['target'].state = :serialized
#attributes['target'].value = new_value
end

How to use ActiveRecord callbacks to assign field values before save?

I'm wondering how I can use callbacks to assign values to the database fields, which are processed out of a virtual attribute field.Example:
field :houseno, :type => String
field :street, :type => String
attr_accessor :address
My attempt at this seems to be unsuccessful. Here is what I have:
before_validation :assign_fields
def assign_fields
if #address
#houseno = #address.match(/^(\d+-?(\d+)?)\W*(.*)/)[1]
#street = #address.match(/^(\d+-?(\d+)?)\W*(.*)/)[3]
end
end
And I keep getting this error:
undefined method `houseno' for Building:0x0000010488f108
Have you tried:
write_attribute(:houseno) = #address.match(/^(\d+-?(\d+)?)\W*(.*)/)[1]
or
self.houseno = #address.match(/^(\d+-?(\d+)?)\W*(.*)/)[1]

Resources