Unable to click on checkbox. Using page-object-gem - ruby

I am having trouble clicking on a checkbox using the page-object-gem. The html code looks like this:
<label class='cc pointer value-apple'>
<input class='hidden' type='checkbox' value='apple'></input>
<div style="background-image:url(/images/fruit-apple-3453452346.jpg)"><div>
</label>
<label class='cc pointer value-banana'>
<input class='hidden' type='checkbox' value='banana'></input>
<div style="background-image:url(/images/fruit-banana-2359235674.jpg)"><div>
</label>
Using watir-webdriver I have no issues clicking on the label or div since the checkbox is hidden. Those work fine. However this does not seem to work using the page-object-gem. I have tried the following:
label(:select_fruit_apple, :class => /apple/)
on(FruitsPage).select_fruit_apple
div(:select_fruit_apple, :style => /apple/)
on(FruitsPage).select_fruit_apple
Any suggestions on how to do this is much appreciated.

Since Watir-Webdriver requires you to click the label or div, you will have to do the same with the page object.
The label and div accessor methods do not create methods to click the element. For the code you tried, when you call select_fruit_apple, it is just returning the text of the label or div element.
To click the label (or div), you will need to:
Get the label/div element. This can be done by either defining a method that uses a NestedElement or creating the label/div accessor and using the _element method.
Call the click method for the element. This will call Watir-Webdriver's click method.
Here is what the class would look like if you were using NestedElement directly:
class MyPage
include PageObject
def select_fruit_apple()
label_element(:class => 'value-apple').click
end
end
Alternatively, if you need the label/div element for multiple things, you might want to create an accessor for them:
class MyPage
include PageObject
label(:apple, :class => 'value-apple')
def select_fruit_apple()
apple_element.click
end
end
You can then call this method to set the checkbox:
on(FruitsPage).select_fruit_apple
Of course, if you want to save having to define a method for each fruit, you could use a method that takes a parameter:
class MyPage
include PageObject
def select_fruit(fruit)
label_element(:class => 'value-' + fruit).click
end
end
Then the method call would be:
on(FruitsPage).select_fruit('apple')

Related

Calling a Ruby Method via a html button in sinatra

I'm trying to build an e-commerce site using Sinatra, as practice. I'm getting stumped on how to implement the 'Add to Cart' Button. My thought process about it is:
User clicks 'add to cart'
The button 'add to cart' invokes a ruby method, for example clicking on the following button
<input class='btn btn-primary' type='button' value='Add To Cart'></input>
should call a ruby method like
shop.add_to_cart(product, quantity)
An example of what this method might looking like:
class Shop
attr_reader :cart
def initialize
#cart = []
end
def add_to_cart(product, quantity)
#cart << product, quantity
end
end
In Rails, I think we use the helper_method in the controller? Is there anything similar I can do in Sinatra?
Thanks!
Note:
This is if you want to do it in ruby. You could probably also do it in javascript as mentioned in the other answer, but I cannot help you with that because I don't know javascript well enough.
To run the ruby method on button click you first need to create a <form> with only the button, then have that run a route in your app file that will run the method then redirect back to the page you were on. Here is my code (have not tested):
home.erb:
<form method="post" action="/runMethod">
<input type="hidden" name="product" value="whatever">
<input type="hidden" name="quantity" value="whatever">
<input class='btn btn-primary' type='submit' value='Add To Cart'>
</form>
You would set the values of the two hidden inputs (where I wrote "whatever") to the quantity and product according to their names.
App File:
class Shop
attr_reader :cart
def initialize
#cart = []
end
def add_to_cart(product, quantity)
#cart << product, quantity
end
end
get '/' do
erb :home
end
post '/runMethod' do
shop.add_to_cart(params[:product], params[:quantity])
redirect '/'
end
This can also be accomplished with ajax so that you dont have to leave the page:
$("#hideCarousel").submit(function() {
//posts the contents of the form to /action using ajax
$.post("/action", $("#myform").serialize(), function(result){
// assuming result is a string of the updated data in html
// and assuming that your data goes in an element with the id data-table
$("#data-table").html(result)
});
return false; // prevents the form from submitting normally
});
Rails/Sinatra run on the server side. If you want stuff happening in Rails directly you probably need a form and post back data.
nowadays people use javascript and it's javascript that makes the callbacks in an asynchronous fashion for this kinds of things.

Accessing a nested element 3 levels deep using page objects

