created data did not appear when testing - ruby-on-rails

hye, I'm new to BDD testing and testing particularly. I'm using rspec, factory_girl_rails, selenium and capybara gem. I want to test for editing a data and save it like so :
it "edit consultant" do
# c = FactoryGirl.create(:consultant)
c = Consultant.create(
orgorcom: "xyz",
year_id: 8,
bidang: "IT & Networking",
project: "E-Sw",
professional_fee: "1000000",
role: "Database Architect",
user_id: 19,
nopkj: "075899 "
)
visit "/consultants?year=#{Time.now.year}"
string = "#consultant#{c.id}"
page.find(string).click_link('Edit')
fill_in "Organization / Company" , :with => c.orgorcom + "aaa"
fill_in "Field", :with => c.bidang + "aaa"
fill_in "Project" , :with => c.project + "aaa"
fill_in "Role", :with => c.role + "aaa"
fill_in "Professional fee", :with => c.professional_fee + 111
click_button "Save"
expect(page).to have_content "Data updated."
sleep 10
# c.destroy
# find('a[href="/consultants/6144/edit?year=2013"]').click
end
But the data I created did not appear & I get this message
1) the consultation edit consultant
Failure/Error: page.find(string).click_link('Edit')
Capybara::ElementNotFound:
Unable to find css "#consultant6157"
when I tried click on existing data like below, it passed.
page.find("#consultant6144").click_link('Edit')
I am able to print out the consultant id but still the record mysteriously did not appear before the test ends (which the db will rollback).

This is a known issue if you're using Selenium for browser testing, with transactional fixtures configured in your spec_helper. Selenium will run in a different thread and with a different database connection than the one being used by Rspec to create your database objects inside transactions, therefore the Selenium thread won't be able to see them.
For these kind of specs, you will need to not use transactional fixtures and use something like database_cleaner to create objects at the start of your specs and truncate/delete them afterwards.

Your problem doesn't seem related to FactoryGirl. The error message thrown by Capybara indicates that the object's id was properly concatenated into the string, but the corresponding CSS wasn't found in the page.
I would recommend you install the launchy gem by inserting this into your Gemfile:
group :development, :test do
gem 'launchy'
end
That will enable you to use the save_and_open_page method in your feature specs. Then insert this method right before the line that's provoking the error, like so:
# ...
save_and_open_page
page.find(string).click_link('Edit')
# ...
When save_and_open_page is called, your default browser will open at the same page. Then you can manually inspect the page's HTML and find out if it does, in fact, have the element you're looking for and, if it doesn't, why that happens.

Related

Teardown called in the middle of a test Capybara

