91

I'm using RSpec2 and Capybara for acceptance testing.

I would like to assert that link is disabled or not in Capybara. How can I do this?

Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121
kriysna
  • 6,118
  • 7
  • 30
  • 30

10 Answers10

147

Another simple solution is to access the HTML attribute you are looking for with []:

find('#my_element')['class']
# => "highlighted clearfix some_other_css_class"

find('a#my_element')['href']
# => "http://example.com

# or in general, find any attribute, even if it does not exist
find('a#my_element')['no_such_attribute']
# => ""

Note that Capybara will automatically try to wait for asynchronous requests to finish, but it may not work in some cases:

Here is one workaround if you are having trouble with assertions on elements that are updated asynchronously:

bowsersenior
  • 12,524
  • 2
  • 46
  • 52
  • I've got a string with the css location, eg. `find('a#my_element[href]')`, would it be possible to retrieve the value of this attribute? Trying with expressions like `find('a#my_element[href]').value` but doesn't seem to work :( – mickael Dec 16 '12 at 04:00
  • @mickael Try `find('a#my_element[href]').text` or `find('a#my_element[href]').native` . Let me know if either of those give the results you expect. – bowsersenior Dec 17 '12 at 18:59
  • 1
    I realise that these comments are really old, but used it this way: page.find('#my_element')['href=""'] and it worked a treat – Dono Feb 17 '14 at 16:24
  • This will not wait for the query to become true if you want to make an assertion about an asynchronous change. – sj26 Feb 23 '15 at 05:55
  • Thanks @sj26, I updated the answer with some info on asynchronous updates. – bowsersenior Feb 23 '15 at 23:00
93

How are you disabling the link? Is it a class you're adding? An attribute?

# Check for a link that has a "disabled" class:
page.should have_css("a.my_link.disabled")
page.should have_xpath("//a[@class='disabled']")

# Check for a link that has a "disabled" attribute:
page.should have_css("a.my_link[disabled]")
page.should have_xpath("//a[@class='disabled' and @disabled='disabled']")

# Check that the element is visible
find("a.my_link").should be_visible
find(:xpath, "//a[@class='disabled']").should be_visible

The actual xpath selectors may be incorrect. I don't use xpath often!

idlefingers
  • 31,659
  • 5
  • 82
  • 68
4

I recommend using have_link and find_link(name)[:disabled] in two separate assertions. While performing the second assertion alone is simpler, this makes error messages about missing links look nicer, making your test results easier to read.

expect(page).to have_link "Example"
expect(find_link("Example")[:disabled]).to be false

Note that "Example" can be changed to the name or id of the link.

Nick McCurdy
  • 17,658
  • 5
  • 50
  • 82
4

It was a bit messy to find out the correct xpath, here is the correct one,
using capybara 0.4.1.1

# <a href="/clowns?ordered_by=clumsyness" class="weep">View Clowns</a>  

page.should have_xpath("//a[@class='weep'][@href='/clowns?ordered_by=clumsyness']", :text => "View Clowns")

If you only have a link without a class, use

page.should have_link('View Clowns', :href => '/clowns?ordered_by=clumsyness')

Something like this will sadly not work:

page.should have_link('This will not work!', :href => '/clowns?ordered_by=clumsyness', :class => "weep")

The class option will be ignored.

Clint
  • 183
  • 5
1

Whenever possible, you should try to use the Capybara provided wrappers which will work more consistently across drivers.

For the particular case of disabled, a wrapper was introduced in 2.1: https://github.com/jnicklas/capybara/blob/fc56557a5463b9d944207f2efa401faa5b49d9ef/History.md#version-210

If you use it, you will get sensible results on both RackTest and Poltergeist:

HTML:

<input type="text" id="disabled-false"            ></div>
<input type="text" id="disabled-true"     disabled></div>
<input type="text" id="disabled-js-true"          ></div>
<input type="text" id="disabled-js-false" disabled></div>
<script>
  document.getElementById('disabled-js-true').disabled = true
  document.getElementById('disabled-js-false').disabled = false
</script>

Tests:

!all(:field, 'disabled-false',    disabled: false).empty? or raise
 all(:field, 'disabled-false',    disabled: true ).empty? or raise
 all(:field, 'disabled-true',     disabled: false).empty? or raise
!all(:field, 'disabled-true',     disabled: true ).empty? or raise
 all(:field, 'disabled-js-true',  disabled: true ).empty? or raise
 all(:field, 'disabled-js-false', disabled: false).empty? or raise

Capybara.current_driver = :poltergeist
!all(:field, 'disabled-false',    disabled: false).empty? or raise
 all(:field, 'disabled-false',    disabled: true ).empty? or raise
 all(:field, 'disabled-true',     disabled: false).empty? or raise
!all(:field, 'disabled-true',     disabled: true ).empty? or raise
!all(:field, 'disabled-js-true',  disabled: true ).empty? or raise
!all(:field, 'disabled-js-false', disabled: false).empty? or raise

Note how by using this instead of CSS selectors, the Javascript tests will work without any changes if you start using a Js capable driver.

Runnable test file here.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
1

Simply you can use page.has_css? method

page.has_css?('.class_name') 

this will return true if element exists.

Do some action based on validation.

page.has_css?('.class_name') do
  #some code
end
Aravin
  • 6,605
  • 5
  • 42
  • 58
  • 1
    In an RSpec example, the validation block can be silently ignored by accident, so make sure it's passed to the matcher and not the expect: `expect(page).to(have_css('.class_name') { |element| ... })` – Henry Blyth Jul 07 '23 at 16:47
1
page.should have_link('It will work this way!', {:href => '/clowns?ordered_by=clumsyness', :class => "smile"})

have_link expects a hash of options which is empty if you do not provide any. You can specify any attributes the link should have - just make sure you pass all the options in ONE hash.

Hope this helps

PS: For attributes like data-method you have to pass the attribute name as a string since the hyphen breaks the symbol.

kaikuchn
  • 107
  • 1
  • 1
0

bowsersenior, thanks for a hint

Another simple solution is to access the HTML attribute you are looking for with []

Here is an example:

let(:action_items) { page.find('div.action_items') }

it "action items displayed as buttons" do
  action_items.all(:css, 'a').each do |ai|
    expect(ai[:class]).to match(/btn/)
  end
end
18augst
  • 542
  • 7
  • 13
0

Using Rspec3's syntax i did it this way:

expect(page).not_to have_selector(:link_or_button, 'Click here')
Camilo Sad
  • 101
  • 1
  • 4
0

According to the docs you can use the [attribute] accessor syntax:

find('#selector')['class'] => "form-control text optional disabled"

For disabled, you could also do this:

expect(find('#selector').disabled?).to be(true)
pixelearth
  • 13,674
  • 10
  • 62
  • 110