Closed knorrium closed 11 years ago
This is how I do it:
def self.url
"#{BASE_URL}dashboard/profile"
end
page_url url
@zeljkofilipin the base url is okay and I'm using it with environment variables, I'm trying to pass parameters to the page class.
Some examples:
Thanks,
FK
This is what I usually do:
homes_page.rb
class HomesPage
include PageObject
def self.url
"#{BASE_URL}homes/"
end
page_url url
def url_with_id(id)
"#{self.class.url}#{id}"
end
def visit_page_with_id(id)
navigate_to url_with_id(id)
end
end
homes_steps.rb
Given /^I am at Homes page for home (\d+)$/ do |id|
on(HomesPage).visit_page_with_id(id)
end
We have a similar problem and extended page object locally with the following:
module PageObject::Accessors
def dynamic_url(url)
define_method("generate_dynamic_url") do |id|
url.sub("#id#", id.to_s)
end
end
def with_id(id)
define_method("goto") do
platform.navigate_to self.generate_dynamic_url(id)
end
self
end
end
Using it looks like:
class ThingPage
include PageObject
dynamic_url "#{your_host}/thing/#id#"
end
visit_page ThingPage.with_id(123) do |page|
# stuff
end
The above just replaces the string '#id#' with the id you want. I wasn't sure how useful this would be to the larger community, so didn't open up a pull request. Could do so if it's desired though.
We're also considering a usage like:
dynamic_url "#{your_host}/thing?p1=#p1#&p2=#p2#"
visit_page ThingPage.with_params(:p1 => 'stuff1', :p2 => 'stuff2')
This would let you have as many params as you want with whatever names.
@gshakhn your idea of the with_params method seems to be exactly what I had in mind but didn't know how to implement :) I would love to see this on master.
What do you think, @cheezy?
@zeljkofilipin thanks for your input as well :)
The downside with my approach is that we modify that page object's class permanently. So if you did:
visit_page ThingPage.with_params(:id => 123)
It would hit 'thing/123', which is what you want. However, if you then did:
visit_page ThingPage
You would still hit 'thing/123'. This feels bad, but I'm not sure how to get around it cleanly.
In theory, you could make the with_params method flip a switch that tells the goto method to use the dynamic url. Once it's used, then the switch flips off and subsequent invocations of visti_page with that page object will use the page_url url, unless that invocation has with_params. i.e.
def with_params
@use_dynamic_url = true
# save params somewhere
end
def goto
if use_dynamic_url
@use_dynamic_url = false
goto_generated_dynamic_url
else
goto_static_page_url
end
I don't fully like the above solution though :(
Another thing that would be nice is if you could do:
visit_page ThingPage.with_id(123).with_locale('en-US')
instead of:
visit_page ThingPage.with_params(:id => 123, :locale => 'en-US')
But that would involve storing even more state.
There is also https://github.com/cheezy/page-object/issues/69, which has a different solution for this problem.
Sorry guys. I've been doing some hard travel the past few days (3 days, 3 cities) and am just now catching up on this thread. There is a callback that happens during object creation but it happens after navigation (if you use visit_page). Here's the constructor from PageObject module.
def initialize(browser, visit=false)
initialize_browser(browser)
goto if visit && respond_to?(:goto)
initialize_page if respond_to?(:initialize_page)
end
If you implement the initialize_page method it might be a way to provide some dynamic data to the page but it will clearly not work in the case of a dynamic url. For things that change from environment to environment (like base url) I use one of my other gems fig_newton.
I think it would not be nice to add the ability to pass parameters via the visit_page method. I could see it working something like this:
visit_page(MyPage, :using_params => { :id => 'the_id', :locale => 'es' })
and in your page you would have access to these params using something like this:
page_url "#{FigNewton.base_url}/account/#{params[:id]}"
I'll need to do some investigation to ensure it can be done without breaking the current functionality.
What do you guys think?
Sounds reasonable. It'll cover our use case and doesn't have my ugly hack. :)
I think this would be very beneficial!
I would like to see it implemented.
+1 :D
This is going to be a little more difficult than I originally thought. The page_url
method is invoked when the class is evaluated (when it is read) and at that time the params are not available. They only become available when the visit_page
method is called. I'll keep working on it to see what I come up with.
The challenge I am having is that the string passed into page_url
is evaluated at the same time the page object class is evaluated. This is long before any calls to visit_page
. As a result, I am unable to use the parameters in my call to page_url
. A simple solution might be to add a url parameter to visit_page
which would override the url passed to page_url
. Here's an example:
class MyPage
include PageObject
page_url "#{FigNewton.base_url}/page"
end
In a step definition the default behavior would be this:
visit_page(MyPage)
This would use the page defined the class. In order to provide a dynamic url you could do this:
visit_page(MyPage, :using_url => "#{FigNewton.base_url}/page/#{something_else}")
This would use the provided url instead of the default value. Would this work for your use cases?
Works for me.
The drawback with passing in the full url is DRY. Our urls are long and unwieldy, with only one part different from test to test. It also seems weird that the test would know the url of the page instead of the page object figuring it out.
Instead of evaluating the url from page_url using string interpolation, could you use simple string substitution? Something like: (haven't actually tried this, may have syntax errors)
class MyPage
include PageObject
page_url "#{FigNewton.base_url}/page/#param1#"
end
Visit page usage would be like:
visit_page(MyPage, :using_params => {:param1 => 'stuff'} )
The definition of page_url could look something like:
def page_url(url)
define_method(":goto") do |params|
real_url = params.reduce(url) do |url, pair|
url.sub("##{pair[0]}#", pair[1].to_s)
end
platform_navigate_to real_url
end
end
That could work or it could support something more standard like erb. In that case the url would be:
page_url "#{FigNewton.base_url}/page/<%=params[:encoding]%>"
You could then use the params similar to what you have above.
visit_page(MyPage, :using_params => {:encoding => 'en'})
This is something I should be able to get in and release this weekend.
Something standard would be better. :)
Unless I hear from anybody else I think that is what I'll go with.
I've just checked in code that does the following:
First of all there is a new instance variable @params
that exists on your page object class. You can setup params that can be used in your url's like this:
class MyPage
include PageObject
params = { :encoding => 'en', :id => 3 }
page_url "#{FigNewton.base_url}/somecontext/?id=<%=params[:id]%>?encoding<%=params[:encoding]"
end
Notice here that I am using both interpolation and erb. The interpolation happens first and then the erb is run with the params
available. You can think of the params
as the default values that are used. There is also an update to visit_page
. With this update you can provide additional params that are merged with the defaults. Here's an example:
visit_page(MyPage, :using_params => {:id => 5})
In this case the Hash
pointed to by :using_params
is merged with the original params
. I believe this solves the problems stated in this thread. Let me know if I am missing something. I plan to roll a release in a couple of hours so please respond soon.
This is released. Please take a look and let me know if all of your issues are resolved.
I'm a little late to the party here, but I wonder if it might be possible to change this to be a little more dynamic? My reasoning for this is because you may not always want to specify query string parameters every time you visit a particular URL.
For example, I may want to visit my accounts listing page and specify a limit on how many results are returned, but I may also want to visit it and test the default functionality.
page_url "http://blahblah/accounts?limit=20"
page_url "http://blahblah/accounts"
With the current architecture, the limit=<%=params[:limit]%>
will be appended regardless, which isn't really helpful for testing the default.
A solution I was thinking of is to strictly specify query params in the visit_page method, and give the opportunity to specify path params in the page_url definition. So for example if you're trying to access the following url: accounts/52/search?limit=20&query=Find%20this%20query
We could implement that by using the following page_url:
page_url "http://blahblah/accounts/{id}/search"
And the following visit_page call:
visit_page(MyPage, :query_params => {:limit => 20, :query => "Find this query"}, :path_params => {:id => 52})
This approach is very flexible in that it allows for both path and query params, and it makes the query params optional. To add the query params to the url, you can just iterate over the query_params hash and append them to the url during the visit_page call. I think this also keeps your page_urls nice and clean.
What are your thoughts about this approach?
I just had to use it, and it works great! Thanks @cheezy :)
Anything for you Zeljko! I was just afraid you'd kick my ass…
-Cheezy
On Nov 27, 2012, at 4:51 PM, Željko Filipin notifications@github.com wrote:
I just had to use it, and it works great! Thanks @cheezy :)
— Reply to this email directly or view it on GitHub.
Why isn't this closed?
@zeljkofilipin I didn't end up testing the fix, but since you did, feel free to close it :)
I can not close the issue that I did not open. :)
@cheezy I think your example above has a typo and is supposed to have a class variable @params.
class MyPage include PageObject @params = { :encoding => 'en', :id => 3 } page_url "#{FigNewton.base_url}/somecontext/?id=<%=params[:id]%>?encoding<%=params[:encoding]" end
Is it possible to visit a page with different parameters?
I tried adding a initialize method to the my page class, but failed while invoking the visit_page(MyPage) method.
I would like to do something like this:
Perhaps a hash or an array of parameters to build the final url would be better.
Any ideas? Do you think this is a feature worth having?
Thanks,
FK