How can I find the page object of the page watir is currently on? - ruby

Context:
I'm trying to make reusable step definitions that click on page objects on the current page,
e.g. (cucumber step def follows):
When(/^the user clicks the "([^"]*)" button$/) do |button|
click_button = button.downcase.gsub(" ","_")
#current_page #somehow get current page object on this line
#current_page.click_button
end
Problem statement:
I can't find anything that returns the current page object.
An explanation for why the obvious solution didn't work:
I thought #current_page was already there as something I could use. I looked in the source code for page object, and the variable #current_page does exist. Not sure how to use it if I can...
BTW, in this case, I have a bunch of testers that can write Gherkin but not necessarily step definitions. We are trying to rapidly finish a bunch of regression tests for an in house app with an unchanging interface.

This is somewhat at odds with what page-object is trying to provide.
Page object attempts to provide well named actions for interacting with a specific page. If you are wanting to make something that works in general against any page, it will be much easier to write it with watir-webdriver directly.
That said, I agree that a specification based heavily on implementation like that is likely to change. I also would add that it doesn't add much value. I would only continue down this path if you understand and accept that you are using cucumber as a test templating tool instead of a requirements communication tool.

As Justin Ko mentioned, #current_page gets set when you call the on or visit methods. Its not a good idea to lump something that changes the page object in a step that performs a specific action (in this case clicking a button). You might want a different step that indicates the behavior of the application, such as
the application lands on the <your page> page
Then you're can use the name of the page object class to load #current_page via the on method in that step definition. This also gives the benifit (or curse of having your step having more lower level details) of indicating expected page navigation behavior.

Related

How to test one page where its content differs with permissions user has in Capybara, SitePrism

Creating tests in Ruby, Capybara using SitePrism. I have faced situation, where I have one site but content of the site depends on permissions the user has. For example element "admin" in menu is visible only for admins e.t.c. One major difference is that admins has their own subdomain like admin.example.com (site for normal user is example.com).
I have to test it both from admins and users point of view and I want to avoid creating two almost identical page objects.
Is there a right way to solve this?
So there are a variety of tools at your disposal here.
#all_there? will check if all declared elements are on the page. Furthermore this can be restricted down via the Use of DSL statements such as .expected_elements
I would advise you going back to the README which has a lot of new info in the last 6-9months and checking on it.
In terms of scoping so there is the concept of a user and an admin, that's also easy to partition using variables in your ENV hash perhaps and setting the url accordingly. Again there is documentation on this on the SitePrism docs/Github.
If you feel as though this still isn't working or there is an issue, open an issue request here: https://github.com/natritmeyer/site_prism/issues
class ExamplePage < SitePrism::Page
...
element :test_element, '#test_element'
...
end
...
let!(:example_page) { ExamplePage.new }
context 'situation 1' do
it 'displays test_element' do
...
expect(example_page).to have_test_element
end
end
context 'situation 2' do
it 'displays test_element' do
...
expect(example_page).to_not have_test_element
end
end

Fire an action when a link gets clicked to a page that has the same action (VoltRb)

At http://localhost:3000/books, I have an index page where I have a list of books.
When you click on one of the links, the action to which it is bound, book, gets fired:
However, when you click on one of the links from one of the book pages, the book action doesn't get fired:
Note that the URL does change when the links are clicked, from both the index page and the book pages, but the problem that I'm having is that book doesn't get activated when you click on a link to a book page from another book page. How can I fix a situation like this?
FYI, here is a repo where this problem can be reproduced.
The method book doesn't get called twice because your view is already setup. The change in the url only triggers an reactive update in your view.
What is it that you are trying to achieve?
As it turns out, Volt computations are a way to solve this problem. By using the params collection, you can attach computations to changes in the URL. I solved this issue and got book to run on changes in the URL like so: https://github.com/ylluminarious/volt_action_problems/commit/5fcefc16d7b14a980f68ce572a80f25540d21636.
Sorry for the late reply. This is a known issue thats on my to fix list. As GDP2 mentioned, you can use .watch! to handle it, or the probably better way to do it is to write your controllers in a more functional way so that the data being pulled from params is used in methods instead of setting instance variables.
So for example, if your using the params in a query, instead of doing something like:
attr_reader :query
def index
#item = store.items.where(name: params._name).first
end
You could do something like:
def query
store.items.where(name: params._name).first
end
This might seem less efficient, but there's a lot of caching and this is pretty much just as efficient.
Once I get time though, I'll make it retrigger the action method when accessed data changes. (Sorry, just haven't gotten to it.)

Following Test Automation best practise of "Methods return other PageObjects" in Ruby

