Cucumber Capybara & Rspec

Libraries (documentation)

  1. Rspec Documentation
  2. Capybara Documentation
    1. https://www.rubydoc.info/github/teamcapybara/capybara/master
    2. examples here: https://github.com/teamcapybara/capybara/tree/master/features
    3. https://devhints.io/capybara
    4. http://cheatrags.com/capybara
    5. https://www.cheatography.com/corey/cheat-sheets/capybara-with-rspec/
  3. Factory Girl Documentation
  4. Webmock Documentation
  5. Timecop Documentation
  6. Shoulda Matchers
  7. Fuubar Release

Cucumber Tables:

Cucumber step definitions:

=== CAPYBARA ===

Interacting with page elements

Summary

When interacting with the page, use action methods like click_on instead of finder methods like find whenever possible. Capybara knows the most about what you’re doing with those methods, and can more intelligently handle odd edge cases.

When verifying that elements are on the page as expected, use RSpec matchers like have_css instead of node methods like text whenever possible. Capybara can wait for the result you want with a matcher, but doesn’t know what text you’re expecting when you invoke methods on a node.

CAPYBARA (Library, DSL, CheatSheet)

More resources on Capybara: (if you care to read more):

  • https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara
  • https://quickleft.com/blog/five-capybara-hacks-to-make-your-testing-experience-less-painful/
  • https://www.sitepoint.com/basics-capybara-improving-tests/
  • https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara
  • http://www.slideshare.net/koffeinfrei/functional-testing-with-capybara
  • http://www.rubydoc.info/github/jnicklas/capybara/master/Capybara/Session
  • https://quickleft.com/blog/five-capybara-hacks-to-make-your-testing-experience-less-painful/
  • http://blog.plataformatec.com.br/2011/02/improving-your-tests-with-capybara-custom-selectors/
  • https://www.varvet.com/blog/simple-tricks-to-clean-up-your-capybara-tests/

CAPYBARA SYNTAX and Examples

Make sure to use methods that implicitly wait for an element before doing an action.

Methods that Wait

  • find(selector)find_fieldfind_link, etc.
  • within(selector) (scoping)
  • has_selector?has_no_selector? & assertions
  • form & link actions
    • click_linkclick_button
    • fill_in
    • checkuncheckselectchoose, etc.

Methods that Won’t Wait

  • visit(path)
  • current_path
  • all(selector)
  • first(selector) (counter-intuitively)
  • execute_scriptevaluate_script
  • simple accessors: textvaluetitle, etc.

So the absolute first step in diagnosing a possible race condition, assuming your setup is known to be sound, is to check that you haven’t accidentally used one of these Capybara methods that won’t wait for page content update.

METHODS THAT DO NOT WAIT often forces us to use ‘sleep(x)’, which is bad practice!

see: http://stefan.haflidason.com/testing-with-rails-and-capybara-methods-that-wait-method-that-wont/

Click on Links

When /^I click on New Item$/ do
click_link ('New Item')
end

can then become… to reuse the same step in other scenarios

When /^I click on (.*)$/ do |link|
click_link (link)
end

https://stackoverflow.com/questions/6930927/how-do-i-confirm-a-javascript-popup-with-capybara

example:

page.driver.browser.switch_to.alert.accept
# or
page.driver.browser.switch_to.alert.dismiss
# or
page.driver.browser.switch_to.alert.text

http://www.rubydoc.info/github/jnicklas/capybara/Capybara%2FSession%3Aaccept_confirm

example;

accept_confirm do
click_link 'Destroy'
end

 

FIND ELEMENT by ID and validate content

these work as step definitions:

better to use the newest “expect”… than “page.has…”

GOOD EXAMPLE:

page.has_xpath?('.//*[@data-automation-id="element-id"]', :text => 'Text that appears on page', :visible => true)

BETTER EXAMPLE: only confirms that xpath is present!