Using the Page Object model and gem I want to access an element that is nested 3 layers deep. I've successfully accessed nested elements that are 2 elements deep but for 3 the same method isn't working.
3 elements defined in my Page Object:
div(:serv_info, :class => "service-info")
div(:validate_method, :class => "validate-method")
div(:scar_input_group, :class => "input-group")
So I tried to chain those 3 elements to access the div class input-container input-left-half round like this:
div(:scar_first_name_error){validate_method_element.serv_info_element.scar_input_group_element.div_element(:class => "input-container input-left-half round")}
But I got the error that serv_info_element is an undefined method, which makes sense, but is it possible to chain the 3 predefined elements I stated above to access the input-container input-left-half round?
I read this: https://github.com/cheezy/page-object/wiki/Nested-Elements but didn't want to have to repeat any code if I can help it.
Assuming that the nesting is always the same, rather than having the :scar_first_name_error map through each ancestor, you could define each element with respect to its parent (or ancestor).
Let us assume the HTML is:
<html>
<body>
<div class="validate-method">
<div class="service-info">
<div class="input-group">
<div class="input-container input-left-half round">
text
</div>
</div>
</div>
</div>
</body>
</html>
You could define the page as:
class MyPage
include PageObject
div(:serv_info) { validate_method_element.div_element(:class => "service-info") }
div(:validate_method, :class => "validate-method")
div(:scar_input_group) { serv_info_element.div_element(:class => "input-group") }
div(:scar_first_name_error) { scar_input_group_element.div_element(:class => "input-container input-left-half round") }
end
Notice that the :serv_info is defined with respect to its parent :validate_method, :scar_input_group is defined with respect ot its parent :serv_info, etc.
With this page object, we can see we can get the lower element's text:
page = MyPage.new(browser)
p page.scar_first_name_error
#=> "text"

How to search for a Page Object element inside another element in ruby

I have a form that contains 3 div for addresses (Main Address, Secondary Address, Add Address).
Each div contains the following elements (text_field): Name, Address, Phone.
Depending on the scenario, I want to edit a text field for a specific div.
So I have this scenario:
Given I am at the form for editing addresses
When the div "main address" is visible
Then fill the "name" element for the "main address" div
And erase the "address" for the "main address" div
And the following steps definitions:
Then(/^fill the "([^"]+)" element for the "([^"]+)" div$/) do |element, div|
on_page(AddressPage).fill_element element div
end
And now the part I'm not sure about - the Address Page
class AddressPage
include PageObject
include DataMagic
div(:main_address, :id => 'main_address')
div(:secondary_address, :id => 'secondary_address')
div(:add_address, :id => 'add_address')
def fill_element(element, div)
current_div = self.send("#{div}_element")
# now the part I don't know how to do:
current_element = div.search_within(current_div, element)
fill_with_correct_data current_element
end
def search_within(div, element)
# How to do this?
end
end
How can I do this so that I don't have to define all the elements for all the divs (the number of div is dynamic)?
What I need to know is if there's a way to search an element to be inside another one.
Thanks :)
Edit The html will look something like this:
<div id='main_address'>
<input id='name'>
</input>
<input id='address'>
</input>
<input id='phone'>
</input>
</div>
<div id='secondary_address'>
<input id='name'>
</input>
<input id='address'>
</input>
<input id='phone'>
</input>
</div>
<div id='add_address'>
<input id='name'>
</input>
<input id='address'>
</input>
<input id='phone'>
</input>
</div>
Second Edit The point was to also declare this:
select_list(:name, :id => 'name')
select_list(:address, :id => 'address')
select_list(:phone, :id => 'phone')
#Is this possible?
current_name_element = main_address.name
#?
#Also, at the end the point is to call the Datamagic populate_page_with method
populate_page_with data_for 'new_user'
Where the 'default.yml" looks like this:
new_user:
name: ~first_name
address: ~address
phone: ~phone
Where I can choose which div this will be filled. Is this possible?
You can find an element within an element by using the element locators. These methods use the same type of locator you would use in the accessor methods.
For example, for a given current_div, you could get its related text fields:
current_element = current_div.text_field_element(:id => 'name')
current_element = current_div.text_field_element(:id => 'address')
current_element = current_div.text_field_element(:id => 'phone')
Note: You might need to change these locators. Presumably these ids are not exactly correct since ids should be unique on the page.
Applying the above to your page object, you could define the fill_element method as:
def fill_element(element, div)
current_div = self.send("#{div}_element")
current_element = current_div.text_field_element(:id => element)
# I assume this is a method you have already
fill_with_correct_data current_element
end
Calling the method would be like:
page.fill_element('name', 'main_address')
page.fill_element('address', 'secondary_address')
page.fill_element('phone', 'add_address')
Update - Created Scoped Locator:
This seems a bit messy, but is the best I can think of given the current implementation of the page object gem. I think there are some backlog feature requests for the gem that would make this cleaner.
What you could do is:
class AddressPage
include PageObject
class ScopedLocator
def initialize(name, page_object)
#name = name
#page = page_object
end
def populate_with(data)
scoped_data = Hash[data.map {|k, v| ["#{#name}_#{k}", v] }]
#page.populate_page_with scoped_data
end
def method_missing(m, *args)
#page.send("#{#name}_#{m}", *args)
end
end
['main_address', 'secondary_address', 'add_address'].each do |name|
define_method(name) { ScopedLocator.new(name, self) }
text_field("#{name}_name"){ div_element(:id => name).text_field_element(:id => 'name') }
text_field("#{name}_address"){ div_element(:id => name).text_field_element(:id => 'address') }
text_field("#{name}_phone"){ div_element(:id => name).text_field_element(:id => 'phone') }
end
end
This does 2 things:
It will create the normal accessors for each address. For example, it creates a :main_address_name, :main_address_address and :main_address_phone (and again for the other address types).
It creates a method, eg main_address that delegates actions to the normal accessors. This is to give the main_address.populate_with syntax.
With this page object, you could do the populate_with for a given address section:
page.main_address.populate_with data_for 'new_user'
page.secondary_address.populate_with data_for 'new_user'
page.add_address.populate_with data_for 'new_user'
As well as be able to update individual fields of an address. For example:
page.main_address.name = 'user'