I am a big advocate of the Page Object Pattern (POP) as defined by the experts at Selenium:
https://code.google.com/p/selenium/wiki/PageObjects
A key view of theirs that I have always followed when using Appium with Java is:
"Methods return other PageObjects"
e.g. LoginPage loginPage = homePage.gotoLoginPage();
I am now trying to following POP using Calabash with Ruby and so have been writing code like this:
e.g. #login_page = #home_page.goto_login_page
However, since Ruby doesn't know what type of object #login_page is or #home_page is, you dont get any of the benefits of intellisense showing what methods are available for a given page.
Anyone know a good way around this?
As much as I appreciate and apply PO design pattern, as much I disagree with returning page object by page object. Page object should be independent and don't need to know about other page objects. Look at two examples:
You test form validation. Click on submit button returns page object which is subsequent in the workflow, but in this case you remain on page with validation errors. Your page object won't know about it and will return the other page.
Page which you get to after clicking a button may differ depending on the context (e.g. from what other page you got to current page). It can lead to having multiple versions of actually same method, which will return different page objects depending on context. This is not good and overcomplicates simple thing.
If you want to return current page object, you can benefit from it e.g. in Java, when you return this at the end of the method. Then you can chain all methods you execute as long as you are on the same page. But when it comes to the question 'how to implement returning different page objects' - answer is simple - 'just don't'. Please note wiki entry you quoted has not been updated for a good while and best practices has evolved since it was originally published.
It seems like you already have your solution. However for others and perhaps also for you the x-platform approach to calabash uses page objects so you could check out that implementation https://github.com/calabash/x-platform-example
An alternative method would be as follows. Not as neat as I would like (given the need to manually create new instances of subsequent pages), but available as an alternative option:
When(/^I buy a movie from the movie page$/) do
movie_page = MoviePage.new
movie_page.buyMovie("Test Movie")
purchase_page = PurchasePage.new
purchase_page.confirmPurchase
end
Found a way of getting this to work after much research and applying well known Java/C#/Obj-c principles to Ruby:
Given(/^I am on the launch page$/) do
#launch_page ||= LaunchPage.new
end
When(/^I open the set alarm time page$/) do
#set_alarm_page = #launch_page.goto_set_alarm_page
end
When(/^I open our apps from the home page$/) do
#launch_page.navigation_toolbar.open_our_apps
end
Then(/^I should see the homepage alarm time is (\d+)$/) do |alarm_time|
alarm_time_actual = #launch_page.get_alarm_time
assert_equal(alarm_time, alarm_time_actual)
end
As long as somewhere on the step definition class you explicitly create a new page object (in the above example: LaunchPage.new), then all subsequent pages will provide intellisense method/property values, since the resulting page types returned will be known by RubyMine.

What is The Rails Way for requesting an alternate view of all records?

I have a rails app that has a list of Products, and therefore I have an index action on my ProductsController that allows me to see a list of them all.
I want to have another view of the products that presents them with a lot more information and in a different format -- what's The Rails Way for doing that?
I figure my main options are:
pass a parameter (products/index.html?other_view=true) and then have an if else block in ProductsController#index that renders a different view as required. That feels a bit messy.
pass a parameter (products/index.html?other_view=true) and then have an if else block in my view (index.html.haml) that renders different html as required. (I already know this is not the right choice.)
Implement a new action on my controller (e.g.: ProductsController#detailed_index) that has it's own view (detailed_index.html.haml). Is that no longer RESTful?
Is one of those preferable, or is there another option I haven't considered?
Thanks!
Another way of doing it would be via a custom format. This is commonly done to provide mobile specific versions of pages, but I don't see why the same idea couldn't be applied here.
Register :detailed as an alias of text/html and then have index.detailed.haml (or .erb) with the extra information. If you need to load extra data for the detailed view you can do so within the respond_to block.
Then visitors to /somecollection/index.detailed should see the detailed view. You can link to it with some_collection_path(:format=>'detailed')
I'm not sure whether this is 'bettrr' than the alternatives but there is a certain logic I think to saying that a detailed view is just an alternative representation of the data, which is what formats are for.
After doing some reading, I think that adding a new RESTful action (option #3 in my question) is the way to go. Details are here: http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
I've updated my routes.rb like this:
resources :products do
get 'detailed', :on => :collection
end
And added a corresponding action to my ProductsController:
def detailed
# full_details is a scope that eager-loads all the associations
respond_with Product.full_details
end
And then of course added a detailed.html.haml view that shows the products in a the detailed way I wanted. I can link to this with detailed_products_path which generates the URL /products/detailed.
After implementing this I'm sure this was the right way to go. As the RoR guides say, if I was doing a lot of custom actions it probably means I should have another controller, but just one extra action like this is easy to implement, is DRY and works well. It feels like The Rails Way. :-)

Cheating traversal

I'm developing a plone4 site on which every user have a sortable inventory of items. The ATFolder's folder_content view is ideal for this. The only problem is that instead of an URL like this:
/site/user/inventory
or this
/site/inventory/user
the url should be:
/site/inventory
I've thought in several solution, but each one have its own doubts.
Make the inventory content dynamic, depending on the authenticated user. I don't even know if this is possible on plone.
Somehow to cheat the transversal mechanism, so /site/inventory render /site/inventory/user.
Change the context before rendering the view. Again, don't know if possible.
Make inventory a subclass of ATCTContent, store the inventory data as annotation on the user and develop the ordering code all by myself. This is the option I'm trying to avoid.
What would you do?
Thanks.
Well, it'll be easy to define a inventory view that then uses the Authenticated User to render it's contents, which could be a simple delegation to an ordered folder that is stored at /site/users/user/folder.
The one thing that you have to remember is that user authentication happens after traversal. This means that when a view is instantiated (it's __init__ method is called) there is no user determined yet because that happens during traversal. Look up your user in the view __call__ or from it's template instead.
Having folder contents show contents that are not the contents of the folder is crraaaaAAAAzytalk. :) Don't do it. Either have a folder per user ( /inventory/user ) or make a custom view called inventory.html. You can make /inventory sho /inventory user but that is one step towards trying to make Plone to non-ploneish things, and that way lies a world of pain.
I don't know why you couldn't just call it /inventory/user? Seems easy enough. Then stick an action in the user viewlet, by the dashboard link, and your done! :-)
Plone is a content management system. Use it for that, as it's supposed to be used, and you'll be happy. Trying to force it to do things it doesn't want is like trying to build a sportscar out of a art deco sculpture. It might end up looking awesome, but it won't run very well. :-)

Resources