I have been trying to create the following test :
Edit a model (client side), check if the view is updated and if the model changed in database.
there is the code :
test 'a' do
user = User.joins(:organization_users).find_by organization_users: { role: OrganizationUser.roles.values_at(:ORGANIZER, :ADMINISTRATOR) }
sign_in_user user
criterion = create(:criterion, scoring_id: #scoring.id, name: "Test criterion name",
description: "Test description")
step = create(:step, criterion_id: criterion.id)
visit "scorings/" + (#scoring.id).to_s + "/criteria"
find("#criteria > div > div > a > i").click()
fill_in 'name', with: 'New name'
fill_in 'description', with: 'New description'
find('#criterion-modal > div:nth-child(2) > form > div:nth-child(4) > input').click()
criterion = criterion.reload
assert criterion.name == 'New name'
end
`
Driver :
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new app , { phantomjs: Phantomjs.path }
end
Capybara.javascript_driver = :poltergeist
Capybara.current_driver = Capybara.javascript_driver
Teardown :
teardown do
DatabaseCleaner.clean
ActiveRecord::Base.connection.close
Capybara.reset_sessions!
end
As you can see at the end of the test i reload the criterion, but when i do that the teardown function is called. After that the Database is cleaned and i get the error "cant find criterion id:1". I'm only using minitest , factory girl and Capybara. So what i want to understand is why Teardown is called since its not the end of the test and how can i fix that ?
Thank you.
You don't show what you have setup for your teardown method, nor do you specify what driver you are using with Capybara. However, since the test code and the teardown are run in the same thread there really is no way for the teardown to run before the test has ended. What is possible (when using a JS capable driver, where clicks are processed asynchronously) is for the teardown to run before a click is processed/handled by the app code. That would mean the "cant find criterion id:1" would actually be coming from your controller code. The reason for this is that you're not actually checking for anything on the page to change after clicking so the test just keeps on moving, finishes (failing the assertion), the teardown cleans and the controller action can't find the record. Something like
assert_text 'Criterion updated' # if a message is displayed on successful update
or
assert_current_path("scorings/#{#scoring.id}") # whatever path it redirects to after updating
after the click and before your reload
On a side note - Using long selectors like '#criterion-modal > div:nth-child(2) > form > div:nth-child(4) > input' will lead to really brittle tests -- It would be much nicer to use simpler selectors or the capybara click_button type helpers if possible

Rails + Capybara + Braintree—how to feature test BT's Hosted Fields?

TL;DR—How do I access fields within Braintree's Hosted Fields' iframes?
I want to test a UX flow of paying a donation through Braintree. This is my code so far:
require "rails_helper"
RSpec.feature "Donation Module", type: :feature do
scenario "Public visitor creates a new donation" do
#load page
website = create(:website)
Capybara.current_session.driver.header 'Referer', website.website
visit "/donate?t=#{website.public_token}&frame=1"
#verify page loaded
expect(page).not_to have_content("Are you sure you're installing this on the correct website?")
#fill page 1
find("input[value='20']").click
#go to page 2
find("#credit-details").click
#verify page 2 content is loaded
expect(find(".total-cost-text")).to be_visible
#fill page 2
fill_in 'First Name', with: 'Leeroy'
fill_in 'Last Name', with: 'Jenkins'
fill_in 'Email for receipt', with: 'new_donor#email.com'
within_frame('#braintree-hosted-field-number') do
fill_in '#credit-card-number', with: '4111-1111-1111-1111'
end
within_frame('#braintree-hosted-field-expirationDate') do
fill_in '#expiration', with: '09/19'
end
within_frame('#braintree-hosted-field-cvv') do
fill_in '#cvv', with: '123'
end
find('Make payment').click
# expect to make a new user, new donation, new receipt, email receipt
end
end
Currently, it's breaking at the first within_frame saying Capybara::NotSupportedByDriverError:
Capybara::Driver::Base#within_frame
How do I access fields inside BT's iframes?
Well, I am writing here not exactly an answer to this question, but rather corrections to the question, as I was in the similar situation and was facing similar errors such as Selenium::WebDriver::Error::NoSuchFrameError: Unable to locate frame: #braintree-hosted-field-number and Test::Unit::Capybara::ElementNotFound: Unable to find field "#credit-card-number".
The within_frame should have the following format (the #-sign for ID should be removed from both):
within_frame('braintree-hosted-field-number') do
fill_in 'credit-card-number', :with => number
end
And in order to use the selenium driver in Test::Unit, I used the following helper:
def js
Capybara.current_driver = Capybara.javascript_driver
yield
Capybara.current_driver = Capybara.use_default_driver
end
And then wrapped my tests in it:
class SomeTest < ActionDispatch::IntegrationTest
test "should ..." do
js do
within_frame('braintree-hosted-field-number') do
fill_in 'credit-card-number', :with => number
end
# ...
end
end
Hopefully, someone will find it useful while using Unit Tests.
Seems like you're using the rack_test driver? That doesn't support JS or frames so braintree isn't going to work with that. You need to switch to one of the real browser drivers like selenium, capybara-webkit, or poltergeist.

Rspec with Capybara sometimes not pass tests

I have problem with testing Rails Application. My tests generally work perfectly. But sometimes tests will fail when I type some features test for modal bootstrap window, or notify with success/error [js]. How can I resolve this problem ?
I'm using Rspec, Capybara, Rails4.2, PhantomJs, Poltergeist as JS driver. Tests is running locally and in Wercker. In test mode, every bootstrap animation is disabled. What perhaps I do wrong ?
Test:
scenario 'return deutsch default title' do
find('.f-edit-item', match: :first).click
find('a', :text => 'Lang').click
find('a', :text => t('menu.languages.de')).click
find('.f-reset-button', match: :first).click
expect(page).to have_field('menu_item[title]', with: 'Exhibitions_de')
end
Output:
Objects Restore Language restore title translations exist for deutsch translation return deutsch default title
Failure/Error: expect(page).to have_field('object_item[title]', with: 'Exhibitions_de')
expected to find field "object_item[title]" with value "Exhibitions_de" but there were no matches. Also found "", "", which matched the selector but not all filters.
When I click manually, everything is working. When I run this test, sometimes passed, sometimes not. Form is in bootstrap modal. Curiosity: When I add save_and_open_page before find('.f-reset-button', match: :first).click test is passed always(5x in a row)
Because the tests are to do with a Bootstrap modal, my guess is that the test is searching the page for the matching elements, BEFORE the modal has loaded in the DOM.
Edit: As #TomWalpole pointed out, it should be enough to override Capybara's max wait time like so:
expect(page).to have_field('menu_item[title]', with: 'Exhibitions_de', wait: 1.0)
But if you are loading the contents of your modal via AJAX, you may need to force a wait for AJAX to complete the expect line. Here is a good guide on how to do this.
Specifically you need:
# spec/support/wait_for_ajax.rb
module WaitForAjax
def wait_for_ajax
Timeout.timeout(Capybara.default_wait_time) do
loop until finished_all_ajax_requests?
end
end
def finished_all_ajax_requests?
page.evaluate_script('jQuery.active').zero?
end
end
RSpec.configure do |config|
config.include WaitForAjax, type: :feature
end
And then your test would become:
scenario 'return deutsch default title' do
find('.f-edit-item', match: :first).click
find('a', :text => 'Lang').click
find('a', :text => t('menu.languages.de')).click
find('.f-reset-button', match: :first).click
wait_for_ajax
expect(page).to have_field('menu_item[title]', with: 'Exhibitions_de')
end

How to test a form Reset button

Googling around and not find a working solution...
Using Rails 3.2.11, RSpec 2.12.2 Capybara 2.0.2 I'm trying to test the "reset button" on a form (which works fine when testing with a browser)
I tried this code
scenario "Fill form then reset it" do
visit contact_path
fill_in 'message_name', :with => 'abc'
fill_in 'message_email', :with => 'abc'
fill_in 'message_subject', :with => 'abc'
click_on 'Reset form'
expect(page).to find_field('message_name').value.should == ''
end
The test fails with this error
expected: ""
got: "abc"
(compared using ==)
It seems the fields are not resetted at all.. (but in a browser they are)
What am I missing ? Something new in this version of Capybara or Rspec ?
Thanks for your help
My fault,
This type of feature/scenario requires js driver.
Selenium is included in the capybara gem but I forgot to activate it in my feature. To have it to work I just had to change the line
scenario "Fill form then reset it" do
to this one
scenario "Fill form then reset it", :js => true do
according the doc https://github.com/jnicklas/capybara#using-capybara-with-rspec. Now all works fine...
Hope it can help someone else ...
Cheers

Capybara detecting CSS ID model ID with find('')

I am currently working on an intranet for work which will include creating invoices.
While writing tests I am using Rspec and Capybara for testing and while running a request test I want to create an invoice and add a couple of items to make sure the vat is calculated correctly.
However I seem to be having an issue where Capybara finds the first #item_price and tries to match that instead of the newly created item.
Below is some of the code in my request test.
...
...
# Laptop for 369.96 inc vat
click_link "Add Item"
fill_in "Quantity", :with => 1
fill_in "Price", :with => 369.96
fill_in "Description", :with => "laptop for 369"
click_button "Create Item"
page.should have_content("Item was successfully created")
find('#item_quantity').should have_content(1)
find('#item_price').should have_content(369.96)
find('#item_description').should have_content("laptop for 369")
find('#item_total').should have_content(369.96)
find('#invoice_subtotal').should have_content(308.30)
find('#invoice_vat').should have_content(61.66)
find('#invoice_total').should have_content(369.96)
# Laptop for 337.53 inc vat
click_link "Add Item"
fill_in "Quantity", :with => 1
fill_in "Price", :with => 337.53
fill_in "Description", :with => "laptop for 337"
click_button "Create Item"
page.should have_content("Item was successfully created")
find('#item_quantity').should have_content(1)
### the offending code below
find('#item_price').should have_content(337.53)
###
find('#item_description').should have_content("laptop for 337")
...
...
I would like to be able to add #item_price_2 where 2 would be the item ID. When doing this and changing find('#item_price').should have_content(337.53) to find('#item_price_#{invoice.id}').should have_content(337.53) I get the exception:
Nokogiri::CSS::SyntaxError:
unexpected '#' after '[#<Nokogiri::CSS::Node:0x007f991889f270 #type=:CONDITIONAL_SELECTOR, #value=[#<Nokogiri::CSS::Node:0x007f991889f3d8 #type=:ELEMENT_NAME, #value=["*"]>, #<Nokogiri::CSS::Node:0x007f991889f9c8 #type=:ID, #value=["#item_price_"]>]>]'
Also changing it to double quotes "#item_price_{invoice.id}" gives me this exception:
undefined local variable or methodinvoice' for #
I'm guessing this is because I'm not using FactoryGirl so invoice isn't set as a method yet. I'm not using FactoryGirl in this occasion because I would like to test how the user would interact with the invoice and items forms.
What would be the best way to fix this problem? Is there a better way of doing what I am trying to do?
Many thanks in advance!
Your guess about invoice not being set yet is correct. After your form is submitted you could set it:
invoice = Invoice.last
Then your specs should pass.

Resources