How to click an element from a dropdown with capybara? - ruby-on-rails

I'm having trouble getting an element to click from capybara. I've searched through other posts and nothing was helping me solve my problem.
The solution that I thought looked the best is this:
select "Manage Users", :from => "accountLink"
But in return I get this:
Admin Abilities Creates a new user
Failure/Error: select "Manage Users", :from => "accountLink"
Capybara::ElementNotFound:
Unable to find select box "accountLink"
# ./spec/features/admin_abilities_spec.rb:15:in `block (3 levels) in <top (required)>'
# ./spec/features/admin_abilities_spec.rb:14:in `block (2 levels) in <top (required)>'
Here is the rendered html:
<li class='dropdown' id='accountLink'>
<a class='dropdown-toggle' data-toggle='dropdown' href='#'>
Account
<b class='caret'></b>
</a>
<ul class='dropdown-menu'>
<li id='accountLink'>Change Info</li>
<li id='impersonateLink'>Impersonate User</li>
<li id='adminLink'>Manage Users</li>
<li>Toggle Fake Names</li>
<li id='logoutLink'>Logout</li>
<li class='divider'></li>
<li class='disabled'>Server Default</li>
</ul>
</li>
Can someone help me get capybara to click this element?

select 'Manage Users' is looking for a checkbox, but your dropdown items are simply anchor tags in a list. So you can just click on them:
find('a.dropdown-toggle').click # assuming you only have one a.dropdown-toggle
click_on 'Manage Users' # or find('#adminLink a').click

Some things that should work
click_link "Manage Users"
find(#adminLink).click
Note: Your html seems to be using id's badly. Their should only be one instance of an id on a page.

Related

How to give a unique id to a test object in Rspec?

context 'with dish available to pre-order' do
let!(:dish) { create :dish, chef: chef }
let!(:dish2) { create :dish, chef: chef2}
it 'should show dish' do
Time.zone = 'Asia/Kuala_Lumpur'
t = Time.zone.local(2016, 7, 25, 20, 30, 0)
Timecop.travel(t)
visit '/'
set_delivery_time
select_delivery_area_by_chef dish.chef
visit '/preorders'
expect(page).to have_content('PREORDER MENU')
within('div#dishes-container') do
expect(page).to have_xpath("//img[#src=\"#{dish.image.small}\"]")
expect(page).to have_content(dish.description)
expect(page).to have_content(dish.name)
expect(page).to have_content(dish.price)
expect(dish).to have_content("Add to Cart")
expect(dish2).to have_content("Pre-Order")
expect(current_path).to eq(preorders_path)
end
end
1) Visitor can see preorders with dish available to add to cart, preparation time over 1 hour should show dish
Failure/Error: expect(dish).to have_content("Add to Cart")
expected to find text "Add to Cart" in "Dock Bogisich"
# ./spec/features/visitor/preorders/list_of_preorders_spec.rb:129:in `block (4 levels) in <top (required)>'
# ./spec/features/visitor/preorders/list_of_preorders_spec.rb:124:in `block (3 levels) in <top (required)>'
I am trying to get rspec to check if specifically dish has 'add to cart' content and dish 2 to have 'pre-order' content. How can I get Rspec to check it? Is it possible to give them both a unique id and call it out here?
In your view assign ids to both the buttons. Something like:
<button id="add_to_cart-{dish.id}"> ADD to Cart </button>
<button id="pre-order-{dish2.id}"> Pre-Order </button>
Than in spec:
add-cart-btn = "button#add_to_cart-#{dish.id}"
with_in add-cart-btn do
page.should have_content 'Add to Cart'
end
Same for the Pre-order!

rails rspec capybara select not working

