Forum: RSpec specifying which button webrat should press

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Pau C. (Guest)
on 2008-11-20 05:32
I was writing a feature in which I had webrat push a button. However,
there are many buttons on this page with the same text. I wanted to
specify which one would be push in as close a way I could to the way a
person would describe which one he was pushing. Here is what I ended up
with:

#custom_webrate_steps.rb
require 'rexml/document'
include REXML

When /^I push "(.*)" near "(.*)"$/ do |button, text|
  #The dom element that contains the text must have an id

  rexml_doc = Document.new(response.body)
  regexp = /#{Regexp.escape(text)}/
  nodes = find_nodes_with_regexp(rexml_doc, regexp)
  dom_id = nodes.first.attributes['id']

  within '#' + dom_id do |scope|
    scope.clicks_button(button)
  end
end


def find_nodes_with_regexp(node, regexp)
  result = []
  result << node if node.methods.include?('text') && node.text =~ regexp
  node.each {|n| result += find_nodes_with_regexp(n, regexp)} if
node.methods.include?('each')
  result
end


Any ideas for making this *nicer*?
aslak hellesoy (Guest)
on 2008-11-20 07:07
(Received via mailing list)
On Thu, Nov 20, 2008 at 4:32 AM, Pau C. <removed_email_address@domain.invalid> 
wrote:
> When /^I push "(.*)" near "(.*)"$/ do |button, text|
> end
>
> Any ideas for making this *nicer*?

give buttons unique dom ids and pass the id to #clicks_button ?
Pau C. (Guest)
on 2008-11-20 08:07
aslak hellesoy wrote:
> give buttons unique dom ids and pass the id to #clicks_button ?

Of course that's the easy way to do it  :)

I was hoping to keep my stories a bit closer to English. Plus then I'd
need some extra instance variable so that step can calculate the dom_id.
Here is an example of what I'm going for:

  Scenario: Accepting a friend request
    Given there is a user named "Fred"
    And I am logged in as a normal user named "Joe"
    And there is an unaccepted invite from "Fred" to me
    And there is a user named "Sally"
    And there is an unaccepted invite from "Sally" to me
    When I go to "the invites from friends page"
    And I push "accept" near "Fred"
    # I want to avoid: And I push #accept_button_user_1
    Then I should see "Now you are friends with Fred"
    When I push "ignore" near "Sally"
    Then I should see "Sally won't ever bother you again"


Or maybe you are suggesting:

  Scenario: Accepting a friend request
    Given there is a user named "Fred"
    And I am logged in as a normal user named "Joe"
    And there is an unaccepted invite from "Fred" to me
    And there is a user named "Sally"
    And there is an unaccepted invite from "Sally" to me
    When I go to "the invites from friends page"
    And I push "accept" for user "Fred"
    Then I should see "Now you are friends with Fred"
    When I push "ignore" for user "Sally"
    Then I should see "Sally won't ever bother you again"

with this matcher:
When /^I push "(.*)" for user "(.*)"$/ do |button, user_name|
  user = User.find_by_name(user_name)
  scope.clicks_button("accept_#{dom_id(user)}")
end

Am I putting too much thought into this?

Paul

P.S. I am sorry if this has been a dumb question. I'm still just trying
to learn the best way to approach this stuff.
David C. (Guest)
on 2008-11-20 09:07
(Received via mailing list)
On Thu, Nov 20, 2008 at 12:07 AM, Pau C. <removed_email_address@domain.invalid> 
wrote:
>    Given there is a user named "Fred"
>    And I am logged in as a normal user named "Joe"
>    And there is an unaccepted invite from "Fred" to me
>    And there is a user named "Sally"
>    And there is an unaccepted invite from "Sally" to me
>    When I go to "the invites from friends page"
>    And I push "accept" near "Fred"
>    # I want to avoid: And I push #accept_button_user_1

You're on the right track, and I don't think Aslak was recommending
putting accept_button_user_1 in the step.

What you can do is put it in the step definition, and use some sort of
map.

For example, I usually have a map of page names to paths so I can say:

  When I visit the new pet form

which is implemented like this:

  When /I visit the (.*)/ do |page|
    visit case page
    when "new pet form"
      new_pet_path
    end
  end

