Capybara not finding signout link by id - ruby-on-rails

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')

Related

Unable to find field “q” (Capybara::ElementNotFound) via fill_in

Trying to get the "what" form (aka job title), from indeed.com
Error when trying to run the program:
/var/lib/gems/2.3.0/gems/capybara-2.11.0/lib/capybara/node/finders.rb:44:in `block in find': Unable to find field "q" (Capybara::ElementNotFound)
Inspecting element via firefox from indeed.com yields: name="q"
<span class="inwrap">
<input class="input_text" maxlength="512" size="31" aria-labelledby="what_label_top hidden_colon what_label_bot" name="q" autocomplete="off" id="what">
</span>
<div style="width:250px"><!-- --></div>
Which matches the code in the scraper:
def perform_search
# For indeed
fill_in 'q', :with => #skillset
fill_in 'l', :with => #region
find('#fj').click
sleep(1)
end
The entire code can be found at:
https://github.com/jasnow/job-hunter/blob/master/scraper.rb
Now the problem here is the inability to locate name="q" Are there any other ways I could link to that form on indeed.com so I could initiate webscraping? I'm talking xpath or css perhaps.
Your code only allows the URL http://www.indeed.com , but that URL redirects to https://www.indeed.com and also hits http://indeed.com. Therefore your page load is being blocked. Change to config.allow_url("indeed.com") and it should be able to find the input.

Capybara cannot find a link on a modal which changes on button press

I have a rails project using rspec 3.4.0 capybara 2.6.2 and capybara-webkit 1.8.0.
I am writing a feature test for a flow on my site which looks like the following:
scenario "Buyer creates a seller profile", :js => true do
click_link("SELL ON MYSITE",match: :first)
expect(page).to have_text("Reach thousands of customers in your area")
click_link("Create an Activity",match: :first)
expect(current_path).to eql (new_seller_profile_path)
fill_in "seller_profile[business_name]", :with => "Test company"
fill_in "seller_profile[business_email]", :with => "test#email.com"
fill_in "seller_profile[business_phone_number]", :with => "07771330510"
fill_in "seller_profile[business_description]", :with => "This is a test company"
find('label[for="social"]').click
find("#facebook-placeholder").click
fill_in "seller_profile[business_facebook_url]", :with => "https://www.facebook.com/test"
click_button("CREATE AN ACTIVITY")
------this button opens a modal after the page changes --------
fill_in "seller_profile[requested_postcode]", :with => "EH21 8PB"
click_button("Submit")
save_and_open_screenshot
------this is where it goes wrong-------
click_link("Continue")
expect(page).to have_text("Choose the type of activity that you want to create")
end
The click_link continue fails with error:
Failure/Error: click_link("Continue")
Capybara::ElementNotFound:
Unable to find link "Continue"
The link actually does exist - when you click on the submit button some javascript executes which changes the contents of the modal to display some new text and a new button. However for some reason click_link does not wait or look in the modal, it fails straight away.
Having added a save_and_open_screenshot call we can see this situation as the modal javascript has yet to execute as we can still see the submit button :
Interestingly the mouse seems to not be on the Submit button as it should have just clicked it?
How can I make the click_link wait until the continue button appears?!
This is the javascript which executes on press of 'submit' added to the modal which changes it:
$('#gate .modal-body .intro').text('Congratulations, we are available in your location, please continue to create your activity.');
$('#gate .modal-footer').append('<a class="btn btn-lg btn-primary" href="/events/new/">Continue</a>');
Seemingly adding a couple of sleeps in has fixed it. I don't know if this is 'right' solution. If somebody has a better way or potentially a better way I would love you to help me find it :
fill_in "seller_profile[requested_postcode]", :with => "EH21 8PB"
sleep 2
click_button("Submit")
sleep 2
click_link("Continue")

How to click an element from a dropdown with capybara?

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.

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