how to set CSS attributes of label in MVC 3

I am creating a static using Html.LabelFor(...).
I have to set Name attribute of the label dynamically using JQuery.
You can set the css class, and set inline styles and any other attribute (even non-existant ones like name) using the htmlAttributes parameter provided in one of the overloads of LabelFor
ie
<%: Html.LabelFor(model=>model.Title,
new { style="xyz", #class="abc", #name="MyTitle" }) %>
this would create a label something like:
<label for="Title" style="xyz" class="abc" name="MyTitle">Title</label>
The reason for the # before class, is that "class" is a reserved word in c#, so you need to qualify it using the # symbol.
If I understand your question and comments together, you're just trying to change the text of a label. The MVC LabelFor turns into an HTML <label> and that doesn't have any attributes like a text box does.
If you need to change it with JS/jQuery then give it an ID and use the html method.
Markup:
#Html.LabelFor(m => m.Something, new { id = "somethingLabel" })
#Html.TextBoxFor(m => m.Something)
jQuery:
$("#somethingLabel").html("New Label Text");
You could also use text instead of html - the difference is just that it escapes the input.

Using awesome_nested_set dynamically adding extra children

I've got a nested model called categories and I have created a nested form to allow creation of a category and sub categories in one form.
This works fine if you pre-build the children in your new method like so:
class CategoriesController < InheritedResources::Base
def new
#category = Category.new
#category.children.build
end
end
The problem starts to happen when you want to dynamically add new children in the form using AJAX.
Here is my form:
%table
= form_for #category do |f|
%tr
%td= f.label :name
%td= f.text_field :name
%tr
%td(colspan=2)
%b Sub categories
- #category.children.each do |sub|
= f.fields_for :children, sub do |child|
= render "child_fields", :f => child
%tr
%td= link_to_add_fields "Add sub category", f, :children
%tr
%td= f.submit 'Save'
Here is my helper method for link_to_add_fields (as per Ryans Railscast):
module ApplicationHelper
def link_to_add_fields(name, f, association)
new_object = f.object.class.reflect_on_association(association).klass.new
fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
render(:partial => association.to_s.singularize + "_fields", :locals => { :f => builder})
end
link_to_function(name, "add_fields(this, '#{association}', '#{escape_javascript(fields)}')")
end
end
And here is the Javascript which over
function add_fields(link, association, content) {
// Generate new unique index, so base this off the current time.
var new_id = new Date().getTime();
var regexp = new RegExp("new_" + association, "g")
// Replace new_association with the current time.
$(link).closest("tr").before(content.replace(regexp, new_id));
}
I noticed that on the pre-built children the rendered output is like this:
<input type="text" size="30" name="category[children_attributes][0][name]" id="category_children_attributes_0_name">
Where as the AJAX generated fields are:
<input type="text" size="30" name="category[children_attributes][1308801890744][name]" id="category_children_attributes_1308801890744_name">
This looks correct but when I go to click create only the pre-built children are saved.
Update1
If I put a debugger line in my def create and type params I only see my pre-built category not the extra one I dynamically added.
(rdb:4) params
{"utf8"=>"✓", "authenticity_token"=>"iwq1Vx3jOZZsjd79Nj+qKNXxOwWP40c8XDFS8ooGMdg=", "category"=>{"name"=>"1", "children_attributes"=>{"0"=>{"name"=>"2"}}}, "commit"=>"Save", "action"=>"create", "controller"=>"categories"}
This is a result of browsers (at least Firefox) behaving weirdly when a form is inside of a table. The easiest/quickest fix is to put the table inside the form. It's a one-line change to your views/categories/new.haml file:
= form_for #category do |f|
%table
%tr
How I debugged it, in case it helps:
I first checked request.raw_post; the parameters weren't there which meant rails was never even seeing the correct request. That pointed to a browser rendering issue.
I was able to debug the issue via firebug by noticing that the form closed out awkwardly when rendering your original haml. Moving the form out of the table seemed to fix it in firefox.
I'd suggest sticking to divs, mainly because it avoids a lot of weird browser issues.

Resources