How to find element in page-object gem with custom parameter? - ruby

I use page-object gem with RSpec and I want to create an element with a custom parameter like
link(:derect_link, text: "#{custom_text}"
Because I want to get the link to text and this text was changed every time when I starting the test.
How can I use it in spec scenario?

The accessor methods do not support custom parameters at run time. You will have to manually create the methods for the link. The equivalent of the methods created by the link accessor would be:
class MyPage
include PageObject
def derect_link_element(text)
link_element(text: text)
end
def derect_link(text)
derect_link_element(text).click
end
def derect_link?(text)
derect_link_element(text).exists?
end
end
This would be used like the standard methods, except that you would specify the text of the link:
# Click the link
page.derect_link('custom_text')
# Check if the link exists
page.derect_link?('custom_text')
# Get the link element to perform other actions (eg inspect attribute values)
link = page.derect_link_element('custom_text')
link.attribute('href')

Related

Cheezy Page-Object Gem Dynamic Locator for Generic Element?

Using Cheezy's page-object gem I've come across the ability to have dynamic element locators. (Noted at this github issue: https://github.com/cheezy/page-object/issues/203).
So for example I can do span_element(id: 'some id'), div_element(class: 'some_class'), etc. However what can I do if I need to locate a generic element? For example I could be working on a page that has angular so the elements are not traditional (like instead of a traditional html select control with options, it is a custom angular dropdown). I've tried element_element(class: 'class_name') and just element(class: 'class_name') but ruby says method missing
The generic dynamic element locator is defined in PageObject::ElementLocators#element as:
def element(tag, identifier={:index => 0})
platform.element_for(tag, identifier.clone)
end
The first argument is the element's tag name. If you don't know the tag name, you can specify "element" for any tag. For example:
class MyPage
include PageObject
def do_stuff
element('element', class: 'class_name').text
end
end

How do i clear my capybara steps using Page Object Pattern?

I have the following step definitions with Page Object Pattern gem 'site_prism':
class Main < SitePrism::Page
element :login_link, "a.log-in-link"
element :login_field, "input[name='userLogin']"
element :pass_field, "input[name='userPassword']"
element :enter_button, ".button_pretty"
end
If /I'm log in as "([^"]*)" with password "([^"]*)"$/ do |login, pass|
#main = Main.new
#main.login_link.click
#main.login_field.set login
#main.pass_field.set pass
#main.enter_button.click
end
It works fine but looks very heavy and unbeautiful. Is there any way to write it like capybara 'within' method? The following isn't work (Error: "can't convert Main into String (TypeError)")
within #main do
login_link.click
login_field.set login
pass_field.set pass
enter_button.click
end
You can hack support for a with statement into Ruby:
http://www.ruby-forum.com/topic/128781#574402
(I've seen this in VBScript, not sure if any other languages support it out of the box).
No, you can't use capybara's within functionality with site_prism.

Rails : jump to function chosen at realtime (variable name)

I would like to jump to an "import function" which can be one off the file I already wrote in lib/import/...
The user chooses an option in a select box and from this choice, I want to execute a specific portion of code on one uploaded file (import excel with different possible layouts)
I wrote this :
# Loading and executing the template chosen by user at step 1
template_path = 'import/'+params[:template]+'/import.rb'
require template_path
Import.action(filename, session, params, current_project)
I have several import.rb files located each in a separate directory. One of these is :
module Import
require 'spreadsheet'
def Import.action(filepath, session, params, project)
# My import code
end
end
The problem is that Rails is always calling the action method from the first directory in lib/firstdirectory/import.rb
I never reach another import.rb file located in lib/otherdirectory/import.rb
Is there a better way to execute a "jump to" functionnality in realtime ?
Why does Rails jump to always the same function ?
Edit :
My application.rb configuration file contains
config.autoload_paths += Dir["#{config.root}/lib/import/**/"]
Edit 2 :
# lib/importer/importer.rb
module Importer
class Base
# Whatever common logic the import.rb files have.
end
end
#lib/importer/Import_test/import_test.rb Note the Big letter for the directory (constant)
module Importer
class Import_test < Base
def self.import
logger.debug(">>>>>>>>>>>>> special function Test <<<<<<<<<<<<<<<<<<<<")
end
end
end
# Call from controller
logger.debug "------------------>> "+params[:template]
raise "Invalid input" unless params[:template].constantize.superclass == Importer::Base
params[:template].constantize.import()
The params[:template] returns the string Importer::Import_test (with capital letters)
I get the error :
NoMethodError (undefined method 'superclass' for Importer::Import_test:Module):
app/controllers/import_controller.rb:57:in `step2'
Your code using the first directory entry makes sense. When you reference a constant whose definition has not yet been loaded, Rails checks the entries in autoload_paths for a corresponding file. Since you already have that import.rb in your first directory, your application loads that file.
A better design for this IMHO would be something along:
config.autoload_paths += ["#{config.root}/lib"]
# lib/importer.rb
module Importer
class Base
# Whatever common logic the import.rb files have.
end
end
# lib/importer/foo.rb
module Importer
class Foo < Base
def self.import
# ...
end
end
end
# lib/importer/bar.rb
module Importer
class Bar < Base
def self.import
# ...
end
end
end
# In your view, a way to identify these:
select_tag :layout, options_for_select({
"Foo" => "Importer::Foo",
"Bar" => "Importer::Bar"
})
# In your controller:
raise "Invalid input" unless params[:layout].constantize.superclass == Importer::Base
params[:layout].constantize.import( ... )
Update:
Rails looks for files this way: Say you want to use FooBar::Baz. If it doesn't have FooBar yet, it will load lib/foo_bar.rb and there is supposed to be something there. Next, it will try to access FooBar::Baz. Only if it doesn't have that yet (already after loading lib/foo_bar.rb), it will load lib/foo_bar/baz.rb and there is supposed to be something there.
If you want to use autoload_paths and not require ruby files yourself, please use the convention of using proper camelcase that Rails can easily change to underscore. e.g. Use camelcase ImporterTest without the underscore and have lib/importer/importer_test.rb so the framework will be able to find the correct file and your definition.
:-) Good luck.
There are much better ways. I suggest having a hash of template names to objects that execute the action. Import all your importers, and construct the hash. Than use the hash to get the function, and execute it.

Have Cucumber Step Verify Variable Set By A Page Object In Another Step

I am using Cheezy's PageObject to setup some cucumber tests. I have everything pretty much setup like Jeff Morgan's book "Cucumber & Cheese".
Right now I have a page object "PublishPage" setup that has a method that sets a variable #tag. For example I have in the file publish_page.rb
Class PublishPage
def tag
#some steps left out
#tag = "123"
end
def verify_tag
#some steps left out
#tag.should include "2"
end
end
In the Cucumber steps, for one of the steps i have on_page(PublishPage).tag, and then in another step i have on_page(PublishPage).verify_tag. In my env.rb file I have require 'rspec-expectations'.
The problem is that when I run this code I get an error that says undefined method 'include' for #<PublishPage:xxxxxx>. But if I move the code inside the verify_tag method into the steps everything works except it does not have access to #tag...
This should be as simple as adding
include RSpec::Matchers
to your page object.
The other alternative would be to expose #tag through some method and then in your Cucumber step say something like
on_page(PublishPage).the_displayed_tag.should include("2")
Every time you invoke on_page(PublishPage), it will instantiate a new page object. You are most likely getting the "cannot convert nil to string" error because you are referencing an instance variable from a new object, hence it's value being nil. You should no longer get that error if you instantiate your page object only once, between calling page.tag and page.verify_tag.
Doing things this way will use one instance of your page object, allowing you to persist between step definitions.
When /I publish a tag/ do
#on_page(PublishPage).tag
#publish_page = PublishPage.new #browser
#publish_page.tag
end
Then /I should have my tag/ do
#publish_page.verify_tag
end
Hope this helps!

Dynamic page URL

I have a page with URL that is dynamic. Let's call it view post page. URL for post 1 is site.com/post/1 and for post 2 is site.com/post/2.
This is what I do at the moment to check if I am at the right page.
The page:
class ViewPostPage
include PageObject
def self.url
"site.com/post/"
end
end
Cucumber step:
on(ViewPostPage) do |page|
#browser.url.should == "#{page.class.url}#{#id}"
end
Is there a better way? Do you even bother checking the entire URL, or just the site.com/post/ part?
I am using the latest page-object gem (0.6.6).
Update
Even bigger problem is going directly to the page that has dynamic URL.
The page:
class ViewPostPage
include PageObject
def self.url
"site.com/post/"
end
page_url url
end
Cucumber step:
visit ViewPostPage
What I do now is to change the Cucumber step to:
#browser.goto "#{ViewPostPage.url}#{#id}"
It would be great if there was a way for the page to know it's ID, but I have not figured out yet how to do it.
You can get the url for the page using the method current_url. On your test above are you trying to determine if you are on the correct page? If that is the case I might suggest using one of the two "expected" methods - expected_title and expected_element.
The page_url method is more than likely not the choice for you if you need to navigate to a url dynamically. What you might try instead is add a method to your page that does something like this:
class ViewPostPage
include PageObject
URL = "site.com/post/"
expected_title "The expected title"
def navigate_to_post_with_id(id)
navigate_to "#{URL}/#{id}"
end
end
And in your test
on_page(ViewPostPage) do |page|
page.navigate_to_post_with_id #id
page.should have_expected_title
end
Let me know if this helps.
There is an option to check dynamic URL in the Page Object gem.
Use the below code:
class ViewPostPage
include PageObject
expected_url "The expected URL"
def initialize
has_expected_url?
end
end
It'll help you
As far as I know, #page_url is for opening corresponding page along with page object initialization. To verify you're on correct page, you can try to use #expected_title method.
Also, maybe it'll be useful for you. When symbol is passed to #page_url, it calls corresponding method, so'd better use it. I haven't tried it, but here are few links for you.
Original issue on GitHub
Specs
Documentation of #page_url

Resources