In my rails app, I have a select on the navigation bar as follows:
<body>
<div id="wrapper">
<!-- Navigation -->
<nav role="navigation" style="margin-bottom: 0">
<div class="navbar-default sidebar hidden-sm hidden-xs" role="navigation">
<div class="sidebar-nav">
<ul class="nav" id="side-menu">
<li>
<h4 class="sidebar-title">SGPLAN</h4>
</li>
<li class="logo">
<form action="#">
<select name="" id="change_plan" class="form-control plan">
<option value="1" id="plan_id" selected="">first </option>
<option value="2" id="plan_id">other </option>
</select>
</form>
</li>
and javascript in application.js to load the home page when the user selects a different option.
$(document).ready(function() {
//...
$('#change_plan').on('change', function(){
var str = ''
str += $( this ).val() + " ";
setCookie("plan", str,1);
window.location.href = "/";
})
});
I have written the following test for this feature using rspec, capybara and capybara-webkit:
require 'rails_helper'
feature "Change plan", :js do
background do
login_as create(:admin_user), scope: :user
Agency.current = Agency.find_by(initials: 'SECTI').id
FactoryGirl.create(:other_plan)
Plan.current = Plan.find_by(name: 'first').id
end
scenario "User changes the current plan" do
visit "/milestones"
save_and_open_page
select('other', from: 'change_plan')
# within '#change_plan' do
# find("option[value='2']").click
# end
# find('#change_plan').find('option', text: 'other').select_option
expect(current_path).to eq("/")
end
end
save_and_open_page results in the html snippet as shown above.
The result of running the test is as follows:
Failures:
1) Change plan User changes the current plan
Failure/Error: expect(current_path).to eq("/")
expected: "/"
got: "/milestones"
(compared using ==)
# ./spec/features/plans/change_plan_spec.rb:19:in `block (2 levels) in <top (required)>'
Finished in 1 minute 1.71 seconds (files took 2.04 seconds to load)
1 example, 1 failure
If I use find('#change_plan')... or find("option...") (as per the commented out lines) in the test instead of the select, the result is the same.
My versions are follows (from Gemfile.lock):
capybara (2.7.1)
capybara-webkit (1.11.1)
database_cleaner (1.5.3)
factory_girl_rails (4.7.0)
rails (4.2.5)
rspec-core (3.5.4)
rspec-expectations (3.5.0)
rspec-mocks (3.5.0)
rspec-rails (3.5.2)
and ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
What do I need to do get this test to work? Should I be using a different test platform? We are relatively committed to rspec but less so to capybara.
Update
I finally got this working with the help of Thomas and employing multiple suggestions that he provided.
There was a javascript error with the capybara webkit driver.
I tried the selenium driver but got a 503 error at the visit /milestones step.
I then switched to the poltergeist driver and found that the wait behaviour was also an issue - so I had to use have_current_path.
You shouldn't be using the eq matcher with current_path since it has no waiting/retrying behavior. This means you're checking the path before the page has time to change. Instead use the have_current_path matcher provided by Capybara
expect(page).to have_current_path('/')

Capybara not finding signout link by id

