Can you choose watir commands through a variable? - ruby

I'm fairly new to programming, and I'm not sure what keywords I should be looking for.
I'm doing something like this right now:
def click(text, type)
b.span(:text=> text).click if type == 'span'
b.button(:name=> text).click if type == 'button'
b.image(:src=>text).click if type == 'image'
b.button(:title=>text).click if type == 'title'
end
I don't like it because it isn't scaling very well. I want to do something like:
def click(text,type)
b.type(:text=> text).click
end
It throws an undefined method error if I try to enter the type without quotes, but it's definitely not a string. How do I tell the script to use watir-webdriver span/button/image/etc?

It's hard to figure out exactly what it is you want to do with this method or why it's even necessary--or why your type parameter would ever be anything other than a string--but here's a way to help you clean up your code that's similar to what orde suggested.
Note that it's unclear what you're implying when you say "it's definitely not a string." If it's not a string, what is it? Where is it coming from that you are sticking it into this method's parameters without knowing what type of Object it is?
So... I'm assuming your type doesn't have to be a String object, so I made it so it takes symbols...
def click(text, type)
types={span: :text, button: :name, image: :src, title: :title }
#b.send(type, {types[type]=>text}).click
end

I'm not sure how you are calling your click method in your scripts, but here is a contrived example that seems to work:
require 'watir-webdriver'
def click_method(element, text)
#b.element(:text => "#{text}").click
end
#b = Watir::Browser.new
#b.goto "http://www.iana.org/domains/reserved"
click_method("link", "Domains")
EDIT:
require 'watir-webdriver'
def method_not_named_click(el, locator, locator_val)
if locator_val.is_a? String
#b.send(el, locator => "#{locator_val}").click
elsif locator_val.is_a? Integer
#b.send(el, locator => locator_val).click
end
end
#b = Watir::Browser.new
#b.goto "http://www.iana.org/domains/reserved"
method_not_named_click(:a, :text, "Domains")
method_not_named_click(:a, :index, 3)

Related

Selenium and Ruby element mapping

I am looking to map my elements using pageObject model, although I am facing the following issue:
1:. error is thrown given I have no driver, this is ok since I only map my driver when I instantiate the class
element = #driver.find_element(:id => 'username')
def initialize driver
#driver = driver
#driver.navigate.to "http://www.google.com"
end
def set_username input
element.send_keys input
end
2:. in the below approach, it doesn't complain about missing driver because I am initializing it before and passing it as a global variable. but now it tries to map the element even before opening the page, which fails with "couldn't find element"
element = $driver.find_element(:id => 'username')
def initialize
$driver.navigate.to "http://www.google.com"
end
def set_username input
element.send_keys input
end
The question is: Is there any cheeky way I can map my elements and assign them to objects but only have them compiled/read when I actually need to use them (I only perform some action with it in the set_username, and I would only want to trigger the object mapping when utilizing it in this method for example)... I prefer not to use an existing pageObject framework...
With the Page Object model, you have an object that represents the page you're testing, and objects that represent lower level HTML objects. In this case, you might have the following classes:
# Represents a text input HTML element...
class TextInput
attr_reader :element
def initialize(driver, id)
element = driver.find_element(:id => id)
end
def type(text)
element.send_keys text
end
end
# Represents the page you are testing...
class SomePage
attr_reader :driver, :username
def initialize(driver)
#driver = driver
end
def username
#username ||= TextInput.new(#driver, 'username')
end
end
Once you have initialized your driver, you pass it to SomePage, and use it
to drive what you're doing.
some_page = SomePage.new(driver)
some_page.username.type("Timmy")
I can't vouch for the page-object gem because I've never used it, but it would handle those HTML-layer objects, and give you a domain-specific language for constructing them into a page object class. Worth checking out.

How do I search a YAML file?

