Capybara matcher for presence of button or link - ruby-on-rails

Users on web page don't distinguish between "button" and "link styled as button".
Is there a way to add check whether a "button or link" is present on page?
For example Capybara has step:
page.should have_button('Click me')
which does not find links styled as buttons.

Updated answer (should matcher is deprecated in RSpec 3.0+):
expect(page).to have_selector(:link_or_button, 'Click me')
Before:
page.should have_selector(:link_or_button, 'Click me')
Followed from click_link_or_button which is defined here: https://github.com/jnicklas/capybara/blob/master/lib/capybara/node/actions.rb#L12
def click_link_or_button(locator)
find(:link_or_button, locator).click
end
alias_method :click_on, :click_link_or_button
It calls a selector :link_or_button. This selector is defined here: https://github.com/jnicklas/capybara/blob/master/lib/capybara/selector.rb#L143
Capybara.add_selector(:link_or_button) do
label "link or button"
xpath { |locator| XPath::HTML.link_or_button(locator) }
end
It calls this method: http://rdoc.info/github/jnicklas/xpath/XPath/HTML#link_or_button-instance_method
# File 'lib/xpath/html.rb', line 33
def link_or_button(locator)
link(locator) + button(locator)
end
So i tried to check the presence of the selector and it worked:
page.should have_selector(:link_or_button, 'Click me')

Using the expect syntax
expect(page).to have_selector(:link_or_button, 'Click me')
This works without needing to define a custom matcher.

You can also use a custom matcher
RSpec::Matchers::define :have_link_or_button do |text|
match do |page|
Capybara.string(page.body).has_selector?(:link_or_button, text: text)
end
end
Then do
expect(page).to have_link_or_button('Login')

Personally I would give your button or link an id and look for that using
page.should have_css('#foo')
This way you can refer to the link or button without worrying about its implementation.
I always find this useful: https://gist.github.com/428105

I think you can use the find button instance method:
(Capybara::Element) find_button(locator)
Using id, name, value.
Or if you want a link
(Capybara::Element) find_link(locator)
From: http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Finders#find_button-instance_method

I had an odd case where some smoke tests marched across various customer-centric login pages that had slight variations on doing the login submit button... Driven by a Cucumber table of user, org, etc.
# A bit of a hack, org_name is normally a subdomain, but sometimes it is the complete domain
def login(user, org_name)
# Use the below to automatically hit each user's org's server
if org_name.include? '.com'
Capybara.app_host = "http://#{org_name}"
else
Capybara.app_host = "http://#{org_name}.mydomain.com"
end
visit '/'
fill_in 'username', :with => user
fill_in 'userpwd', :with => '***'
begin
page.find(:link_or_button, 'submit')
click_on 'submit'
rescue Capybara::ElementNotFound
page.find(:link_or_button, 'Log In')
click_on 'Log In'
rescue Capybara::ElementNotFound
pending "Need to determine how to invoke the Login button for #{org_name} near Line ##{__LINE__} of #{__method__} in #{__FILE__} "
end
# -----------------------
# Work-around for modal popup saying SSL is mismatched if you are using actual production URLs
# The rescue is for cases that do not exhibit the modal pop-up
page.driver.browser.switch_to.alert.accept rescue Selenium::WebDriver::Error::NoAlertPresentError
# Ensure that login was successful
page.should_not have_content 'Login failed'
end

if html:
<a class="top-menu__item">text123
<span class="label">
<span class="label">86</span>
</span>
</a>
not work:
assert page.has_selector?(:link_or_button, text: 'text123')
assert page.should have_selector(:link_or_button, text: 'text123')

Related

Capybara and remote links

I have this link in records_path page:
link_to "CLICK HERE", edit_record_path(record), remote: true
The controller:
def edit
puts request.format # Just to show you part of the issue
end
And when clicked, it executes the edit.js.coffee and renders a modal for editing the record.
It works great. The problem I have is with Capybara.
context 'when on records list page' do
before { visit records_path }
context 'when clicking on "CLICK HERE"', js: true do
before { click_link('CLICK HERE') }
it 'shows the record name' do
expect(page).to have_content record.name
end
end
end
When I run the test, it raise an error:
Capybara::ElementNotFound: Unable to find link "CLICK HERE"
But if I remove the js: true from the context, it works (It executes the edit method on the RecordsController) but the printed format is html instead of js. I don't even have an edit.html, I just have the edit.js.coffee and that is the one that should be rendered in the test.
What am I doing wrong? If the link has the remote: true prop, and the test has the js: true shouldn't be enough for make it work?
In your setup you don't actually create a record. I am assuming this is the cause of your problem. The edit_record link will not show up if you do not have records to edit. I could be wrong but from what you pasted I think this could be the cause.

