0

What would cause Capybara (with selenium-webkit as the driver) to not render a partial in a Ruby on Rails 4.2.5 view? I have a pretty standard setup (spec helper included) and nothing fancy going on in the view directly relating to the partial that I can see (such as conditionals not allowing something in a test environment or something or other). Kinda lost on this one.

ENV['RAILS_ENV'] ||= 'test'

require 'spec_helper'
require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails'

require 'capybara/rails'
require 'capybara/rspec'

require 'email_spec'
require 'database_cleaner'
require 'bcrypt'
require 'sidekiq/testing'
Sidekiq::Testing.inline!

require 'rake'
require 'vcr'
require 'webmock'
require 'webmock/rspec'

Bookly::Application.load_tasks

require 'simplecov'
require 'metric_fu/metrics/rcov/simplecov_formatter'
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
  SimpleCov::Formatter::HTMLFormatter,
  SimpleCov::Formatter::MetricFu
]

SimpleCov.start do
  add_filter '/spec/'
  add_filter '/config/'
  add_filter '/lib/'
  add_filter '/vendor/'

  add_group 'Controllers', 'app/controllers'
  add_group 'Models', 'app/models'
  add_group 'Helpers', 'app/helpers'
  add_group 'Mailers', 'app/mailers'
  add_group 'Views', 'app/views'
  add_group 'Services', 'app/services'
  add_group 'Workers', 'app/workers'
  add_group 'Presenters', 'app/presenters'
  add_group 'Decorators', 'app/decorators'

  minimum_coverage 80
end

include ActionDispatch::TestProcess

# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
Dir[Rails.root.join('spec/fakes/**/*.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.maintain_test_schema!

# Capybara Settings
# Capybara.register_driver :poltergeist do |app|
#   Capybara::Poltergeist::Driver.new(app, { debug: false })
# end
# Capybara.register_driver :selenium_chrome do |app|
#   Capybara::Selenium::Driver.new(app, :browser => :chrome)
# end

Capybara.javascript_driver = :selenium
Capybara.default_max_wait_time = 10

RSpec.configure do |config|
  # Reduce cost
  BCrypt::Engine::DEFAULT_COST = 1

  # 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 = false
  config.include FactoryGirl::Syntax::Methods
  config.include EmailSpec::Helpers
  config.include EmailSpec::Matchers
  config.include LoginMacros
  config.include SecurityMacros
  config.include ResourceAccessMacros
  config.include ActiveSupport::Testing::TimeHelpers

  config.before(:suite) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:all) do
    DatabaseCleaner.clean_with(:truncation)
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
    Sidekiq::Worker.clear_all
  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

  config.before(:all) do
    # Rake::Task['init:yodlee'].invoke
    DeferredGarbageCollection.start
  end

  config.after(:all) do
    DeferredGarbageCollection.reconsider
  end

  # RSpec Rails can automatically mix in different behaviours to your tests
  # based on their file location, for example enabling you to call `get` and
  # `post` in specs under `spec/controllers`.
  #
  # You can disable this behaviour by removing the line below, and instead
  # explicitly tag your specs with their type, e.g.:
  #
  #     RSpec.describe UsersController, :type => :controller do
  #       # ...
  #     end
  #
  # The different available types are documented in the features, such as in
  # https://relishapp.com/rspec/rspec-rails/docs
  config.infer_spec_type_from_file_location!
end

The view in question:

<div class="expanded clearfix">
    <div class="inline-form edit-form clearfix">
      <%= render partial: 'transactions/form', locals: { transaction: transaction.object } %>
    </div>
  </div>
nobody
  • 7,803
  • 11
  • 56
  • 91
  • Just to eliminate the possibility, are you sure it's not there? I you haven't yet, call `save_and_open_page`, and confirm that it's not rendering. – elements Dec 28 '15 at 20:43
  • I used the inspect element in the Firefox browser that gets opened and has the rendered page in it and it wasn't present in that. Would there be a difference in Capybara using save_and_open_page vs what is rendered in browser? The internals of Capybara / Selenium seem mysterious sometimes. – nobody Dec 28 '15 at 20:53
  • Oddly enough when I use save_and_open_page the id seems to be present on that page. So confusing! The error I get when my tests fail is that it can't find the add-split-transaction ID ( Capybara::ElementNotFound: Unable to find css "#add-split-transaction" ). The ID is part of the partial that does not appear to get rendered in Firefox when the Selenium runner loads the page. Anyone have ideas on the discrepancy? – nobody Dec 28 '15 at 21:07
  • Hmm. I agree that Capybara and Selenium can be mysterious sometimes. What happens if you make Selenium wait a bit? Try a `sleep(3)` or something between the `visit` call and the `find` for `#add-split-transaction` just to see if it's a timing issue. – elements Dec 28 '15 at 21:29
  • I switched to another changeset and nuked those changes so if you give me a minute I can post another result. I don't know why it would be a sleep issue though since the rendering of the page at that point shouldn't be an issue - should it? – nobody Dec 28 '15 at 23:17
  • Well, I wouldn't think so, but I've had luck uncovering timing issues using `sleep` before. In my case, though, it was looking for elements that were rendered in response to an ajax call, so I was seeing a race condition. (Just skimmed this, but it seems to offer some good advice if that's the case for you: https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara). Is this happening on the initial page load? – elements Dec 29 '15 at 00:09
  • Love thoughtbot! Great article and I read it before. It's not waiting for an initial page load or any other JavaScript event to load that modal so that's why I don't think that is the issue. – nobody Dec 29 '15 at 00:13
  • Oh man. Hmm. Can capybara find it if you use the default driver? I know that's not what you're after, just trying to narrow down the possibilities. – elements Dec 29 '15 at 00:28
  • If you're saying that with save_and_open_page the id is on the page, then the most likely answer is the element isn't visible since Capybara doesn't find invisible elements by default (an empty element that takes no space may also be considered invisible). You can verify that by passing visible: false option to whatever finder you're using. If that then finds the element you'll need to trigger whatever action makes it visible before looking for it – Thomas Walpole Dec 29 '15 at 00:33
  • ok that makes way more sense! it is hidden by default and then changes to visible based on a javascript action. i think the thing that was the most confusing about this whole experience is that the firefox browser that gets the page loaded with selenium doesnt have that element anywhere in it (even hidden). is there some reason why that would happen? – nobody Dec 29 '15 at 17:15
  • The firefox browser should load the page the same way anything would load it, Is the element loaded dynamically via ajax, are you looking before that ajax call finishes? Other than that, a lot more logs, controller actions, source of the partial, and page html would be required to diagnose - Capybara technically isn't even involved in that part of the action, the loading of the page is strictly between the browser and your app running in its own thread – Thomas Walpole Dec 29 '15 at 18:09
  • No there is no ajax involved in the page loading at all. It is rendered as a Rails partial directly on the page. What logs should I look at to help diagnose this issue? I can post additional information regarding controller actions, etc. if pointed into a direction which would be the most beneficial. – nobody Dec 29 '15 at 18:11
  • I would start with the test.log which should show everything being rendered as part of the request response, and make sure what you assume is being rendered actually is - then if the partial is shown as being rendered in the response look at the partial and see if it is dependent on an instance variable and whether or not that variable is actually set to what you think it is. I find it very strange that save_and_open_page would have the element but FF wouldn't since save_and_open_page asks FF for the source of the page and saves it. – Thomas Walpole Dec 29 '15 at 18:42

1 Answers1

0

I had exactly the same issue. It turns out, that Capybara wasn't able to see the data. I verified this, by including two counts in my index.html.haml, just before the render partial call.

    %p= Example.count # => 14 # as seeded
    %p= @examples.count # => 0 # explains why partial wasn't rendering!
    = render @examples

With new set of search terms I found this Capybara server can't access data created in a spec

And there, a link to, where point "3. Use shared connection with transactional fixtures" solved my issue. Even though I am not using fixtures. http://blog.plataformatec.com.br/2011/12/three-tips-to-improve-the-performance-of-your-test-suite/

So I added to my spec_helper.rb the following lines, and now the partial renders correctly:

  class ActiveRecord::Base
    mattr_accessor :shared_connection
    @@shared_connection = nil

   def self.connection
     @@shared_connection || retrieve_connection
   end
 end

 ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
Community
  • 1
  • 1
Matilda Smeds
  • 1,384
  • 11
  • 18