describe 'JavaScript errors', :js => true do
context 'home page' do
it 'does not raise a JavaScript error' do
visit root_path
fill_in "country", :with => 'Uni'
end
end
end
Minimum Viable Test
Slide 22
Slide 22 text
describe 'JavaScript errors', :js => true do
context 'home page' do
it 'does not raise a JavaScript error' do
visit root_path
fill_in "country", :with => 'Uni'
end
end
end
Minimum Viable Test
Slide 23
Slide 23 text
it 'shows off the Capybara DSL' do
visit '/path'
# use text or DOM ID
click_on('Login')
fill_in('Username',
:with => 'RailsConf')
page.should have_css('button.red')
# Finders wait and retry
# if the element isn't found
find('button.red').click
# Run JavaScript in the page context
page.evaluate_script("1 == '1'").should be_true
end
describe 'country autocomplete', :js => true do
it 'shows autocompletions and redirects' do
visit root_path
fill_in "country", :with => 'norw'
find("body").click # HACK
find('.tt-suggestion').click
current_path.should == country_path('NO')
end
end
Slide 26
Slide 26 text
1) country autocomplete with match shows
autocompletions and redirects to the
selected country
Failure/Error: find('.tt-suggestion').click
Capybara::ElementNotFound:
Unable to find css ".tt-suggestion"
Slide 27
Slide 27 text
page.driver.debug
Slide 28
Slide 28 text
No content
Slide 29
Slide 29 text
No content
Slide 30
Slide 30 text
describe 'country autocomplete', :js => true do
it 'shows no results' do
visit root_path
fill_in "country", :with => 'zzz'
# Sometimes it may be easier to
# use JavaScript directly!
js = '$(".tt-suggestions").children().size() == 0'
page.evaluate_script(js).should be_true
end
end
Slide 31
Slide 31 text
expect do
click_on '#some-ajax-thing'
page.should have_css(".some-result-class")
end.to have_clean_global_scope
Slide 32
Slide 32 text
RSpec::Matchers.define :have_clean_global_scope do |*allowed|
match do |event_proc|
# globals_js is a string containing a JavaScript function
# that returns an array of global variable names
before_globals = page.evaluate_script(globals_js)
event_proc.call
after_globals = page.evaluate_script(globals_js)
diff = after_globals - before_globals
# ignore objects in allowed
diff = diff - allowed
diff.empty?
end
end
Slide 33
Slide 33 text
describe 'xss', :js => true do
let(:title) do
"Title window.xss = true;"
end
let(:document) { Document.create(:title => title) }
it "escapes JavaScript" do
visit document_path(document)
find("h1").should have_content(title)
page.evaluate_script('window.xss').should_not be_true
end
end
Slide 34
Slide 34 text
Gotchas
Slide 35
Slide 35 text
it "should show autocomplete without leaking globals" do
# force capybara to wait until the script gets added to the DOM
page.find("script[src*='/assets/swiftype']")
page.find("link[rel='stylesheet'][href*='/assets/swiftype']")
wait_until {
page.evaluate_script('typeof $stjq === "undefined"') == false
}
expect do
click_on '#st-search-input'
# wait for click before filling in the field
sleep(1)
fill_in 'st-search-input', :with => 'content'
wait_for_ajax!('$stjq')
wait_until {
page.has_css?('.swiftype-widget div.autocomplete ul li')
}
end.to have_clean_global_scope
end
Capybara 1
Slide 36
Slide 36 text
Capybara 2
it "should show autocomplete without leaking globals" do
page.find("script[src*='/embed.js']")
page.find("script[src*='/assets/swiftype']")
page.find("link[rel='stylesheet'][href*='/assets/swiftype']")
page.current_scope.synchronize do
if page.evaluate_script("typeof $stjq === 'undefined'")
raise Capybara::ExpectationNotMet.new("Script wasn't true")
end
end
expect do
click_on '#st-search-input'
fill_in 'st-search-input', :with => 'content'
page.has_css?('.swiftype-widget div.autocomplete ul li')
end.to have_clean_global_scope
end
Slide 37
Slide 37 text
# Use Capybara's waiting mechanism until a
# JavaScript expression evaluates to true.
def synchronize_javascript(script)
current_node.synchronize do
unless evaluate_script(script)
raise ExpectationNotMet.new("Script wasn't true")
end
end
end
Kyle VanderBeek
http://bit.ly/YrBG7t
Slide 38
Slide 38 text
No content
Slide 39
Slide 39 text
Transactions and database setup
Some Capybara drivers need to run against an
actual HTTP server. Capybara takes care of this
and starts one for you in the same process as
your test, but on another thread. Selenium is
one of those drivers, whereas RackTest is not.
– Capybara README
Slide 40
Slide 40 text
Next steps: JavaScript unit testing
describe("recursive factorial",
function() {
it("is correct", function() {
expect(factorial(10)).toBe(3628800);
});
});
Slide 41
Slide 41 text
No content
Slide 42
Slide 42 text
Full stack integration testing with Rails 3,
Cucumber, RSpec, QUnit and Capybara
Matthew O'Riordan, April 17, 2011
blog.mattheworiordan.com/post/4701529828/
Slide 43
Slide 43 text
No content
Slide 44
Slide 44 text
Photo: Noah Scalin
modeset/teabag
Slide 45
Slide 45 text
$ teabag
Starting the Teabag server...
Teabag running default suite at http://127.0.0.1:52681/(...)
FFF
Failures:
1) Factorial without memoization base case is correct
Failure/Error: Expected 0 to be 1.
2) Factorial without memoization recursion case is correct
Failure/Error: Expected 0 to be 3628800.
3) Factorial with memoization has the same result as the (...)
Failure/Error: Expected 3628800 to be 0.
Finished in 0.00500 seconds
3 examples, 3 failures