Using regular expressions with Capybara

In the below capybara test I want to check that a user can see any number after 'Temperature:' when they visit the root path of my rails application. The value which is stored in the instance variable #temperature is retrieved from an API and is displayed to the user when the page is refreshed.
How can i put a ruby regular expression like 'Temperature: \d' into my spec below?
Spec:
require "rails_helper"
feature "user sees temperature" do
scenario "success" do
visit root_path
expect(page).to have_css 'p', text: 'Temperature: \d'
end
end
View:
<p>Temperature: <%= #temperature %></p>
This is about right, you just have to use regex syntax (//) instead of string one (''):
expect(page).to have_css('p', text: /Temperature: \d/)

Testing ruby with rails, Element not found

I get the error:
Capybara::ElementNotFound:
Unable to find field "user_email"
And this is the test code:
feature 'User' do
given!(:user) { User.new(email: 'testuserid#example.com', encrypted_password: 'test') }
scenario 'opens sign_up page' do
visit new_user_session_path
expect(page).to have_content 'unique text on the page'
end
scenario 'signs in with invalid email' do
visit new_user_session_path
fill_in('user_email',with: 'ssd')
expect(page).to have_content 'unique text on the page'
end
end
My HTML file consists of this code literally:
unique text on the page
<br>
<input type="text" id="user_email">
So this proves that the path is correct because my first scenario runs correctly. It is visiting the right page. But still I get this error for second scenario in fill_in.
I have also tried element = page.find("user_email"), it gives same error.
What am I possibly doing wrong?
I have been scratching my head like hell.
Usually the reason for this is that the input isn't actually visible on the page. You can verify this by doing
fill_in('user_email', with: 'ssd', visible: false)
If that succeeds in finding the element, then you need to change your test to first perform whatever actions make the field visible before attempting to fill it in.
Your code seems right. Maybe you are visiting wrong url or you have used user_email id once more. But you can give a try with alternative syntax like following :
find("input[id$='user_email']").set "ssd"

How to test for flash using cucumber

I am writing cucumber tests for my RoR app and I need to test for a flash to appear:
<p class='flashnotice flash'>Message was successfully sent.</p>
However, I just can't find a solution that works. I've tried:
page.should have_selector('p', :class => 'flashnotice flash') do
page.should have_content fartknocker
end
But that returns an error on :class and says I can't use that. Can someone show me the right way to test for flash?
You can use this style instead
page.should have_css('.flashnotice', text: "Message was successfully sent")

How do I simulate an "Edit" or "Destroy" button click in Rails 3.2 w/ MiniTest?

How can I write an integration test for editing an item? My "create" test looks like this:
it "lets a user create a product" do
login_user
click_link("Products")
click_link("New")
fill_in "Identifier", :with => "MyString"
click_button "Create"
assert page.has_content?("Product was successfully created")
end
And that works great. What I am confused about is how to do the Edit and Destroy tests. My index page provides a list of all products. So first I use a factory to create a couple of products. Now I am in the situation where there are multiple "Edit" and "Destroy" buttons. I can't just say:
click_button "Destroy"
because there are two of them. How do I tell it which one to click?
And if I do get the correct "Destroy" button clicked, how do I hit the "OK" button in the Javascript window that pops up?
Assuming that you're using Webrat, you can use the "within" selector.
The Webrat "within" method takes a CSS selector as an argument. Supposing your "Destroy" button is in a div with an id like "#product-2", you can isolate that button with:
within "#product-2" do |scope|
scope.click_button "Destroy"
end
If you need to / would rather use XPath, you can do something like:
response.should have_xpath(xpath) do |button|
click_button(button)
end
Alternatively, if you are using Capybara, then you can use the "find" method:
find("#product-2").find("button").click
find(:xpath, "//div/div/button").click

Resources