So you can do a similar mapping with Fred. Maybe it works like this
(just guessing - don't know what your app looks like)

  When /I push "(.)" near "(.*)" do |what, who|
    user = User.find_by_first_name(who)
    clicks_button "#{what}_button_user_#{user.id}"
  end

As long as you keep this sort of thing simple, I find it very
manageable and it actually begins to encourage good APIs and
conventions in the app.

>    And there is an unaccepted invite from "Fred" to me
>    And there is a user named "Sally"
>    And there is an unaccepted invite from "Sally" to me

I might do something to eliminate the need for the previous statement:

  Given /there is an unaccepted invite from "(.*)" to me/ do |username|
    user = User.find_or_create_by_first_name(username)
    # whatever you need to create the invite
  end

Now these first lines can be reduced to:

  Given I am logged in as a normal user named "Joe"
  And there is an unaccepted invite from "Fred" to me
  And there is an unaccepted invite from "Sally" to me

That's a lot less noise, and there is a clear implication that Fred
and Sally exist.

WDYT?

David
Andrew P. (Guest)
on 2008-11-20 15:45
(Received via mailing list)
I did something similar with

  Scenario: Cart shows item price and total price
    Given there is a product with name foo and price £24.50
    And I am on the products page
    When I add product with name foo to cart

And a step matcher

When /^I add product with name (.*) to cart$/ do |name|
  p = Product.find_by_name(name)
  p.should_not be_nil
  clicks_link_within("#product_#{product.id}", "Add to Basket")
end

A couple of things that influenced this where

1) I didn't think it was appropriate to have the technical mechanism
for adding the product (push button, click add link etc.) in the
feature

2) Any page with multiple button|links with the same text must have
some surrounding element that allows you to differentiate. This
technical detail belongs in the steps

As far as your features go I think that

  And I accept Fred's invite

scans better than

  And I push "accept" near "Fred"

HTH


2008/11/20 Pau C. <removed_email_address@domain.invalid>:
Pau C. (Guest)
on 2008-11-20 19:11
Andrew P. wrote:
>   And I accept Fred's invite
> scans better than
>   And I push "accept" near "Fred"


That definitely is better. I guess I was trying to be too broad. My
original goal was to write a match that would work on any page with
multiple links/buttons (of the same name).

It seems like everyone is suggesting that I write a step that is more
specific to this particular page. That will make my step matcher MUCH
simpler, and it will make my story more readable.

Thanks for all the advice!

Paul
James B. (Guest)
on 2009-02-20 22:13
Given this:

<form>

 <p>
   <input id="submit_commit_client" name="commit" type="submit"
value="Create" />
 </p>

</form>

If I want webrat to select on the ccs id (#submit_commit_client) rather
than the value (Create) how do I pass this to click_button?

I have tried this:

click_button("#submit_commit_client")

Which gives this error:

Could not find button "#submit_commit_client" (Webrat::NotFoundError)

/usr/lib64/ruby/gems/1.8/gems/webrat-0.4.1/lib/webrat/core/locators/locator.rb:14:in
`locate!'
Zach D. (Guest)
on 2009-02-20 22:26
(Received via mailing list)
On Fri, Feb 20, 2009 at 3:13 PM, James B. <removed_email_address@domain.invalid>
wrote:
>
> If I want webrat to select on the ccs id (#submit_commit_client) rather
> than the value (Create) how do I pass this to click_button?
>
> I have tried this:
>
> click_button("#submit_commit_client")
>

Can you use the following or do you need to use an id for other reason?

   click_button "Create"

> removed_email_address@domain.invalid
> http://rubyforge.org/mailman/listinfo/rspec-users
>



--
Zach D.
http://www.continuousthinking.com
http://www.mutuallyhuman.com
James B. (Guest)
on 2009-02-20 22:50
Zach D. wrote:

>
> Can you use the following or do you need to use an id for other reason?
>
>    click_button "Create"
>

That is how I am doing the check now.  I am simply investigating whether
another means is available to me.  At the back of my mind is the idea
that it is the presence of the function and not the form of the label
that is important.  By adding an id to the input tag and selecting on
that then whatever the button itself is labeled as becomes unimportant.
Zach D. (Guest)
on 2009-02-20 23:20
(Received via mailing list)
On Fri, Feb 20, 2009 at 3:50 PM, James B. <removed_email_address@domain.invalid>
wrote:
> Zach D. wrote:
>
>>
>> Can you use the following or do you need to use an id for other reason?
>>
>>    click_button "Create"
>>
>
> That is how I am doing the check now.  I am simply investigating whether
> another means is available to me.

You should be able to click the button by id, by not using a CSS
selector. ie:

   click_button "submit_commit_client"


> At the back of my mind is the idea
> that it is the presence of the function and not the form of the label
> that is important.  By adding an id to the input tag and selecting on
> that then whatever the button itself is labeled as becomes unimportant.


--
Zach D.
http://www.continuousthinking.com
http://www.mutuallyhuman.com
James B. (Guest)
on 2009-02-20 23:28
Zach D. wrote:

>
> You should be able to click the button by id, by not using a CSS
> selector. ie:
>
>    click_button "submit_commit_client"
>

You are right. That works. Thank you.

Regards,
This topic is locked and can not be replied to.