Using capybara+rspec how can i compile a form with empty fields?
I'm testing an edit resource page, so i have a compiled form and want to clean its text fields. This is a partial of test:
context "when submitting" do
before { visit edit_post_path(post) }
it {should have_content('Editing')}
it {current_path.should == edit_post_path(post)}
describe "whit invalid information" do
before do
fill_in "post[title]", :with => "" #not working
fill_in "post[body]", :with => "" #not working
click_button "update"
end
it {current_path.should == edit_post_path(post)}
end
describe "whit valid information" do
before do
fill_in "post[title]", with: "some"
fill_in "post[body]", with: "some"
click_button "update"
end
it {should have_content('some')}
it {should have_content('some')}
it {current_path.should == post_path(post)}
end
end
Check manually actual ID/name/label of relevant field in generated HTML of Edit-page via Chrome:InspectElement of Firefox:Firebug. High chances they are different to "post[title]".
UPD. Try to fill in empty strings manually on the page. Does it work fine? I mean error displayed and the route is correct. Firing "Update"-button and getting an error you're not on the edit_post_path anymore. It happens bc in case of unsuccessful #post.update you're rendering Post#edit-view from Post#update action.
Likely problem is that post[title] and post[body] are the names of the fields, and not the IDs.
Also, something you might want to look into to make your tests a little more rigorous: capybara has a builtin within function that yields a block in which you can perform more actions. Check out the documentation on the front page of the gem page: https://github.com/jnicklas/capybara. It would probably look something like:
describe "whit invalid information" do
before do
within("#post") do
fill_in "title", :with => ""
fill_in "body", :with => ""
click_button "update"
end
end
it {current_path.should == edit_post_path(post)}
end
Related
Tell me please,why does it happen?
I can't understand, if I write:
feature "Article Creation" do
#here i write (:all)
before(:all) do
sign_up_helper
end
I get the error:
Article Creation allows user to visit to creating article page
Failure/Error: fill_in :article_title, :with => 'test_title'
Capybara::ElementNotFound:
Unable to find field :article_title
or
1) Article Creation allows user to visit to article page
Failure/Error: expect(page).to have_content I18n.t('articles.articles_new')
expected to find text "New Article:" in "Toggle navigation Blog Rails New Contacts Sign in --- !ruby/hash:ActionController::Parameters controller: devise/sessions action: new {\"controller\"=>\"devise/sessions\", \"action\"=>\"new\"} nil You need to sign in or sign up before continuing. Sign in: Email Password Remember me Sign up Forgot your password?"
but, if I write:
feature "Article Creation" do
#here i write(:each)
before(:each) do
sign_up_helper
end
It's Ok. All tests works. My question -WHY?
This is my test:
*#before all test visitor signs up
#here I've changed :all and :each*
feature "Article Creation" do
before(:all) do
sign_up_helper
end
scenario "allows user to visit to article page" do
visit new_article_path
expect(page).to have_content I18n.t('articles.articles_new')
end
scenario "allows user to visit to created article page" do
visit new_article_path
fill_in :article_title, :with => 'test_title'
fill_in :article_text, :with => 'example_text'
click_button 'Save Article'
expect(page).to have_content 'example_text'
end
This is sign_up_helper method:
#spec/support/session_helper.rb
def sign_up_helper
visit new_user_registration_path
fill_in :user_email, :with => 'user#example.com'
fill_in :user_username, :with => 'mike'
fill_in :user_password, :with => 'secure123!##'
fill_in :user_password_confirmation, :with => 'secure123!##'
click_button 'Sign up'
end
This is html form:
<p>
<label for="article_title">Title</label><br/>
<input type="text" name="article[title]" id="article_title" />
</p>
<p>
<label for="article_text">Text</label><br/>
<textarea name="article[text]" id="article_text">
</textarea>
</p>
Environment for each test is set anew, I think. New session, cookies, etc. In many cases, even brand new users are generated. So one "global" login is not possible.
Even if it were possible, it would still be a problem, as it introduces spec order dependency which is bad. Imagine that one of your specs logs user out. Then each subsequent spec would fail, because user is not logged in anymore.
To prevent this, make sure that each spec sets its own environment as it needs it (user logins, method stubs, etc.), without relying on side-effects from previously executed specs (which may or may not persist).
Using capybara/rspec to test rails. Want to check current path is generated correctly with the id but cant access the created Contact id.
Example expectation:
localhost:3000/contacts/27
Example recieved:
localhost:3000/contacts/
Code base:
feature 'contacts' do
before do
visit '/'
click_link 'Sign up'
fill_in 'Email', with: 'test#test.com'
fill_in 'Password', with: '123456'
fill_in 'Password confirmation', with: '123456'
click_button 'Sign up'
click_link 'Add a contact'
fill_in 'Firstname', with: 'John'
fill_in 'Surname', with: 'Jones'
fill_in 'Email', with: 'test#test.com'
fill_in 'Phone', with: '223344'
attach_file('contact[image]', Rails.root + 'spec/mouse1.jpeg')
click_button 'Create Contact'
end
context 'view a contact' do
scenario 'click contact to view details' do
click_link('Mouse1')
expect(page).to have_content 'John Jones 223344 test#test.com'
expect(page).to have_xpath("//img[contains(#src, \/html/body/a[2]/img\)]")
expect(page).to have_current_path(contact_path("#{#contact.id}"))
end
end
Surprised the interpolation hasn't worked and throws error undefined method 'id' for NilClass using the below. Clearly it cant access the id.
expect(page).to have_current_path(contact_path("#{#contact.id}"))
Also tried swapping it out with #p = Contact.find_by_id(params[:id]) then passing in the #p in the interpolation. But throws error undefined local variable or method params
Any ideas/thoughts?
You can't access your controllers instance variables from within a feature test. You can however access the database, and since you've only created one contact in this test first or last should work -
expect(page).to have_current_path(contact_path("#{Contact.last.id}"))
That being said, signing up a user and creating the contact through the UI when your test is only checking that an existing contact can be viewed doesn't make a lot of sense when you could just create the database records for your feature tests. You probably want to look into something along the line of FactoryGirl for building your feature test objects.
Let's say I have test on new input and test on new input when input is invalid (first test is on case input is valid).
For example (from my code):
scenario "valid input saving" do
visit program_stream_path(#program, #stream)
click_link "#link"
fill_in "#fill_in", :with=>"1"
click_button "Next"
expect(page).to have_current_path new_students_list_stream_path(#stream)
within("#student_0") do
fill_in "Имя", :with => "Name"
fill_in "Фамилия", :with => "Surname"
fill_in "Электронная почта", :with => "randommail#mail.com"
end
print page.html
click_button "Save"
expect(page).to have_current_path program_stream_path(#program, #stream)
#...other code
end
Obviously, test that checks behavior on invalid input repeats this part:
scenario "invalid input leads to correct input page" do
visit program_stream_path(#program, #stream)
click_link "#link"
fill_in "#fill_in", :with=>"1"
click_button "Next"
expect(page).to have_current_path new_students_list_stream_path(#stream)
#other code
How to avoid this copy-paste way?
You can use before blocks for this kind of thing
feature "..." do
before :each do
visit program_stream_path(#program, #stream)
click_link "#link"
fill_in "#fill_in", :with=>"1"
click_button "Next"
expect(page).to have_current_path new_students_list_stream_path(#stream)
end
scenario "valid input saving"
#unique code for this scenario
end
scenario "invalid input leads to correct input page"
# unique code for this scenario
end
end
The outer feature block could be a describe or a scenario block if wanted/needed since you can nest multiple levels. If you need to use the code across multiple feature files then it makes sense to move it to a method in one of your spec helper files.
You can place it in a method in your spec file like this and reuse it.
scenario "valid input saving" do
your_named_method
...
end
scenario "invalid input leads to correct input page" do
your_named_method
...
end
def your_named_method
visit program_stream_path(#program, #stream)
click_link "#link"
fill_in "#fill_in", :with=>"1"
click_button "Next"
expect(page).to have_current_path new_students_list_stream_path(#stream)
end
A great way to avoid duplication when testing with Capybara is to use Capybara Test Helpers.
RSpec.feature 'Program Stream', test_helpers: [:programs] do
before { visit program_stream_path(#program, #stream) }
scenario 'valid input saving' do
programs.click_to_add_student
programs.should.be_adding_a_student(#stream)
programs.add_student(name: 'Имя', surname: 'Фамилия', email: 'randommail#mail.com')
programs.should.have_new_student('Имя')
end
scenario 'invalid input leads to correct input page' do
programs.click_to_add_student
programs.should.be_adding_a_student(#stream)
programs.add_student(name: nil, surname: nil, email: nil)
programs.should.have_invalid_form("Name can't be blank")
end
end
Besides reducing code duplication, it has the benefit of being a lot more descriptive, which can help to make tests a lot easier to maintain in the long term.
class ProgramsTestHelper < Capybara::TestHelper
# Actions: Encapsulate complex actions to provide a cleaner interface.
def click_to_add_student
click_link '#link'
fill_in '#fill_in', with: '1')
click_button "Next"
end
def add_student(name:, surname:, email:)
fill_in 'Имя', with: name
fill_in 'Фамилия', with: surname
fill_in 'Электронная почта', with: email
click_button 'Save'
end
# Assertions: Allow to check on element properties while keeping it DRY.
def be_adding_a_student(stream)
have_current_path urls.new_students_list_stream_path(stream)
end
def have_new_student(name)
have_content(name)
end
def have_invalid_form(message)
have('form', text: message)
end
end
Have in mind that you could choose to combine click_to_add_student with add_student, running assertions inside the helper methods. It all boils down to how much granularity you need in tests.
Passing blocks to methods is also a nice way to customize interactions or outcomes.
I am writing some integration test cases for an existing application. My test works fine if there is only one 'it' block. However, If I add more than one 'it' block it throws an error. Below is my code that works:
require 'spec_helper'
describe 'Group' do
before do
visit 'http://groups.caremonkey.com/users/sign_in'
fill_in "Email", :with => "email#example.com"
fill_in "Password", :with => "password"
click_button "Login"
page.should have_link('Account')
end
it 'Should check all the links and functionality of groups' do
#add new subgroup with valid data should save a new group
find("#group-squares").click_link("Add")
fill_in "Group Name", :with => "Melbourne futsal"
click_on("Save")
page.should_not have_content("can't be blank")
page.execute_script("parent.$.fancybox.close();")
page.should have_link('Account')
#test edit group: should be able to update group info provided valid data are given
first(".actual img").click
page.should have_content("Group")
page.should have_link("Cancel")
fill_in "Group name", :with => "Futsal club"
page.execute_script("$('#sub-group-color-options').find('.color23').click()")
click_button "Save"
click_on("Cancel")
page.should have_link('Account')
end
end
It works perfectly fine when I put all the 'it' block together in a single 'it' block. But when I split them in different 'it' block, it stops working. For example if I split this ("test edit group: should be able to update group info provided valid data are given") test case into separate 'it' block as follows
require 'spec_helper'
describe 'Group' do
before do
visit 'http://groups.caremonkey.com/users/sign_in'
fill_in "Email", :with => "email#example.com"
fill_in "Password", :with => "password"
click_button "Login"
page.should have_link('Account')
end
it 'add new subgroup with valid data should save a new group' do
find("#group-squares").click_link("Add")
fill_in "Group Name", :with => "Melbourne futsal"
click_on("Save")
page.should_not have_content("can't be blank")
page.execute_script("parent.$.fancybox.close();")
page.should have_link('Account')
end
it 'should be able to update group info provided valid data are given' do
first(".actual img").click
page.should have_content("Group")
page.should have_link("Cancel")
fill_in "Group name", :with => "Futsal club"
page.execute_script("$('#sub-group-color-options').find('.color23').click()")
click_button "Save"
click_on("Cancel")
page.should have_link('Account')
end
end
then rspec fails, it passes the first test, however second test gets failed throwing following error.
Failure/Error: visit 'http://groups.caremonkey.com/users/sign_in'
ActionController::RoutingError:
No route matches [GET] "/users/sign_in"
One more thing, I have to test all the features in remote(url: http://groups.caremonkey.com/). Because, I am writing integration tests for an existing application. In addition, I need to login to the system before I test rest of the features of my application. Thanks in advance for your help.
Have you followed the Capybara documentation for calling remote servers? It says you should have the following:
Capybara.current_driver = :selenium # Or anything but rack_test, probably
Capybara.run_server = false # Don't run your app in-process
Capybara.app_host = 'http://groups.caremonkey.com/'
My guess is that when you have visited the site once, future visit calls are trying to use relative routes, which then is routed to the default server. I can't think why you would get a ActionController::RoutingError if you don't have some kind of Rack server running. Are you running these tests in some other Rails application?
I guess something like this:
require 'spec_helper'
describe 'Group' do
before do
visit 'http://groups.caremonkey.com/users/sign_in'
fill_in "Email", :with => "email#example.com"
fill_in "Password", :with => "password"
click_button "Login"
page.should have_link('Account')
find("#group-squares").click_link("Add") #apperently both specs are "scoped" to this page
end
it 'Should check all the links and functionality of groups' do
fill_in "Group Name", :with => "Melbourne futsal"
click_on("Save")
page.should_not have_content("can't be blank")
page.execute_script("parent.$.fancybox.close();")
page.should have_link('Account')
end
it "test edit group: should be able to update group info provided valid data are given"
first(".actual img").click
page.should have_content("Group")
page.should have_link("Cancel")
fill_in "Group name", :with => "Futsal club"
page.execute_script("$('#sub-group-color-options').find('.color23').click()")
click_button "Save"
click_on("Cancel")
page.should have_link('Account')
end
end
My gut feeling tells me both test need the follow this: find("#group-squares").click_link("Add") so I added it to the before block This test however is cryptic, what is first(".actual img")?
I am running my tests for a rails application using test/unit and capybara. I am fairly new to rails, so I'm hoping that I'm missing something obvious. I have the following integration test to fill out a single field and submit a form. This works as expected and the test passes:
test "create post with title only should add post" do
assert_difference('Post.count') do
visit '/posts/new'
fill_in :title, :with => "Sample Post"
click_button 'create Post'
end
assert current_path == post_path(Post.last)
assert page.has_content?("Sample Post")
end
I added a second test, that pretty much copies the previous test but also fills out a second field and checks for the additional input (this fails).
test "create post with title and body should add post" do
assert_difference('Post.count') do
visit '/posts/new'
fill_in :title, :with => "Testing"
fill_in :body, :with => "This is a sample post"
save_and_open_page
click_button 'create Post'
end
save_and_open_page
assert current_path == post_path(Post.last)
assert page.has_content?("Testing")
assert page.has_content?("This is a sample post")
end
When this failed, I added the call to:
save_and_open_page
and found that the form was being filled out with the title value from the previous test and no body value was supplied at all. The name of the test and the assertions match the second test, so this isn't a case of mistaken identity. It seems that Capybara isn't getting the updated values. I also have this code in my test_helper.rb file:
DatabaseCleaner.strategy = :truncation
module ActionController
class IntegrationTest
include Capybara::DSL
self.use_transactional_fixtures = false
teardown do
DatabaseCleaner.clean
Capybara.reset_sessions!
Capybara.use_default_driver
end
end
end
I'm assuming this should clear out values in between tests. Since that clearly wasn't happening, I also tried adding a call to Capybara.rest_sessions! at the end of the first test and that didn't help.
Any advice or help would be greatly appreciated.
I figured it out. I was calling the fill in method with the symbol :title instead of a string for the field id. I needed to be using 'post_title'. I started that way, but was not prefixing the name with the model name so it wasn't found and it started working when I changed to the symbol that I was using in my erb code.
So use:
fill_in 'post_title', :with => "whatever"
instead of
fill_in :title, :with => "whatever"