Testing for specific link and its value

OK; I found a bug in my app wherein a link made an incorrect reference. Off I go to write a test but turns out this isn’t so easy. The code is simple:

<%= link_to 'mylist', '/wrong_place' %>

which of course generates:

<a href="/wrong_place">mylist</a>

What I want to do is find the link based on the contained text ‘mylist’ and then test for the href value that I expect to be there.

From what I can tell, there’s no way to do this with CSS selectors: ‘mylist’ is a text element contained in the ‘a’ tag and if I search for that element value, there’s no way to move “up” to its parent, which is where the href attribute lives that I want to test. There was a :contains selector proposed for CSS3 which was removed; various posts claim that it was actually implemented in most browsers, but it doesn’t work in rspec.

Unless I’ve fallen down a rabbit hole and can’t see the obvious solution (entirely possible :=), the only way I figure I can test this is by adding an attribute (class, id, …) to the link which I can then use to select the link and test for the desired href value.

What’s the obvious answer?

We can use the Capybara have_css matcher:

expect(page).to have_css "a[href='/wrong_place']", text: 'mylist' 

Thanks very much, @harlow… that works now in my feature spec. I know I tried that at some point, but I guess there were other problems with my test that prevented it from working.

Edit: So I got the selection with two items working by using the class of the items (one’s inside my nav/menus).

However, I’m running into more questions about have_css. On my home page, the user has to be logged in to view the ‘mylist’ page; if they’re not, they get a flash-error message and remain on the home page. The html on the page is:

<div class="error">You must be logged in to view this page</div>

This expectation works:

expect(page.html).to have_css('div.error', :text => 'You must be logged in to view this page')

while this one fails:

expect(page).to have_css('div.error', :text => 'You must be logged in to view this page')

Capybara docs say to use page.html only for testing, so I’m sure there’s a better way to get this to work without the page.html incantation…?

Here’s more detail about the spec, along with what worked (and didn’t work)…

  scenario "rejects mylist links if not logged in" do
    visit root_path
    ### All these commented-out versions failed
    #find('#nav li > a[href*="hoards"]').click_on
    #click_on find('#nav>ul>li>a[href*="hoards"]')
    #click_on find('#nav>a[href*="hoards"]')
    #click_on find('#nav a[href*="hoards"]')
    #find('#nav').find_link('[href*="hoards"]').click
    #find('#nav').find_link('ul a[href*="hoardss]').click
    #find('#nav').click_link('a[href*="hoards"]')
    #find('#nav').click_link('a[href="/hoards"]')
    within '#nav' do
      click_link 'mylist'
      expect(page.html).to have_css('div.error', :text => 'You must be logged in to view this page')
      ### Don't know why this fails and the above works...?
      #expect(page).to have_css('div.error', :text => 'You must be logged in to view this page')
    end
  end

@JESii what does save_and_open_page show you?

Here’s the pastie, @cyptel… I added the save_and_open page just before the “expect(page.hml)…” inside the “within” clause. I retested both expections just in case I missed something and (page) still fails while (page.html) passes.