ActionController::UnknownFormat - Capybara spec and AJAX requests - ruby-on-rails

I got problem with my Capybara test. When I run spec with Selenium support (js: true) it goes through the instructions until it encounters click_on method, which hits create controller action and returns error message: "ActionController::UnknownFormat at /category/items
ActionController::UnknownFormat", which is probably respond_with(#items) issue. In fact, create method works fine as well as whole functionality on the server, but into test it crashes. What is more, item creates in the database, so that confirms create method works properly.
Request information:
GET No GET data
POST commit "Do AJAX request"
item {"item_id"=>"12345"}
utf8 "✓"
COOKIES cookie_policy "accepted"
Spec:
require 'spec_helper'
feature "Test in Capybara and Selenium", js: true do
background(:all) do
...seed data
end
scenario "my test scenario" do
visit root_path
visit '/category/1'
within first(".css_style") do
click_on("item_link")
end
# we are into item view
click_on("Do AJAX request") # error
visit another_view_path
end
end
Controller:
class ...::ItemsController < ...::BaseController
respond_to :html, except: ['create', ...]
respond_to :js, only: ['create', ...]
def create
...
#item.save
respond_with(#item)
end
Views:
thing view:
<%= link_to("request", category_items_path(item: {thing_id: thing}), method: :post, class: 'add_item_button', remote: true, name: "animate_item") %>
create.js.erb:
if($('#add-item-button').length > 0){
location.reload(200);
}
spec_helper.rb
...
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rspec'
require 'capybara/rails'
require 'selenium-webdriver'
require 'rack/utils'
Capybara.app = Rack::ShowExceptions.new(Sklep::Application)
Capybara.register_driver :selenium do |app|
Capybara::Selenium::Driver.new(app, :browser => :chrome)
end
Capybara.default_driver = :selenium
...
If I forgot about something please let me know.

Related

Capybara and Factorybot - created data does not appear

I am facing a wired issue, factoryBot is creating a record in Db, but when capybara try to access it, there is no record in HTML.
I tried debugging with "byebug", on prompt, when I say
#topics => It gives me Nil data. (#topic is instance variable in topics_controller.rb -> index method )
If I do "Topic.all.first" it will show me correct record of Topic with an expected random name that is -> "Toys & Garden"
If I do "random_topic.name" -> "Toys & Garden"
I have somewhat similar setup in other feature i.e "account creation feature", it is working fine in there. Any pointer or help would be highly appreciated.
My factory file is
FactoryBot.define do
factory :topic do
name { Faker::Commerce.department(2, true) }
archived false
end
end
My Feature spec file looks like below
require 'rails_helper'
describe "topics" do
let(:user) {create(:user)}
before do
sign_user_in(user) #support fuction to sign user in
end
it "allows topics to be edited" do
random_topic = create(:topic)
visit topics_path # /topics/
expect(page).to have_text random_topic.name # Error1
click_edit_topic_button random_topic.name # Another support fuction
random_topic_name_2 = Faker::Commerce.department(2, true)
fill_in "Name", with: random_topic_name_2
check "Archived"
click_button "Update Topic"
expect(page).to have_text "Topic updated!"
expect(page).to have_text random_topic_name_2
end
end
I get the error on line marked as "Error 1" , please note that "Toys & Garden" is sample name generated by Faker Gem.
Failure/Error: expect(page).to have_text random_topic.name
expected #has_text?("Toys & Garden") to return true, got false
my Rails helper(rails_helper.rb) file setup is as below.
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
require 'database_cleaner'
require 'capybara/rspec'
require 'shoulda/matchers'
require 'email_spec'
require "email_spec/rspec"
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
ActiveRecord::Migration.maintain_test_schema!
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
# This is for setting up Capybara right host.
# https://stackoverflow.com/questions/6536503/capybara-with-subdomains-default-host
def set_host (host)
default_url_options[:host] = host
Capybara.app_host = "http://" + host
end
RSpec.configure do |config|
config.include Capybara::DSL
config.include Rails.application.routes.url_helpers
config.include FactoryBot::Syntax::Methods
config.include EmailSpec::Helpers
config.include EmailSpec::Matchers
config.order = "random"
config.use_transactional_fixtures = false
config.before(:each) do
set_host "lvh.me:3000"
end
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 Topics Controller file is something like below
class TopicsController < ApplicationController
layout 'dashboard'
before_action :authenticate_user!
def index
#topics = Topic.all
end
end
Updated with #smallbutton.com comments, but issue continues.
SOLVED
I am using apartment gem and hence topic was getting created in public schema while test was looking into a respective tenant.
As per #thomas suggestion, I modified the code:
before do
set_subdomain(account.subdomain)
sign_user_in(user) #support fuction to sign user in
Apartment::Tenant.switch!(account.subdomain)
end
When this happens it's generally caused by one of a few things
The record isn't actually being created
From your code it doesn't appear to be that
The record is being created in one transaction while the app is running in a different transaction.
You appear to have database_cleaner configured correctly, and this shouldn't be an issue in your case, however you should be using - https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example - rather than the configuration you have (which depends on the :js metadata rather than checking which driver is actually being used)
The user has configured Capybara to hit a different server than the one the test is actually running on.
Here we appear to have an issue, since you're configuring Capybara.app_host to be 'lvh.me:3000'. Port 3000 is what your dev app is generally run on, while Capybara (by default) starts your app on a random port for tests. This probably means your tests are actually running against your dev app/db instance which has nothing to do with the test app/db instance, and therefore the tests won't see anything you create in your tests. Remove the setting of Capybara.app_host unless you have an actual need for its setting (in which case remove the port from your app_host setting and enable Capybara.always_include_server_port)
This all being said, since you're using Rails 5.1+ database_cleaner should not be needed anymore. You should be able to remove all of database_cleaner, reenable use_transactional_fixtures, and set Capybara.server = :puma and have things work fine (still would need to fix the app_host setting)
Your record isn't persisted when you visit the page, so it's normal that it return false.
Try the following :
require 'rails_helper'
describe "topics" do
let(:user) {create(:user)}
let!(:random_topic) {create(:topic)}
before do
sign_user_in(user) #support fuction to sign user in
visit topics_path # /topics/
end
it "allows topics to be edited" do
expect(page).to have_text random_topic.name # Error1
click_edit_topic_button random_topic.name # Another support fuction
random_topic_name_2 = Faker::Commerce.department(2, true)
fill_in "Name", with: random_topic_name_2
check "Archived"
click_button "Update Topic"
expect(page).to have_text "Topic updated!"
expect(page).to have_text random_topic_name_2
end
end
Note that random_topic is extracted in a let! (more info about the difference between let and let! : https://relishapp.com/rspec/rspec-core/v/2-5/docs/helper-methods/let-and-let !
You should create the record before you visit the page. Currently you do it the other way around so your record cannot appear.
People having this kind of issue should definitly try this:
instance.reload after clicking and before using "expect".
This solved my issue I've been stuck on for a whole day ...

Capybara error with Poltergeist and RSpec

I am trying to write a simple spec with Capybara using the Poltergeist driver in RSpec. There doesn't seem to be a problem when tests should fail , however when I am expecting a passing test I get the following error:
~/.rbenv/versions/2.2.3/lib/
ruby/gems/2.2.0/gems/poltergeist-1.6.0/lib/capybara/poltergeist/errors.rb:17:in
`initialize': wrong number of arguments (0 for 2) (ArgumentError)
I navigated to where the the line of code the error was indicating:
class JSErrorItem
attr_reader :message, :stack
def initialize(message, stack)
#message = message
#stack = stack
end
def to_s
[message, stack].join("\n")
end
end
But I was unable to find anywhere that I should be interacting with this constructor.
This is the spec that I am writing
describe 'Sign Up', type: :feature do
it 'should allow user creation through the signup form' do
visit new_user_url host: 'http://localhost:3000'
within('.form') do
fill_in('user[username]', with: 'Example')
fill_in('user[password]', with: 'Password')
fill_in('user[password_confirmation]', with: 'Password')
find(".submit-button").click
puts page.body
expect(page).to have_content('Welcome')
User.last.destroy!
end
end
end
The puts page prints the content of the page as expected but after the error occurs and the remainder of the lines in the spec are not run. Oddly enough the error only occurs when I am expecting the spec to pass. When I am expecting a failing test the entire spec runs without error.
My spec helper was set up as below:
RSpec.configure do |config|
require 'capybara/rspec'
require 'capybara/poltergeist'
require 'capybara/dsl'
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, time_out: 120, phantomjs_options: ['--ignore-ssl-errors=yes'], js_errors: false)
end
Capybara.configure do |c|
c.javascript_driver = :poltergeist
c.default_driver = :poltergeist
c.app_host = 'http://localhost:3000'
end
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
end
I upgraded Poltergeist to 1.9 and PhantomJS to 2.1 as suggested in the comments and it fixed the issue.

rails rake test, url helpers not working

Getting the following when running my capybara rails tests with "rake test". Problem seems to be url helpers I'm using in my application.html.erb:
DashboardLoginTest#test_login_and_check_dashboard:
ActionView::Template::Error: arguments passed to url_for can't be handled. Please require routes or provide your own implementation
app/views/layouts/application.html.erb:29:in `_app_views_layouts_application_html_erb__938277815294620636_4045700'
test/integration/dashboard_login_test.rb:6:in `block in <class:DashboardLoginTest>'
Here's line 29 in application.html.erb that its complaining about:
<%= link_to("asdf", root_path, {:class => 'brand'}) %>
Here's what the test looks like:
test "login and check dashboard" do
visit("/")
assert page.has_content?("welcome")
end
Here's my test_helper.rb:
ENV["RAILS_ENV"] ||= "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'factory_girl'
require 'capybara/rails'
include Warden::Test::Helpers
Warden.test_mode!
def main_app
Rails.application.class.routes.url_helpers
end
class ActiveSupport::TestCase
ActiveRecord::Migration.check_pending!
fixtures :all
end
class ActionDispatch::IntegrationTest
# Make the Capybara DSL available in all integration tests
include Capybara::DSL
include Rails.application.routes.url_helpers
def sign_in(user = nil)
if user.nil?
user = FactoryGirl.create(:user)
$current_user = user
end
login_as(user, :scope => :user)
user.confirmed_at = Time.now
user.save
end
end

Undefined method "contain" for controller spec

It's definitely best to divide specs up so you have specs pertaining to each aspect of the MVC architecture, but I think there is a slight crossover with controller specs and view specs.
With view specs, you should only be concerned with the view, but with controller specs I still think it would be a good idea to test that the correct view is rendered, and maybe even test the content of the view, although more in-depth testing of the content should take place in the view spec.
Despite this clear article, https://www.relishapp.com/rspec/rspec-rails/v/2-1/docs/controller-specs/render-views, describing how to do this, I just cannot integrate my view and controller specs.
I keep getting the error undefined method 'contain'!
Here's my spec_helper:
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'capybara/rspec'
require 'capybara/rails'
require 'factory_girl_rails'
require 'ap'
def set(factory)
#user = FactoryGirl.create(factory)
end
def sign_up(first_name, last_name, profile_name, email, password)
visit "/"
click_link "Register"
fill_in('First name', with: first_name)
fill_in('Last name', with: last_name)
fill_in('Profile name', with: profile_name)
fill_in('Email', with: email)
fill_in('Password', with: password)
fill_in('Password confirmation', with: password)
click_button 'Sign up'
end
def sign_in(email, password)
visit "/"
click_link "Sign In"
fill_in('Email', with: email)
fill_in('Password', with: password)
click_button 'Sign in'
end
def sign_out
visit "/"
click_link "Sign Out"
end
#Webrat.configure do |config|
# config.mode = :rails
#end
#webrat
require 'capybara/poltergeist'
# Capybara.javascript_driver = :poltergeist
Capybara.javascript_driver = :selenium
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
RSpec.configure do |config|
# true means 'yes, filter these specs'
config.filter_run_excluding stress: true
# config.current_driver = :webkit
# config.use_transactional_fixtures = false
# config.include Capybara::DSL
DatabaseCleaner.strategy = :truncation
config.after(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
# config.before(:suite) do
# DatabaseCleaner.strategy = :transaction
# DatabaseCleaner.clean_with(:truncation)
# DatabaseCleaner.start
# end
# config.after(:each) do
# DatabaseCleaner.clean
# end
#config.after(:suite) do
# DatabaseCleaner.strategy = :transaction
# DatabaseCleaner.clean_with(:truncation)
# DatabaseCleaner.clean
# end
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
# config.fixture_path = "#{::Rails.root}/spec/fixtures"
# config.include RSpec::Rails::RequestExampleGroup, type: :feature
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
I18n.enforce_available_locales = true
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = "random"
end
Here's my controller spec:
require "spec_helper"
describe UserFriendshipsController, type: :controller do
render_views
let (:user_1) { FactoryGirl.create(:user_1)}
before {
sign_in user_1
get :index
}
it "renders the :index view" do
response.should render_template(:index)
end
it "view contains expected html" do
# a sanity test more than anything
response.should contain("Welcome to the home page")
end
end
Upon running this spec I get this:
.F
Failures:
1) UserFriendshipsController view contains expected html
Failure/Error: response.should contain("Listing widgets")
NoMethodError:
undefined method `contain' for #<RSpec::Core::ExampleGroup::Nested_1:0x00000008632268>
# ./spec/controllers/user_friendships_spec.rb:18:in `block (2 levels) in <top (required)>'
Finished in 0.1835 seconds
2 examples, 1 failure
Why is this happening? How can I get this to work?
If you look at the relish documentation for the current 2.14 version of Rspec you'll see that they're using match now instead:
expect(response.body).to match /Listing widgets/m
Using the should syntax, this should work:
response.body.should match(/Welcome to the home page/)
Right, it was a very unclear article from Rspec that caused this error. It was using webrat in its example and didn't think to tell you. If anyone else gets here, you can add webrat to your gemfile to use the contain method:
Gemfile
group :test do
gem 'webrat'
end
However, it makes a lot more sense to use rspec's native match method:
expect(response.body).to match /Listing widgets/m

Setting up one time login with minitest/capybara for running rails tests

I'm using capybara with minitest on Rails 2.3.14. Like most applications, this one also requires login to do anything inside the site. I'd like to be able to login once per test-suite and use that session throughout all tests that are run. How do I refactor that to the minitest_helper? Right now my helper looks something like this:
#!/usr/bin/env ruby
ENV['RAILS_ENV'] = 'test'
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
gem 'minitest'
gem 'capybara_minitest_spec'
require 'minitest/unit'
require 'minitest/spec'
require 'minitest/mock'
require 'minitest/autorun'
require 'capybara/rails'
require 'factory_girl'
FactoryGirl.find_definitions
class MiniTest::Spec
include FactoryGirl::Syntax::Methods
include Capybara::DSL
include ActionController::URLWriter
before(:each) do
# .. misc global setup stuff, db cleanup, etc.
end
after(:each) do
# .. more misc stuff
end
end
thanks.
Here’s an example of multiple sessions and custom DSL in an integration test
require 'test_helper'
class UserFlowsTest < ActionDispatch::IntegrationTest
fixtures :users
test "login and browse site" do
# User avs logs in
avs = login(:avs)
# User guest logs in
guest = login(:guest)
# Both are now available in different sessions
assert_equal 'Welcome avs!', avs.flash[:notice]
assert_equal 'Welcome guest!', guest.flash[:notice]
# User avs can browse site
avs.browses_site
# User guest can browse site as well
guest.browses_site
# Continue with other assertions
end
private
module CustomDsl
def browses_site
get "/products/all"
assert_response :success
assert assigns(:products)
end
end
def login(user)
open_session do |sess|
sess.extend(CustomDsl)
u = users(user)
sess.https!
sess.post "/login", :username => u.username, :password => u.password
assert_equal '/welcome', path
sess.https!(false)
end
end
end
Source : http://guides.rubyonrails.org/testing.html#helpers-available-for-integration-tests

Resources