I'm building a setup screen for billing of individuals. The controller/views are in the Admin namespace.
When run the first test without :js => true I get one failure, which I assume is down to the fact that the link does not work as its a helper which uses a js script to build a nested set of fields (Based on Railscasts Single form, multiple tables - Nested Attributes).
Failures:
1) Patient Setup create patient bill heading - with extended details -with valid data
Failure/Error: fill_in "Extended Bill Heading", :with => 'Regular Registration'
Capybara::ElementNotFound:
cannot fill in, no text field, text area or password field with id, name, or label 'Extended Bill Heading' found
# (eval):2:in `fill_in'
# ./spec/requests/admin/patient_setup_pages_spec.rb:52:in `block (4 levels) in <top (required)>'
Finished in 0.83047 seconds
3 examples, 1 failure, 2 pending
But when I use :js = true, I'm getting a failure which appears to be from the login with Invalid user/password flashing up on the screen when running.
Failures:
1) Patient Setup create patient bill heading - with extended details -with valid data
Failure/Error: click_link 'System'
Capybara::ElementNotFound:
no link with title, id or text 'System' found
# (eval):2:in `click_link'
# ./spec/requests/admin/patient_setup_pages_spec.rb:22:in `block (2 levels) in <top (required)>'
Finished in 6.33 seconds
3 examples, 1 failure, 2 pending
Here is the code that backs all this up.
spec/requests/admin/patient_setup_spec.rb
require 'spec_helper'
feature 'Patient Setup' do
let!(:ci_user) { FactoryGirl.create(:user,
name: "configuration engineer",
password: "password",
password_confirmation: "password"
) }
let!(:admin_role) { FactoryGirl.create(:admin_role) }
let!(:assignment) { FactoryGirl.create(:assignment,
:role => admin_role,
:user => ci_user
) }
before do
visit login_path
fill_in "Name", with: "configuration engineer"
fill_in "Password", with: "password"
click_button "Login"
save_and_open_page
click_link 'System'
click_link 'Patient Setup'
end
describe "create patient bill heading" do
before do
click_link 'New Bill Heading'
fill_in 'Bill heading', :with => 'Consultation'
fill_in 'Abbreviation', :with => "CON"
end
context "- no extended details" do
pending
scenario "- with valid data" do
pending
click_button 'Create Patient bill heading'
page.should have_content('Patient Bill Heading created.')
end
end
context "- with extended details", :js => true do #(without :js => true 1st error)
before do
# save_and_open_page
click_link "Extended Bill Heading"
# save_and_open_page
end
scenario "-with valid data" do
save_and_open_page
fill_in "Extended Bill Heading", :with => 'Regular Registration'
end
end
end
end
Here is my factory setup.
spec/factories.rb
FactoryGirl.define do
# Users, Roles
factory :user do
name "Joesephine Bloggs"
password "testmenow"
password_confirmation "testmenow"
end
factory :admin, :class => User do
sequence(:name) { |n| "Administrator-#{n}" }
password "adminiam"
password_confirmation "adminiam"
after(:create) do |user|
FactoryGirl.create(:assignment, :role => FactoryGirl.create(:admin_role), :user => user )
end
end
factory :role do
description { "Clerical-#{rand(99)}" }
factory :admin_role do
description "Admin"
end
end
factory :assignment do
user
role
end
# Patients Module
factory :patient_bill_heading do
sequence(:bill_heading) { |n| "bill-heading-#{n}" }
sequence(:abbreviation) { |n| "abbreviation-#{n}" }
factory :delete_patient_bill_heading, :class => PatientBillHeading do
bill_heading :bill_heading
abbreviation :abbreviation
end
end
end
Here is the link in my view to call the helper that generates the nested attribute fields.
<p>
<%= link_to_add_fields "Extended Bill Heading", f, :patient_extended_bill_headings %>
</p>
And here is the helper.
helpers/application_helper.rb
def link_to_add_fields(name, f, association, options={})
defaults = {
:partial_name => nil
}
options = defaults.merge(options)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
if options[:partial_name].nil?
render(association.to_s.singularize + "_fields", f: builder)
else
render(options[:partial_name], f: builder)
end
end
link_to("#{name} <i class='icon-plus icon-white'></i>".html_safe,
'#',
class: "btn btn-success add_fields",
data: {id: id, fields: fields.gsub("\n", "")}
)
end
I'm trying to improve my RSpec testing knowledge, as I have successfully built this working in my application after an hour trying to figure out why I was getting test failures. So in the app it works, but I want to understand how to make my test pass.
Is my assertion that one error is due to using js to create the link, and capybara is not running it as I don't use the :js => true option?
Can anybody see what I'm doing wrong when using the :js => true option?
You probably have config.use_transactional_fixtures = true in your spec_helper.rb. It doesn't work with Capybara javascript specs because the server and the browser client run on separate threads. Due to the fact that database transactions on the server are not visible to the client, the client has no clue about the user you created in the let!(), therefore the user cannot login to the system.
You need to turn off transactional fixtures and clean your database before/after each run (consider the gem database_cleaner) for your js specs.
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.clean_with :truncation
end
config.before(:each) do
if example.metadata[:js]
DatabaseCleaner.strategy = :truncation
else
DatabaseCleaner.strategy = :transaction
end
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
The above code snippet is taken from the contact manager app readme and slightly modified
Related
The following Capybara test...
require 'rails_helper'
feature 'Sheet owner' do
scenario 'logs in, finds sheet and adds a row' do
sheet = create :sheet
user = create :user
visit login_path
fill_in('Email', :with => user.email)
fill_in('Password', :with => user.password)
click_button('Log in')
end
end
... results in an error:
Failures:
1) Sheet owner logs in, finds sheet and adds a row
Failure/Error: click_button('Log in')
ActionView::Template::Error:
no implicit conversion of nil into String
Yet passes when I remove sheet = create :sheet.
My sheet factory:
FactoryGirl.define do
factory :sheet do
organization "Organization"
event "Event"
description "Description"
location_id 1
user_id 1
misc_dates "1"
end
end
Any ideas why this factory is breaking this test?
In terms of my models, users has_many sheets.
Perhaps you are getting an error that prevents click_button from having access to the "Log In" button. It may be looking for it on an error page.
I am trying to test a feature spec for a user who needs to edit their account model settings. I am new to testing so not sure if my issue is due to how I am setting up my Factory girl associations or if a problem with my database cleaner configurations.
FactoryGirl.define do
factory :user do
first_name "John"
last_name "Smith"
sequence(:email) { |n| "John#{n}#example.com" }
password "pw"
end
end
FactoryGirl.define do
factory :account do
association :owner, factory: :user
name "Account One"
end
end
My spec_helper.rb:
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
require 'shoulda/matchers'
require 'database_cleaner'
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
RSpec.configure do |config|
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.use_transactional_fixtures = false
config.infer_base_class_for_anonymous_controllers = false
config.order = 'random'
config.include FactoryGirl::Syntax::Methods
config.include Devise::TestHelpers, type: :controller
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:twitter] = OmniAuth::AuthHash.new({
:provider => 'twitter',
:uid => '12345'
})
OmniAuth.config.add_mock(:google, {:uid => '12345'})
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
My spec/features/account_spec.rb
require 'spec_helper'
feature 'Account' do
before :each do
#account = create(:account)
#user = #account.owner
sign_in
end
scenario 'a signed in user updates the account settings' do
expect(current_path).to eq edit_account_path(#account.id)
expect(page).to have_content('Edit Account')
fill_in 'Company Name', with: 'New XYZ Co.'
click_button 'Update Account'
expect(page).to have_content('Settings were successfully updated.')
end
scenario 'a signed in user receives an error message when deletes company name' do
fill_in 'Company Name', with: nil
click_button 'Update Account'
expect(page).to have_content("Can't be blank")
end
def sign_in
visit root_path
click_link 'Sign in'
fill_in 'Email', with: #user.email
fill_in 'Password', with: #user.password
click_button 'Sign in'
click_link 'Account'
end
end
If I run just the one spec I get passing tests:
Account
a signed in user updates the account settings
a signed in user receives an error message when deletes company name
Finished in 1.71 seconds
2 examples, 0 failures
But when I run the entire test suite I get errors:
1) Account a signed in user updates the account settings
Failure/Error: expect(current_path).to eq edit_account_path(#account.id)
expected: "/accounts/11/edit"
got: "/accounts/33/edit"
(compared using ==)
# ./spec/features/account_spec.rb:11:in `block (2 levels) in <top (required)>'
Finished in 6.85 seconds
88 examples, 1 failure, 7 pending
Failed examples:
rspec ./spec/features/account_spec.rb:10 # Account a signed in user updates the account settings
I'm creating a bit of code where a user creates Sales Opportunities, which are associated with a certain company. I set up some code that was working fine when a user had to manually input the id of the company, then changed it so the form would display a list of all companies associated with that user's organization (user belongs_to organization, company belongs_to organization, sales_opportunity belongs_to both User and Company).
This has caused my Rspec/Capybara tests to fail with the following error message:
Failure/Error: page.select "Test Co", :from => "Company"
Capybara::ElementNotFound:
Unable to find option "Test Co"
The relevant tests:
describe "sales opportunities" do
let(:organization) { FactoryGirl.create(:organization, :name_one, :with_users)}
let(:company) {organization.companies.create(company_name: "Test Co", organization_id: organization.id, existing_customer: true)}
let(:user) {organization.users.first}
before do
sign_in user
visit user_path(user)
end
it 'has links to add a new sales opportunity' do
expect(page).to have_link('Add sales opportunity', href: new_user_sales_opportunity_path(user_id: user.id))
end
it 'adds a new sales opportunity' do
page.click_link('Add sales opportunity')
page.fill_in('Opportunity name', with: "Capybara Opportunity")
page.fill_in('Close date', with: "2014/12/18")
page.fill_in('Sale value', with: 20000)
page.select "Test Co", :from => "Company"
page.click_button('Save')
expect(current_path).to eq(user_path(user))
expect(page).to have_content('Capybara Opportunity')
expect(page).to have_content('20000')
expect(page).to have_content('2014-12-18')
end
The form field for selecting a company:
<%= f.label :company_id %><br>
<%= f.collection_select :company_id, #user.organization.companies(:company_name), :id, :company_name %>
I can include other parts of the code if you think they're necessary, but from my current guess it seems the Company I'm creating with the "Let" block is not associated with my Organization/User, OR the form is not able to identify this company for some reason. I can't quite work out what I'm doing wrong here - can you help please?
OK, so I fixed this - it was related to the way I was building the company in the test suite. Instead of doing it as above, I put it into a Factory instead:
FactoryGirl.define do
factory :organization do
trait :name_one do
organization_name "New Example Org"
end
trait :name_two do
organization_name "Any Wrong Org"
end
trait :with_users do
before(:create) do |organization|
organization.users << FactoryGirl.build(:user)
organization.users << FactoryGirl.build(:user, name: "Second User", email: "email2#example.com")
end
end
And:
factory :company do
company_name "Test Company"
existing_customer "False"
association :organization, :name_one, :with_users
end
And then called it as follows in the rspec test:
describe "sales opportunities" do
let(:company) {FactoryGirl.create(:company)}
let(:user) {company.organization.users.first}
before do
sign_in user
visit user_path(user)
end
it 'has links to add a new sales opportunity' do
expect(page).to have_link('Add sales opportunity', href: new_user_sales_opportunity_path(user_id: user.id))
end
it 'adds a new sales opportunity' do
page.click_link('Add sales opportunity')
page.fill_in('Opportunity name', with: "Capybara Opportunity")
page.fill_in('Close date', with: "2014/12/18")
page.fill_in('Sale value', with: 20000)
page.select "Test Company", :from => "Company"
page.click_button('Save')
expect(current_path).to eq(user_path(user))
expect(page).to have_content('Capybara Opportunity')
expect(page).to have_content('20000')
expect(page).to have_content('2014-12-18')
end
This sends all the tests green, so whilst I'm not sure what the hell I was doing wrong initially, the answer is to build using FactoryGirl.
Rails noob here. I'm having trouble understanding how/what to test for regarding authentication with Omniauth-Facebook. Pretty much have a similar setup to the relevant railscast. I have my test environment set up similarly to what's described in previous questions and on the Gem wiki.
Couple of questions. When you create a User factory how do you get resulting object to mock an authentication?
Also what goes on when the following code is run?
before do
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:facebook]
visit '/auth/facebook'
end
Is an object saved in a database?
I've added some sample specs below and the Factory spec. Again the spec_helper setup file has the test mode config set to true.
The add_mock setup:
OmniAuth.config.add_mock(:facebook,
{ :provider => 'facebook',
:uid => '1234567',
:info => { :name => 'Jonathan', :image => 'http://graph.facebook.com/1234567/picture?type=square'},
:credentials => {
:expires_at => 1351270850,
:token=> 'AAADzk0b791YBAHCNhBI3n6ScmWvuXTY4yIUqXr9WiZCg1R808RYzaHxsHnrbn62IwrIgZCfSBZAVIP6ptF41nm8YtRtZCeBbxbbz1mF8RQZDZD'
} })
User_pages_spec:
describe "user pages" do
let(:user) { Factory.create(:user) }
describe "sign_in" do
before { visit '/' }
it "should add a user to the User model" do
expect { click_link "sign_in" }.to change(User, :count).by(1)
end
it "should route to the appropriate page if email is nil" do
click_link "sign_in"
page.should have_selector('h5', text: 'Edit Email')
end
it "should redirect to the show page upon" do
user.email = 'example#stanford.edu'
user.save!
click_link 'sign_in'
page.should have_selector('div', text: user.name)
end
end
end
Factory
FactoryGirl.define do
factory :user do
provider "facebook"
uid '1234567'
name 'Jonathan'
end
end
I have a Group model that I am testing using Request specs with Capybara and generating the data using Factory Girl
In my groups.rb factory...
FactoryGirl.define do
factory :group do
sequence :name do |n|
"Group#{n}"
end
expiry Date.today + 2.weeks
end
end
And use this in my groups_spec.rb...
describe "Groups" do
describe "GET /groups" do
it "an admin user can create a new group" do
user = Factory.create(:user, :is_admin => true )
group = Factory.build(:group)
visit root_url
fill_in "Email", :with => user.email
fill_in "Password", :with => user.password
click_button "Login"
click_link "Groups"
click_link "New Group"
fill_in "Name", :with => group.name
# need to change the below to use the Factory eg select Date.new(group.expiry)
select "2014", :from => "group_expiry_1i"
select "June", :from => "group_expiry_2i"
select "1", :from => "group_expiry_3i"
click_button "Create Group"
page.should have_content("Group was successfully created.")
page.should have_content(group.name)
end
end
end
So you see that this is not a good way to do the test as I'm not using the factory generated expiry. Does anyone know how to input the expiry date into the form properly?
This is just off the cuff without testing in Capybara, but I'd try:
select group.expiry.year.to_s, :from => "group_expiry_1i"
select Date::MONTHNAMES[group.expiry.month], :from => "group_expiry_2i"
select group.expiry.date.to_s, :from => "group_expiry_3i"
As long as those values actually exist in the dropdown it should select them correctly.
You can also use select_date:
select_date('31/12/2014', :from => 'Expiry')