expect(page).to have_xpath('.//*[@data-automation-id="element-id"]')

BETTER EXAMPLE: finds the xpath and confirms the content value

expect(find(:xpath,"//*[@data-automation-id='element-id']")).to have_content('Text that appears on page')

Use Within, so you are not scoping the whole page

within(:xpath,"//*[@class='bla-bla-bla']"do
expect(find('element-header')).to have_content(header)
end
OR
within(:xpath,"//*[@class='register-button']"do
should have_link(href: account_creation_url)
end

EXACT TEXT MATCH

or element contains text

see:

NOTE: Capybara won’t consider the content to exist if it is not visible.

Scenario: ‘Exact match‘ example

Given an element on the page with the text string “chocolate cake”

When using ‘expect(content).to eq(cake)‘ step definition

Then my test will Fail because “cake” does not equal to “chocolate cake”

Scenario: ‘Contains text’ example

Given an element on the page with the text string “chocolate cake”

When using ‘should have_content(cake)‘ step definition

Then my test will Pass because “cake” is found within the string “chocolate cake”

1- EXACT MATCH EXAMPLES:

Example with clicking on an element by its name: Exact Match

# Will match links that exactly match 'Password'
click_link('Password':exact => true)
find(:xpath,$PATH['edu_text']).text.should match(eduText)

2- CONTAINS TEXT EXAMPLES: (not exact match)

Examples with clicking on an element by its name: Contains Text

# Will match links that contain the word 'Password' - ex 'Password Confirmation'
click_link('Password':exact => false)

if you use the following: it will check if content exists but not exact content match!

but useful if dictionary labels keep changing, you can assert that only part of the text is present.

should have_content(arg1)

within(scope) do
page.should have_content(content)
end

3- USING CAPYBARA EXACT = TRUE:

ALSO you can use Following for: Capybara.exact = true

expect(page).to have_content(arg1)

OR

expect(tooltiptext).to eq(arg1)

HREF

try if these work:

link = page.find(:css, 'a[href="actual link"]')

find_link('link_text')[:href].should == 'url'
find_link('link_text')[:target].should == '_blank'

https://github.com/jnicklas/capybara

has_link_spec.rb

# frozen_string_literal: true
Capybara::SpecHelper.spec '#has_link?' do
before do
@session.visit('/with_html')
end
it "should be true if the given link is on the page" do
expect(@session).to have_link('foo')
expect(@session).to have_link('awesome title')
expect(@session).to have_link('A link', :href => '/with_simple_html')
expect(@session).to have_link(:'A link', :href => :'/with_simple_html')
expect(@session).to have_link('A link', :href => /\/with_simple_html/)
end
it "should be false if the given link is not on the page" do
expect(@session).not_to have_link('monkey')
expect(@session).not_to have_link('A link', :href => '/non-existant-href')
expect(@session).not_to have_link('A link', :href => /non-existant/)
end
end
Capybara::SpecHelper.spec '#has_no_link?' do
before do
@session.visit('/with_html')
end
it "should be false if the given link is on the page" do
expect(@session).not_to have_no_link('foo')
expect(@session).not_to have_no_link('awesome title')
expect(@session).not_to have_no_link('A link', :href => '/with_simple_html')
end
it "should be true if the given link is not on the page" do
expect(@session).to have_no_link('monkey')
expect(@session).to have_no_link('A link', :href => '/non-existant-href')
expect(@session).to have_no_link('A link', :href => /\/non-existant-href/)
end
end

Capybara doesn’t provide a link_to method so I’m not sure where you’re getting that from (rails provides a link_to for generating links in the view). To find a link based on just its href using capybara you could do

link = page.find(:css, 'a[href="actual link"]')

or if you’re looking to assert that the element exists

page.assert_selector(:css, 'a[href="actual link"]')

or – if using RSpec

expect(page).to have_selector(:css, 'a[href="actual link"]')

Since have link by default searches for substrings in the link text you can also do

expect(page).to have_link(nil, href: 'actual link')

or

page.assert_selector(:link, nil, href: 'actual link')

 

Current HOST, Current PATH, Current URL

Step def:

#  current_host.should == $BASE_URL_SHORT+(localsite)
#  current_path.should == destination
#  current_url.should == $BASE_URL_SHORT+(localsite)+(destination)

or

expect(page).to have_current_path(destination)

EXAMPLES:

EXPECTED: Path to equal "https://complete-url"

with this STEP DEF:

expect(page).to have_current_path(destination, url: true)

RESULT:

expected "https://site/path?var1=this&var2=that"

to equal "/path"

 

with this STEP DEF:

expect(page).to have_current_path(destination, only_path: true)

RESULT:

PASS!

TO PRINT OUT in command prompt:

puts "\n\n current_host --->", current_host
puts "\n\n current_path --->", current_path
puts "\n\n current_url --->", current_url
puts "\n\n current_scope --->", current_scope
RESULT examples:
current_host --->, https://www-staging.yoursite.com,
current_path --->, /qa-automation/fsb-buy/buy-container/individual-plc/acd-popup,
current_url --->, https://www-staging.yoursite.com/qa-automation/fsb-buy/buy-container/individual-plc/acd-popup?plc=ACD&term=1-MONTH&support=BASIC&quantity=1
current_scope --->, #<Capybara::Node::Element:0x000000062b83c8>,

read: http://stackoverflow.com/questions/5228371/how-to-get-current-path-with-query-string-using-capybara

If the current page is /people?search=name but you only care that it’s on the /people page regardless of the param, you can send the only_path option:

expect(page).to have_current_path(people_path, only_path:true)

Additionally, if you want to compare the entire URL:

expect(page).to have_current_path(people_url, url:true)

ASSERT URLs

http://stackoverflow.com/questions/5153550/capybara-assert-attributes-of-an-element

To Try:

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

ASSERT ATTRIBUTES

http://stackoverflow.com/questions/5153550/capybara-assert-attributes-of-an-element

# 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

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
You can actually use already existing methods defined by Capybara matchers.
assert has_no_field?('Username')
Furthermore there are additional methods available the can help you in finding different types of elements in your page
has_link? , has_no_link?
has_button?, has_no_button?
has_field?, has_no_field?
has_checked_field?, has_no_checked_field?
has_select?, has_no_select?
has_checked_field or has_no_check_field
page.should have_no_selector(:xpath,"//*[@data-automation-id='element-id']")

Example: page has CSS

if css style changed between being disabled and not disabled.

ie. when button is enabled, the class = “wd-button ptdlb-download-button wd-uppercase”

when button is disabled, the class = “wd-button ptdlb-download-button wd-uppercase wd-button-disabled”

page.should have_no_css('.wd-button-disabled', :visible => true)
page.should have_css('.wd-button-disabled', :visible => true)

ASSERT ATTRIBUTE IS VISIBLE OR NOT

from: http://stackoverflow.com/questions/8801845/how-to-make-capybara-check-for-visibility-after-some-js-has-run

to try:

assert element is present, regardless of visibility

these might not work though?

page.should have_css('#some_element', :visible => false)

assert visible element is not present

page.should have_no_css('#some_element', :visible => true)

My working examples:

expect (find(:xpath, "//*[@class='wd-font-21 element-name hide']")).should be_visible
find(:css, "#some_element").should be_visible
expect (find(:xpath, "//*[@class='wd-font-21 element-name hide']")).should_not be_visible
find(:css, "#comment_stream_list li[data-id='#{@id3}']").should_not be_visible

_______________________________________________________________________________________________

check out: http://stackoverflow.com/questions/8801845/how-to-make-capybara-check-for-visibility-after-some-js-has-run

or https://www.varvet.com/blog/introducing-capybara-2-1/

 

VISIBILITY

check out: https://makandracards.com/makandra/11133-how-to-test-print-stylesheets-with-cucumber-and-capybara

“LOCAL STORAGE”

We have a before hook already set in place. just add the tag before your scenario

@StorageClear
page.execute_script("localStorage.clear()")

or

page.execute_script('if (localStorage && localStorage.clear) localStorage.clear()')
or
visit($BASE_URL)
Capybara.page.execute_script("localStorage.clear()")

 

Browser SESSION STORAGE

We have a before hook already set in place. just add the tag before your scenario

@SessionStorageClear

here is the code example:

visit($BASE_URL)
Capybara.page.execute_script("sessionStorage.clear()")

 

“DEMANDBASE” (dmdbase_full) in Local Storage

Delete “DMDBASE_FULL” object

1- Function to delete existing ‘dmdbase_full’ key and value

page.execute_script("localStorage.removeItem('dmdbase_full')")

GET “DMDBASE_FULL” object

2- funtion to get/read the value of  ‘dmdbase_full’ value

 you first need to visit the page.

visit('https://site.com)
dmdbase1 = page.evaluate_script("localStorage.getItem('dmdbase_full')")
puts "\n\n dmdbase1 --->",dmdbase

SET “DMDBASE_FULL” object

3- funtion to define new value of ‘dmdbase_full’ from feature file variable named “countrycode” which stores the 2-letter value ex. FR

page.execute_script('localStorage.setItem("dmdbase_full",":false:false:false:false:false:false:'+countrycode+':")')

EXAMPLE with Local Storage Clear:

.feature file
Scenario Outline: test 1
Given page x loads "<dmd>"
Examples:
| dmd   |
| GB    |
| DE    |
step_defs.rb file
Given(/^page x loads "([^"]*)"$/) do |dmd|
####### 1- funtion to clear localStorage ############
visit('https://site.com/page')
#  Capybara.page.execute_script("localStorage.clear()")
page.execute_script('if (localStorage && localStorage.clear) localStorage.clear()')
f
####### 2- funtion to get existing value of dmdbase_full ############
visit('https://site.com/page')
dmdbase1 = page.evaluate_script("localStorage.getItem('dmdbase_full')")
#  puts "\n\n dmdbase1 --->",dmdbase1
#  puts "\n\n"
####### 3- funtion to delete existing dmdbase_full ############
page.execute_script("localStorage.removeItem('dmdbase_full')")
####### 4- funtion to define new value of dmdbase_full from feature file if using an examples data table ############
country = page.execute_script('localStorage.setItem("dmdbase_full",":false:false:false:false:false:false:'+dmd+':")')
#  puts "\n\n dmd --->",dmd
#  puts "\n\n country --->",country
####### 5- funtion to get value of new dmdbase_full ############
visit('https://yoursite.com/page')
#    sleep 10
dmdbase2 = page.evaluate_script("localStorage.getItem('dmdbase_full')")
#  puts "\n\n dmdbase2 --->",dmdbase2
end

 

COOKIES

se the gem “show_me_the_cookie” included in the GEMFILE.

see:

gem install show_me_the_cookies

examples:

API
# puts a string summary of the cookie
show_me_the_cookie(cookie_name)
# returns a hash of the cookie
# form: {:name, :domain, :value, :expires, :path}
get_me_the_cookie(cookie_name)
# puts a string summary of all cookies
show_me_the_cookies
# returns an array of cookie hashes
# form: [{:name, :domain, :value, :expires, :path, :secure}]
get_me_the_cookies
# deletes the named cookie
delete_cookie(cookie_name)
# removes session cookies and expired persistent cookies
expire_cookies
# creates a cookie
create_cookie(cookie_name, cookie_value)
# creates a cookie for the path or domain
create_cookie(cookie_name, cookie_value, :path => "...", :domain => "...")

also

Show_me_the_cookie gem has the following methods.

We can also create helper methods such as:

if cookie exist or does not exist:

the use the function:

cookies_should_contain(key, value)
or
cookies_should_not_contain(key, value)

see: https://github.com/nruth/show_me_the_cookies/blob/master/spec/spec_helper.rb

wondering if we already have this as part of the gem install though. might be worth checking out.

 

Reload Page

page.evaluate_script("window.location.reload()")

Maximize Browser Window

Capybara.page.current_window.maximize

Resize Browser Window

Capybara.page.current_window.resize_to(414736)

SELECT OPTION FROM DROPDOWN

OPTION 1:

Basically here we are looking into a specific path in which we are looking to find a path option[2], meaning the second element from the dropdown list as an example.

The first example below is looking for a reference from the xpath.yml file. You can either go with option 1 or option 2 which is by adding a direct path there.

  1. find(:xpath,$PATH[‘your_path_reference’])
  2. find(‘#yourPath’)
find(:xpath,$PATH['your_path_reference']).find(:xpath, 'option[2]').select_option

OPTION 2:

using value of element in dropdown and name of dropdown vs xpath:

Gherkin and step definition:

Example 1 with a “examples data table” if you have more than one variation to test:

When I select my "<Country>" from the dropdown
Examples:
| Country       | CountryCode |
| Canada        | CA          |
| France        | FR          |
---
When(/^I select my "([^"]*)"$/) do |country|
select(country, :from => 'element-id')
end
Explanation:
select(Canada, :from => 'name of dropdown')

Example 2 with an argument:

When I select "canada" from the dropdown
When(/^I select "([^"]*)"$/) do |arg1|
select(arg1, :from => 'element-name-or-id)
end

OPTION 3: to be tested out if still works, as post was 6 years old and Capybara syntax may have changed

From: https://makandracards.com/makandra/864-test-that-a-select-option-is-selected-with-cucumber

also check out: https://makandracards.com/makandra/996-test-that-a-select-field-contains-an-option-with-cucumber

Example:

Then /^"([^"]*)" should be selected for "([^"]*)"(?: within "([^\"]*)")?$/ do |value, field, selector|
with_scope(selector) do
field_labeled(field).find(:xpath, ".//option[@selected = 'selected'][text() = '#{value}']").should be_present
end
end

Check / Uncheck a ‘checkbox’

# Check
find(:css, "#cityID[value='62']").set(true)
# Uncheck
find(:css, "#cityID[value='62']").set(false)

Checkbox is checked or Not checked

checkbox = find(:xpath,$PATH['checkbox-id'])
checkbox.should be_checked
checkbox = find(:xpath,$PATH['heckbox-id'])
checkbox.should_not be_checked

 

POPUPS & ALERTS

should have a “if” statement

references:

https://stackoverflow.com/questions/2458632/how-to-test-a-confirm-dialog-with-cucumber

https://stackoverflow.com/questions/6930927/how-do-i-confirm-a-javascript-popup-with-capybara

https://github.com/teamcapybara/capybara/issues/29

http://toolsqa.com/selenium-webdriver/handling-of-alerts-javascript-alerts-and-popup-boxes/ (this is Java code but can likely re-use the selenium code)

 

Infinite scroll

see: http://www.tweetegy.com/2013/05/testing-infinite-scroll-using-rspec-without-sleep-or-wait-until/

 

=== RSPEC ====

identifying and verifying that elements are on the page as expected

rspec (rspec guidelines with ruby)

TESTING API

Use RSPEC and not Capybara for testing APIs

RSPEC MATCHERS

see: https://github.com/rspec/rspec-collection_matchers

EXAMPLE:

Basic usage

First of all, you need to require rspec-collection matchers. Add the following line to your spec_helper.rb:

require 'rspec/collection_matchers'

Using rspec-collection_matchers you can match the number of items in a collection directly, e.g.:

it 'matches number of items in a collection' do
  expect([1,2,3]).to have_at_least(3).items
end

You can also match the number of items returned by a method on an object, e.g.:

class Cart
  def initialize(*products)
    @products = products
  end
  attr_reader :products
end

it 'matches number of items returned from a method' do
  cart = Cart.new('product a', 'product b')
  expect(cart).to have_at_most(2).products
end

The last line of the example expresses an expected outcome: if cart.products.size <= 2 then the example passes, otherwise it fails with a message like:

expected at most 2 products, got 3
 

Available matchers

expect(collection).to have(n).items
expect(collection).to have_exactly(n).items
expect(collection).to have_at_most(n).items
expect(collection).to have_at_least(n).items

more ?

#Model 

@user.should have(1).error_on(:username) # Checks whether there is an error in username 
@user.errors[:username].should include("can't be blank")  # check for the error message 

#Rendering
response.should render_template(:index)

#Redirecting 
response.should redirect_to(movies_path)

#Capybara Matchers

response.body.should have_content("Hello world")
response.body.should have_no_content("Hello world")

response.body.should have_css("input#movie_title")
response.body.should have_css("input#movie_title", :value => "Twelve Angry Men")
response.body.should have_css("input", :count => 3) #True if there are 3 input tags in response
response.body.should have_css("input", :maximum => 3) # True if there or fewer or equal to 3 input tags 
response.body.should have_css("input", :minimum => 3) # True if there are minimum of 3 input tags 
response.body.should have_css("input", :between => 1..3) # True if there 1 to 3 input tags 
response.body.should have_css("p a", :text => "hello") # True if there is a anchor tag with text hello
response.body.should have_css("p a", :text => /[hH]ello(.+)/i) 
                                                   # True if there is a anchor tag with text matching regex

response.body.should have_xpath("//a")
response.body.should have_xpath("//a",:href => "google.com")
response.body.should have_xpath("//a[@href => 'google.com']")
response.body.should have_xpath("//a[contains(.,'some string')]")
response.body.should have_xpath("//p//a", :text => /re[dab]i/i, :count => 1)


# can take both xpath and css as input and can take arguments similar to both have_css and have_xpath
response.body.should have_selector(:xpath, "//p/h1")
response.body.should have_selector(:css, "p a#movie_edit_path")

# For making capybara to take css as default selector
Capybara.default_selector = :css
response.body.should have_selector("input")   #checks for the presence of the input tag
response.body.should have_selector("input", :value =>"Twelve Angry Men") # checks for input tag with value
response.body.should have_no_selector("input") 

# For making capybara to take css as default selector
Capybara.default_selector = :xpath
response.body.should have_selector("//input")   #checks for the presence of the input tag
response.body.should have_selector("//input", :value =>"Twelve Angry Men") # checks for input tag with value


# To access elements inside form 
response.body.should have_field("FirstName")  # checks for presence of a input field named FirstName in a form
response.body.should have_field("FirstName", :value => "Rambo")
response.body.should have_field("FirstName", :with => "Rambo")

response.body.should have_link("Foo")
response.body.should have_link("Foo", :href=>"googl.com")
response.body.should have_no_link("Foo", :href=>"google.com")


 

Hash rocket is not a valid XPath symbol (it leads to “Invalid expression” exception).

Please use an “equals” sign: it should be “//a[@href = ‘google.com’]” instead of “//a[@href => ‘google.com’]”.

However, regarding response.body.should have_css("input#movie_title", :value => "Twelve Angry Men"), :value is not actually a valid key for have_css, and :text doesn’t actually match against the value attribute of input fields I’ve found.

You can either do something like have_css("input#movie_title[value=\"Twelve Angry Men\"]"), or user the have_field and :with key.

This works but using page.should instead of response.body.should

You’d better use the new :expect syntax like expect(page).to have_

With capybara 2.4.4, if you want to find a field with a value, use the following

  expect(page).to have_field("My Field", with: "the value in the field")