For a rails app I'm currently working on I recently changed the design somewhat so that the signout link no longer has the anchor text "Sign out" but instead a glyphicon from twitter bootstrap. the html for link now looks like this:
<a class="btn btn-primary btn-sm" data-method="delete" href="/users/sign_out" rel="nofollow">
<span class="glyphicon glyphicon-log-out"></span>
</a>
meanwhile my capybara test looks like this:
context "when not logged in" do
it 'cannot create wikis' do
#free_user = create(:user)
login_as(#free_user, :scope => :user)
click_link "Sign out"
visit root_path
expect(page).to_not have_link('Create wiki')
end
end
Now that the text "Sign out" is no longer there, I need a new way to identify the link. Checking the documentation for capybara (or rather this handy cheatsheet), it looks like I can supply either the text of the link or its id. So I tried giving it an id:
<a class="btn btn-primary btn-sm" data-method="delete" href="/users/sign_out" id="signout" rel="nofollow">
<span class="glyphicon glyphicon-log-out"></span>
</a>
So now it's got an id of "signout" however when I make this change to the test, it still won't pass.
1) Standard (free) User when not logged in cannot create wikis
Failure/Error: click_link "signout"
Capybara::ElementNotFound:
Unable to find link "signout"
# ./spec/features/standard_user_role_spec.rb:107:in `block (3 levels) in <top (required)>'
I tried making sure that I was still a logged in user in the test by creating and logging in the user as seen above and by adding a check that the html on the page contains Hello, since it says "Hello" and the name of the user when the user is logged in:
expect(page).to have_content('Hello')
This gave me another error that I don't understand:
1) Standard (free) User when not logged in cannot create wikis
Failure/Error: expect(page).to have_content('Hello')
Capybara::ElementNotFound:
Unable to find xpath "/html"
# ./spec/features/standard_user_role_spec.rb:107:in `block (3 levels) in <top (required)>'
So what could be going on here?
Full spec available here
You can re-add the text but hide it from all but screen readers:
<a class="btn btn-primary btn-sm" data-method="delete" href="/users/sign_out" id="signout" rel="nofollow">
<span class="sr-only">Sign out</span>
<span class="glyphicon glyphicon-log-out"></span>
</a>
This also improves accessibility somewhat.
context "when not logged in" do
it 'cannot create wikis' do
#free_user = create(:user)
login_as(#free_user, :scope => :user)
visit root_path
click_link "Sign out", visible: false
expect(page).to_not have_link('Create wiki')
end
end
Note that we explicitly tell Capybara to look for hidden text with the visible option.
For the applications, which use JS, that means the request is being sent using for example json format, and is begin triggered by click on the link. (suppose the HTML markup is the same). It is useful the following call:
find('Sign Out', visible: false).trigger('click')
or by id (if you'll add it):
find(:css, "#sign-out", visible: false).trigger('click')

Rspec testing view test fails but HTML is OK?

With Rspec, I'm testing the presence of the logo in the navnar:
#spec/views/_header_spec.html.erb
require 'spec_helper'
describe "layouts/_header.html.erb" do
subject{rendered}
it "should have the clickable logo" do
render
should have_selector("img")
should have_link("/")
end
end
This is my generated HTML:
<a href="/" class="navbar-brand">
<img alt="Logo" src="/assets/logo.png">
</a>
The page is OK, but the test fails:
$rspec spec/views/_header_spec.rb
F
Failures:
1) layouts/_header.html.erb should have the clickable logo
Failure/Error: should have_link("/")
Capybara::ExpectationNotMet:
expected to find link "/" but there were no matches
# ./spec/views/_header_spec.rb:10:in `block (2 levels) in <top (required)>'
Finished in 0.13914 seconds
1 example, 1 failure
Failed examples:
rspec ./spec/views/_header_spec.rb:7 # layouts/_header.html.erb should have the clickable logo
Randomized with seed 37707
The test fails, but the HTML behaviour page is correct, so I think my test is not working properly. Can you help me?
Replace
should have_link("/")
With
should have_link("Logo", href: "/")
have_link takes the display text of the link or alt attribute value of an image, and using the href option you can specify the corresponding path.

Rails Capybara not seeing fields

I feel like I'm going insane here. Doing a routine capybara test of my Post model. Specifically, making sure the new page redirects to the newly created post after a valid submission. So in my spec/requests folder, I've got this:
describe "Valid post submission --" do
it "should log in a user and redirect to new post" do
# Sign in works find, so I won't reproduce it
# Make a blogpost
Post.destroy_all
fill_in :name_here, with: "Post Name Here"
fill_in :content_here, with: "This is the content of a really short post."
screenshot_and_open_image
click_on "Post It"
screenshot_and_open_image
puts current_path
page.should have_selector 'h2', text: "prohibited this post"
page.should have_selector 'h1', text: "Post Name Here"
page.should have_selector '.alert', text: "Post was successfully created."
page.should have_selector 'title', text: full_title('Post Name Here')
end
The screenshots and puts are there to clarify what's going on. There are basically two different cases. In
I just leave the fields be and test for the default IDs ("post_name"
and "post_content"); obviously the above test is for case two:
I change the field ids to "name_here" and "content_here" to test if
the label names or anything else is interfering. That's the one the spec of which is
above.
In case 2 the post is getting rejected because Capybara can't find either field.
In case 1, only the content would be empty, but the name field would be filled with the content.
The precise error in case 1 is:
2) Posts Invalid post - should refresh with error if content empty
Screenshot: /Users/myusername/rails/appname/tmp/capybara/screenshot_2013-03-14-22-37-36.634.png
Failure/Error: fill_in :post_name, with: "Post Name Here"
Capybara::ElementNotFound:
cannot fill in, no text field, text area or password field with id, name, or label 'post_name' found
# (eval):2:in `fill_in'
# ./spec/requests/posts_spec.rb:43:in `block (3 levels) in <top (required)>'
Case 2 doesn't throw an error about not finding the appropriate content, I believe because the label has that id as well.
This is ridiculous, because I took a snapshot of the HTML durin the test, just before it errors. Here's case 2 -- both cases look 90% the same here, apart from that ID difference:
<form accept-charset="UTF-8" action="/posts" class="new_post" id="new_post" method="post">
<div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓"></div>
<div class="field">
<label for="post_name">Name</label>
<br><input id="name_here" name="post[name]" size="30" type="text">
</div>
<div class="field text-area">
<label for="post_content">Content</label>
<br><textarea cols="50" id="content_here" name="post[content]" rows="20"></textarea>
</div>
<div class="actions btn-group">
<input class="btn" name="commit" type="submit" value="Post It">
</div>
</form>
Note the clear existence of the post_content field. In cases where both fail (Case 1), there are both content_here and name_here fields. So the fields are there. And capybara is generally working (this sort of thing works find in other parts of my app). And the problem isn't a conflict with the label name, because putting a different id on the input fields doesn't help capybara find them.
By the way, this all works perfectly in reality.
Any idea at all what's going on here? I'm super frustrated/clueless.
UPDATE -- Following user2172816's advice, I changed my HAML so the HTML looks like this:
<form accept-charset="UTF-8" action="/posts" class="new_post" id="new_post" method="post">
<div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓"></div>
<div class="field">
<label for="post_name">Name</label>
<br><input id="name" name="post[name]" size="30" type="text">
</div>
<div class="field text-area">
<label for="post_content">Content</label>
<br><textarea cols="50" id="content" name="post[content]" rows="20"></textarea>
</div>
<div class="actions btn-group">
<input class="btn" name="commit" type="submit" value="Post It">
</div>
</form>
The test now looks like this:
# Make a blogpost
Post.destroy_all
fill_in "Name", with: "Post Name Here"
fill_in "Content", with: "This is the content of a really short post."
page.should have_selector 'h2', text: "prohibited this post"
page.should have_selector 'h1', text: "Post Name Here"
page.should have_selector '.alert', text: "Post was successfully created."
page.should have_selector 'title', text: full_title('Post Name Here')
But it's still erroring (in new parts of the same spec, too!):
1) Posts Invalid post - should refresh with error if content empty
Screenshot: /Users/username/rails/appname/tmp/capybara/screenshot_2013-03-14-23-54-38.072.png
Failure/Error: fill_in :name, with: "Post Name Here"
Capybara::ElementNotFound:
cannot fill in, no text field, text area or password field with id, name, or label 'name' found
# (eval):2:in `fill_in'
# ./spec/requests/posts_spec.rb:43:in `block (3 levels) in <top (required)>'
2) Posts Invalid post - should refresh with error if name empty
Screenshot: /Users/username/rails/appname/tmp/capybara/screenshot_2013-03-14-23-54-38.274.png
Failure/Error: fill_in :name, with: ""
Capybara::ElementNotFound:
cannot fill in, no text field, text area or password field with id, name, or label 'name' found
# (eval):2:in `fill_in'
# ./spec/requests/posts_spec.rb:33:in `block (3 levels) in <top (required)>'
3) Posts Valid post submission -- should log in a user and redirect to new post
Screenshot: /Users/username/rails/appname/tmp/capybara/screenshot_2013-03-14-23-54-38.459.png
Failure/Error: fill_in :name, with: "Post Name Here"
Capybara::ElementNotFound:
cannot fill in, no text field, text area or password field with id, name, or label 'name' found
# (eval):2:in `fill_in'
# ./spec/requests/posts_spec.rb:67:in `block (3 levels) in <top (required)>'
The attribute "for" of label should be the "id" of the input element, i.e.
<label for="name_here">Name</label>
<br><input id="name_here" name="post[name]" size="30" type="text">
And in the spec you should call:
fill_in 'Name', with: "Post Name Here"

Resources