How to call a page-object from a class.rb at Support folder - ruby

I am using the page-object gem. Suppose i have a page-object on features/bussines/pages/booking_page.rb for a page like:
class Booking
include PageObject
span(:txtFirstName, :id => 'details_first_name')
end
...and i use a "tools" class located at features/support/tools.rb with something like:
class MyTools
def call_to_page_object
on Booking do |page|
puts page.txtFirstName
end
end
end
...but this approach fails because calling to the object from the class is not allowed:
undefined method `on' for #<Booking:0x108f5b0c8> (NoMethodError)
Pretty sure i'm missing some concept on the way to use the page-object from a class but don't realize whats the problem. Can you please give me an idea about what could be wrong here, please?
Thank you very much!
============================
Justin found the reason why the call to the class crash. The final class code results:
class MyTools
#Include this module so that the class has the 'on' method
include PageObject::PageFactory
def initialize(browser)
#Assign a browser object to #browser, which the 'on' method assumes to exist
#browser = browser
end
def getCurrentRewards
on Booking do |page|
rewards_text = page.rewards_amount
rewards_amount = rewards_text.match(/(\d+.*\d*)/)[1].to_f
puts "The current rewards amount are: #{rewards_amount}."
return rewards_amount
end
end
end
And the call to the function:
user_rewards = UserData.new(#browser).getCurrentRewards
Why it did not work me? Two main reasons:
I didn't pass the browser object to the class <== REQUIRED
I didn't include the PageObject::PageFactory in the class <== REQUIRED for the "on" method.
Thanks all!

To use the on (or on_page) method requires two things:
The method to be available, which is done by including the PageObject::PageFactory module.
Having a #browser variable (within the scope of the class) that is the browser.
So you could make your MyTools class work by doing:
class MyTools
#Include this module so that the class has the 'on' method
include PageObject::PageFactory
def initialize(browser)
#Assign a browser object to #browser, which the 'on' method assumes to exist
#browser = browser
end
def call_to_page_object
on Booking do |page|
puts page.txtFirstName
end
end
end
You would then be calling your MyTools class like:
#Assuming your Cucumber steps have the the browser stored in #browser:
MyTools.new(#browser).call_to_page_object

What are you trying to do?
Did you read Cucumber & Cheese book?
Pages should be in the features/support/pages folder. You can put other files that pages need there too.
If you want to use on method in a class, you have to add this to the class:
include PageObject
The code from MyTools class looks to me like it should be in Cucumber step file, not in a class.

Your class should use the extend keyword to access special class methods like span:
class Booking
extend PageObject
span(:txtFirstName, :id => 'details_first_name')
end
I hope this works.

Related

Undefined method for class - Sinatra

It seems I don't quite understand initializing or using a class within another class.
I have a Sinatra app and have created a class to handle fetching data from an api
# path: ./lib/api/bikes/bike_check
class BikeCheck
def self.check_frame_number(argument)
# logic here
end
end
BikeCheck.new
I then have another class that needs to consume/use the check_frame_number method
require 'slack-ruby-bot'
# Class that calls BikeCheck api
require './lib/api/bikes/bike_check'
class BikeCommands < SlackRubyBot::Bot
match /^Is this bike stolen (?<frame_number>\w*)\?$/ do |client, data, match|
check_frame_number(match[:frame_number])
client.say(channel: data.channel, text: #message)
end
end
BikeCommands.run
When check_frame_number is called I get a undefined method error. What I would like to know is what basic thing am I not doing/understanding, I thought by requiring the file which has the class it would be available to use.
No, you can not require a method defined in class - methods defined in class only available to class, class instances and within the inheritance.
Mixing method only possible with including modules.
To solve you issue you could either do
class BikeCommands < SlackRubyBot::Bot
match /^Is this bike stolen (?<frame_number>\w*)\?$/ do |client, data, match|
BikeCheck.check_frame_number(match[:frame_number]) # <===========
client.say(channel: data.channel, text: #message)
end
end
or write a module with the method and include/extend in class, you want that method to be available in.

How to create and call parameterised page-object classes

I am using page-factory visit, on methods to call page-object classes from spec file in ruby. I would like know how to parameterise page-object classes, passing parameters from spec file using page factory methods.
I want to log all steps information in page-object class. To do this, I created a log in spec file using the logger gem. I need to pass the log object as input parameter to page classes to capture data. Here is the code I am using to do this.
spec file that calling page class:
require './lib/pages/Test_page'
file="logs/uniusecase_#{#ncs_server['build_no']}_#{#ncs_server['test_type']}_#{time}.log"
$log=Logger.new(file)
describe 'testcase-1',:sanity do
visit Testpage, using_params: {logger: $log} do |page|
end
end
page-object class:
class Testpage
include PageObject
log = "<%=params[:logger]%>"
def goto
log ("test msg-1")
end
def testmethod()
log("test msg -2")
end
end
I am getting "NameError: undefined local variable or method `log' error message while execution. Could somebody help me in doing this?
The :using_params are stored in the class' #merged_params variable. You can get this variable by doing:
self.class.instance_variable_get(:#merged_params)
So your method would look something like:
def goto
logger = self.class.instance_variable_get(:#merged_params)[:logger]
logger("test msg-1")
end
However, if you are defining the logger in a global variable, it will already be available to the page object class (ie you do not need to pass it along). In other words, you could simply do:
def goto
$log ("test msg-1")
end

undefined method error when accessing class page object from module

I'm trying to access class method which is defined in Module, I can call function but function has page object element which performs some operation like click, I'm getting following error:
undefined method "label_year" for Datefunctions:Class (NoMethodError)
Here's my files structure:
./lib/calender_util.rb:
module CalenderUtil
def set_date
Datefunctions.get_calender_year
end
end
class Datefunctions
include PageObject
span(:label_year, :class=> 'ui-datepicker-year')
span(:label_month, :class=> 'ui-datepicker-month')
def self.get_calender_year
return label_year
end
end
./home_page.rb:
require 'calender_helper.rb'
include CalenderUtil
def setTravelDate date
CalenderUtil.set_date
end
parts of env.rb:
require 'page-object'
require 'page-object/page_factory'
$: << File.dirname(__FILE__)+'/../../lib'
require 'calender_helper.rb'
include CalenderHelper
World PageObject::PageFactory
World CalenderHelper
In addition; I've defined include/require multiple times I'll take off once this solved.
The reason is, the methods auto-generated by PageObject, are all instance methods. You can't use it in a class method because there is no instance.
Look at the doc's example:
class LoginPage
include PageObject
text_field(:username, :id => 'username')
text_field(:password, :id => 'password')
button(:login, :id => 'login')
end
login_page.username = 'cheezy'
login_page.password = 'secret'
login_page.login
The methods are for instances.
To fix, you need to create an instance.
module CalenderUtil
def set_date
page = Datefunctions.new(args_foo)
page.label_year
end
end
The problem is that label_year is an instance method while get_calender_year is a class method. You cannot call the instance method since you have not created an instance of the class.
As Billy Chan pointed out, for your code to work, you need to create an instance of the Datefunctions class within your module. This seems a bit awkward since you would need to pass the browser instance to each method called in the CalenderUtil. To me CalenderUtil is a layer of abstraction that is not adding any value.
I think that you should:
Use modules to encapsulate controls that are used across multiple pages.
Include these modules within the page object classes that have the controls.
Call the methods from the page objects.
For your example, I would create a Datefunctions module that defines the datepicker controls.
module DateFunctions
include PageObject
span(:label_year, :class=> 'ui-datepicker-year')
span(:label_month, :class=> 'ui-datepicker-month')
end
Then for each page class that uses the datepicker control, include the module:
class MyPage
include PageObject
include DateFunctions
end
In your tests, I assume it is Cucumber but the same is true for whatever framework, use the method from the page object.
page = MyPage.new(browser)
page.label_year.should == '1/1/2013'

Monkey patch class methods

I'm trying to do some monkey patching in ActiveShipping UPS class .
I need to add a class level method (starting with .self), so here it's what I'm trying to do:
module ActiveMerchant
module Shipping
class UPS < Carrier
def self.process_request(receiver, sender, packages, options = {})
# some code
end
def regular_method
"foobar"
end
end
end
end
Unfortunately when I'm trying to use it:
ActiveMerchant::Shipping::UPS.process_request(receiver etc)
I get an error:
NoMethodError: undefined method `process_request' for ActiveMerchant::Shipping::UPS:Class
from (irb):6
from C:/Ruby19/bin/irb.bat:19:in `<main>'
There is no class method named process_request in original class.
In original UPS class provided in gem there is one static method defined self.retry_safe = true
and I can use it without errors.
I can also use regular_method after creating instance of UPS class.
More details provided:
I'm working with Rails 2.3 ( :-( ) and Ruby 1.9.2. I have no influce on environment.
Monkey patched code is under plugins/my_plugin/lib/active_shipping/ext/carriers/ups.rb
In /active_shipping I have file named extensions.rb in which i have:
require 'active_shipping'
require_relative 'ext/carriers'
require_relative 'ext/carriers/ups'
It deals with loading everything properly (I suppose basing on regular_method beheaviour from first chunk of code in my question).
I try to invoke process_request in one of my Controllers. This part is little tricky, beacuse i'm using sth like this:
MyModel.courier_service.process_request(parameters)
where courier_service, in this case holds the ActiveMerchant::Shipping::UPS class.
I'm still a newbie in Ruby and don't know what sort of details i should provide.
Maybe you want to do it in another way
File patch_classes.rb:
module ActiveMerchantExpand
module Shipping
module ClassMethods
def self.process_request(receiver, sender, packages, options = {})
# some code
end
end
module InstanceMethods
def regular_method
"foobar"
end
end
def self.included(receiver)
receiver.extend ClassMethods
receiver.send :include, InstanceMethods
end
end
end
Then you have to load your class "ActiveMerchant::Shipping::UPS"
and after that you can attach your methods to your class via
Rails.configuration.to_prepare do
require_dependency [[file for ActiveMerchant::Shipping::UPS]]
require 'patch_classes' )
ActiveMerchant::Shipping::UPS.send(:include, ::ActiveMerchantExpand::Shipping)
end
This is from rails plugin writing, i hope this helps.
regards tingel2k
Do you explicitly require file with your monkey patch? If you just put it under your app or lib path without requiring, it wouldn't load because constant ActiveMerchant::Shipping::UPS is defined in gem and it doesn't trigger dependency resolution mechanism.

Rails Module issue - undefined method 'validates_email' for User:class

I have a class called User, that needs to include a module.
The class looks like this:
require 'sequel'
require 'modules/validations'
class User < Sequel(..)
many_to_one :country
includes ::Validations
validates_email(:email)
end
The module is defined in a subfolder called modules. It has been added to the $LOAD_PATH and Ruby is no complaining about the loading. The module looks like this:
module Validations
def validates_email(attr, options = {})
email = super.email
end
end
The error I am getting is :
undefined method 'validates_email' for User:class
What am I missing to make this work properly?
If you want to use modules to define class methods you should use the extends method.
class User < Sequel(..)
extend ::Validations
validates_email(:email)
...
end
I suggest reading this article: http://railstips.org/blog/archives/2009/05/15/include-vs-extend-in-ruby/

Resources