I have a YAML file books.yaml:
- !ruby.object:Book
title: Ruby for Newbz
author: LeeRoy Jenkins
category: Educational
I already have a method that adds books to this file, but I need a method that can search the YAML file using a regular expression. If no book matches the title then it should raise an exception NoBookfound. If there are any matches, that list should be returned to the caller.
Here is my existing code:
require 'yaml'
require './book'
class Library
attr_accessor :books
def initialize file_name = false
#books = file_name ? YAML::load(File.read(file_name)) : []
end
def add_book(book)
#books.push(book)
end
def search_library(file_name , book)
if
YAML::load(File.read(file_name)).include?(book) == false
raise 'No Book Found'
end
end
end
This is something I tried for the exception portion, but I feel like I'm way off. I'm really new to Ruby and this seems to be a pretty hard task. Does anyone have any ideas?
What you need is to test the class from the loaded object:
book = YAML::load(File.read(file_name))
raise 'No Book Found' unless book.kind_of? Book
There are also some kind_of? alternatives, which you can read about the differences on this nice question
Based on your post, I guess that book param is a Regexp object.
I think you should remove the file_name param from search method, since the file is already loaded on initialize method.
My implemetation would be:
def search_library term
#books.find_all {|b| b.title =~ term}
end
Since you already have "#books" in an enumerable you can use the select method to find books whose title have the search term as a substring.
def search_library(term)
matches = #books.select { |x| x.title.index(term) }
raise "No books have title like '#{term}'" if matches.empty?
matches
end

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

"Here methods" in Ruby?

I'm writing a few helpers to DRY up my tests. I pictured something like:
class ActiveSupport::TestCase
def self.test_presence_validation_of model, attribute
test "should not save #{model.to_s} with null #{attribute.to_s}", <<-"EOM"
#{model.to_s} = Factory.build #{model.to_sym}, #{attribute.to_sym} => nil
assert !#{model.to_s}.save, '#{model.to_s.capitalize} with null #{attribute.to_s} saved to the Database'
EOM
# Another one for blank attribute.
end
end
So that this:
class MemberTest < ActiveSupport::TestCase
test_presence_validation_of :member, :name
end
Executes exactly this at MemberTest class scope:
test 'should not save member with null name' do
member = Factory.build :member, :name => nil
assert !member.save, 'Member with null name saved to the Database'
end
Is it possible to do it this way (with a few adaptations, of course; I doubt my "picture" works), or do I have to use class_eval?
Have you seen Shoulda? It's great for testing common Rails functionality such as validations, relationships etc. https://github.com/thoughtbot/shoulda-matchers
In this case, it seems class_eval is necessary since I want to interpolate variable names into actual code.
Illustrated here.

Why do I lose my instance variables when I invoke Shoes#'visit'?

I'm sure this has to do with the intricacies mentionned in Shoes > Manual > Rules but I just don't get it. If someone would care to explain why #var == nil in the following code ...
I thought I could use visit to switch between different views in my application but that won't work if I lose all state.
class MyShoe < Shoes
url '/', :index
url '/somewhere', :somewhere
def index
#var = para link( "go somewhere", :click => "/somewhere" )
end
def somewhere
para "var = #{#var.inspect}"
end
end
Shoes.app
_why himself has answered this issue, and I'll get to that in a minute. First, the simplest way to pass data (specifically, strings) between different urls is like this:
class MyShoe < Shoes
url '/', :index
url '/somewhere/(\d+)', :somewhere
def index
#var = para link( "What is 2 + 2?", :click => "/somewhere/4" )
end
def somewhere(value)
para "2 + 2 = #{value}"
end
end
Shoes.app
It will match the subgroups of the regex and pass the matching strings as parameters to the method. Occasionally useful, but it gets unwieldy in a hurry. The other solution is to use constants or class variables, as _why explains here:
OK, so fooling around further it looks like all instance variables get
wiped at the beginning of every method within a Shoes subclass.
That's OK I guess. So what's the preferred way to have some data
that's shared from one Shoes URL to another? Passing it from one page
to the next in the URL itself works -- if it's a string. If it's not
a string, what should you use -- ##class_variables?
Sure you could use a class var. Those
are guaranteed to persist throughout
the life of the app. Or a constant.
Also, Shoes comes with SQLite3, data
can be passed there.
In your example, it would look like this:
class MyShoe < Shoes
url '/', :index
url '/somewhere', :somewhere
def index
##var = para link( "go somewhere", :click => "/somewhere" )
end
def somewhere
para "var = #{##var.inspect}"
end
end
Shoes.app

Resources