Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I test the page title with Capybara 2.0?

Trying to test that page contains <title>My Title</title> with:

# spec/features/reports_spec.rb
require 'spec_helper'

feature "Archive Management" do
  subject { page }

  describe "Index Page" do
    before(:all) { 10.times { FactoryGirl.create(:randomreport) } }
    after(:all) { Report.delete_all }

    describe "when no search terms present" do
      before { visit reports_path }

      it { should have_selector('title', text: 'My Title') } # <= Fails w/Capybara 2.0
      it { should have_selector('title') }                   # <= passes
      it { should have_text('My Title') }                    # <= passes
      it { should have_selector('h2', text: "Welcome") }     # <= passes
    end
  end
end

Error message:

 Failure/Error: it { should have_selector('title', text: base_title) }
 Capybara::ExpectationNotMet:
   expected to find css "title" with text "My Title" but there were no matches. Also found "", which matched the selector but not all filters.

I know I'm overlooking the painfully obvious but can't figure out what it is? Are <title> tags no longer considered 'selectors'?!? Or...?!?

Edit (debugger info):

If I drop down into the debugger as cleverly suggested by @shioyama it is clear that the page.body contains <title>My Title</title>. The same page.body that contains <h2>Welcome to My Title Project</h2> and passes!

It appears to find the <title>...</title> tag but not My Title within it. But it does find My Title later in the page within <a href=\"/\" class=\"brand\">My Title</a> and/or in <h2>Welcome to The My Title Project</h2>:

(rdb:1) p page
#<Capybara::Session>
(rdb:1) p page.body
"<!DOCTYPE html>\n<html>\n<head>\n<title>My Title</title>\n
<meta content='research, report, technology' name='keywords'>\n<meta 
content='Some Project' name='description'>\n<link href=\"/assets/application.css\"
...
</head>\n<body>\n<header class='navbar navbar-fixed-top 
navbar-inverse'>\n<div class='navbar-inner'>\n<div class='container'>\n
<a href=\"/\" class=\"brand\">My Title</a>\n<div class='pull-right'>\n
<ul class='nav'>\n<li><a href=\"/about\">About</a></li>\n<li><a href=\"/help\">Help</a>
</li>\n</ul>\n<form accept-charset=\"UTF-8\" action=\"/reports\" 
class=\"navbar-search\" method=\"get\"><div style=\"margin:0;padding:0;display:inline\">
<input name=\"utf8\" type=\"hidden\" value=\"&#x2713;\" /></div>\n
<input class=\"search-query\" id=\"query\" name=\"query\" 
placeholder=\"Search\" type=\"text\" />\n</form>\n\n</div>\n</div>\n</div>\n</header>\n\n
<div class='container'>\n<div class='hero-unit center'>\n<h1>My Title</h1>\n
<h2>Welcome to The My Title Project</h2>\n<p>Lorem ipsum dolor sit amet, consectetur 
adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
...

What else could I try in the debugger to figure out why have_selector?('title', text: ...) is failing?

So, what is the proper way to test for a title in Capybara 2.0?

like image 815
Meltemi Avatar asked Nov 26 '12 21:11

Meltemi


4 Answers

I had the same issues when I upgraded to Capybara 2.0, and managed to solve them by creating the following custom RSpec matcher using Capybara.string:

spec/support/utilities.rb

RSpec::Matchers::define :have_title do |text|
  match do |page|
    Capybara.string(page.body).has_selector?('title', text: text)
  end
end

Now, in a spec file where subject { page }, I can use:

it { should have_title("My Title") }
it { should_not have_title("My Title") }

As an aside, the conversation on this question was extremely helpful in getting me to this answer, so thank you!

Update 1:

If you don't want to create a custom RSpec matcher, thanks to this StackOverflow answer by user dimuch I now know that you can call have_selector directly on page.source (aliased page.body) to test for non-visible DOM elements:

it "should show the correct title" do
  page.source.should have_selector('title', text: 'My Title')
end

or where subject { page }:

its(:source) { should have_selector('title', text: 'My Title') }

Update 2:

From Capybara 2.1, there are built-in have_title/has_title? matchers, negating the need for the custom RSpec matcher in this answer. Furthermore, the its(:source) { ... } tests described in Update 1 seem to break under Capybara 2.1; I've confirmed the have_title/has_title? work as expected, so it's probably best to go with the following syntax if you plan on upgrading:

When subject { page }:

it { should have_title("My Title") }
it { should_not have_title("My Title") }

When using expect syntax within an it/scenario block:

expect(page).to have_title("My Title")
expect(page).to_not have_title("My Title")
like image 101
Paul Fioravanti Avatar answered Oct 06 '22 00:10

Paul Fioravanti


Capybara 2.1 changed its support for querying the title element. So using have selector in to query for the title element in the head of the html doc in this fashion will fail "page.should have_selector('title', :text => 'Some text'). From what i understand the preferred method in capybara 2.1 new API is to use "page.should have_title('Some text')" to query the title element should work.

like image 23
elrick Avatar answered Oct 05 '22 23:10

elrick


i ran into the same issue when upgrading capybara from version 1.x to > 2.0

The problem is that Capybara ignores invisible text in its matchers. The cleanest way to address this issue is to use the :visible => false option on finders.

e.g.

it { should have_selector('title', text: 'My Title', visible: false) }

Another (global) option is to set:

Capybara.ignore_hidden_elements = false

like image 45
dfherr Avatar answered Oct 05 '22 22:10

dfherr


I had this problem only when using 'shared_examples_for', maybe because of other changes. I also noticed that when I placed

<title>My Title</title>
in the body of the page (where it doesn't belong and won't render), the test passed. Not a good thing!

This worked:

it{should have_title("My Title")}

(capybara 2.1.0, launchy 2.3.0, nokogiri 1.5.9, rspec 2.13)

like image 45
Noddinoff Avatar answered Oct 06 '22 00:10

Noddinoff