Capybara with :js => true causes test to fail - javascript

I'm new to Capybara and testing on Rails in general, so please forgive me if this is a simple answer.
I've got this test
it "should be able to edit an assignment" do
visit dashboard_path
select(#project.client + " - " + #project.name, :from => "assignment_project_id")
select(#team_member.first_name + " " + #team_member.last_name, :from => "assignment_person_id")
click_button "Create assignment"
page.should have_content(#team_member.first_name)
end
it passes as is, but if I add :js => true it fails with
cannot select option, no option with text 'Test client - Test project' in select box 'assignment_project_id'
I'm using FactoryGirl to create the data, and as the test passes without JS, I know that part is working.
I've tried with the default JS driver, and with the :webkit driver (with capybara-webkit installed)
I guess I don't understand enough what turning on JS for Capybara is doing.
Why would the test fail with JS on?

I've read the Capybara readme at https://github.com/jnicklas/capybara and it solved my issue.
Transactional fixtures only work in the default Rack::Test driver, but
not for other drivers like Selenium. Cucumber takes care of this
automatically, but with Test::Unit or RSpec, you may have to use the
database_cleaner gem. See this explanation (and code for solution 2
and solution 3) for details.
But basically its a threading issue that involves Capybara having its own thread when running the non-Rack driver, that makes the transactional fixtures feature to use a second connection in another context. So the driver thread is never in the same context of the running rspec.
Luckily this can be easily solve (at least it solved for me) doing a dynamic switching in th DatabaseCleaner strategy to use:
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before :each do
if Capybara.current_driver == :rack_test
DatabaseCleaner.strategy = :transaction
else
DatabaseCleaner.strategy = :truncation
end
DatabaseCleaner.start
end
config.after do
DatabaseCleaner.clean
end
end

A variation of brutuscat's answer that fixed our feature specs (which all use Capybara):
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
# set the default
DatabaseCleaner.strategy = :transaction
end
config.before(:each, type: :feature) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.append_after(:each) do
DatabaseCleaner.clean
end

There is another way to deal with this problem now described and discussed here: Why not use shared ActiveRecord connections for Rspec + Selenium?

Related

Cucumber+Ruby+Capybara+Selenium: How to make the 'visit' method wait for dynamic content

Here is the issue that has been nagging for weeks and all solutions found online do not seem to work... ie. wait for ajax, etc...
here is versions of gems:
capybara (2.10.1, 2.7.1)
selenium-webdriver (3.0.1, 3.0.0)
rspec (3.5.0)
running ruby 2.2.5
ruby 2.2.5p319 (2016-04-26 revision 54774) [x64-mingw32]
in the env.rb
Capybara.register_driver :selenium do | app |
browser = (ENV['browser'] || 'firefox').to_sym
Capybara::Driver::Selenium.new(app, :browser => browser.to_sym, :resynchronize => true)
Capybara.default_max_wait_time = 5
end
Here is my dynamicpage.feature
Given I visit page X
Then placeholder text appears
And the placeholder text is replaced by the content provided by the json service
and the step.rb
When(/^I visit page X$/) do
visit('mysite.com/productx/')
end
When(/^placeholder text appears$/) do
expect(page).to have_css(".text-replacer-pending")
end
Then(/^the placeholder text is replaced by the content provided by the json service$/) do
expect(page).to have_css(".text-replacer-done")
end
the webpage in question, which I cannot add it here as it is not publicly accessible, contains the following on page load:
1- <span class="text-replacer-pending">Placeholder Text</span>
after a call to an external service (which provides the Json data), the same span class gets refreshed/updated to the following;
2- <span class="text-replacer-done">Correct Data</span>
The problem I have with the "visit" method in capybara + selenium is that as soon as it visits the page, it thinks everything loaded and freezes the browser, and it never lets the service be called to dynamically update the content.
I tried the following solutions but without success:
Capybara.default_max_wait_time = 5
Capybara::Driver::Selenium.new(app, :browser => browser.to_sym, :resynchronize => true)
add sleep 5 after the visit method
wait for ajax solution from several websites, etc...
adding after hooks
etc...
I am at a complete loss why "visit" can't wait or at least provide a simple solution to an issue i am sure is very common.
I am aware of the capybara methods that wait and those that don't wait such as 'visit' but the issue is;
there is no content that goes from hidden to displayed
there is there is no user interaction either, just the content is getting updated.
also unsure if this is a capybara issue or a selenium or both.
Anyhow have insight on any solutions? i am fairly new to ruby and cucumber so specifically what code goes in what file/folder would be much appreciated.
Mel
Restore wait_until method (add it to your spec_helpers.rb)
def wait_until(timeout = DEFAULT_WAIT_TIME)
Timeout.timeout(timeout) do
sleep(0.1) until value = yield
value
end
end
And then:
# time in seconds
wait_until(20) { has_no_css?('.text-replacer-pending') }
expect(page).to have_css(".text-replacer-done")
#maxple and #nattf0dd
Just to close the loop on our issue here...
After looking at this problem from a different angle,
we finally found out Cucumber/Capybara/ is not a problem at all :-)
The issue we are having lies with the browser Firefox driver (SSL related), since we have no issues when running the same test with the Chrome driver.
I do appreciate the replies and suggestions and will keep those in mind for future.
thanks again!

Check if JS box exists with RSpec + capybara-webkit

I'm using RSpec + capybara, and the capybara-webkit as driver.
I have to check if a JS box exists in the page after clicking on a button, but with no results.
If I use selenium as a driver, the test passes, but I need to use capybara-webkit.
I'm using
expect(page).to have_selector(#js_window)
My configuration is
Capybara.run_server = false
Capybara.default_selector = :css
Capybara.default_max_wait_time = 1
Capybara.javascript_driver = :webkit
RSpec.configure do |config|
config.include Capybara::DSL
end
In the end, it was a problem with my config.block_unknown_urls (I was blocking the url I needed) and the default_max_wait_time (I changed it from 1 to 3).
Solved now!

Rails capybara visiting unrelated page causes test to fail

I've got got a page with some AJAX code that I'm trying to build a spec for using Capybara. This test works:
context 'from the JobSpec#index page' do
scenario 'clicks the Run now link' do
visit job_specs_path
within( find('tr', text: #job_spec.name) ) { click_link 'run_now' }
visit current_path
expect(find('tr', text: #job_spec.name)).to have_content(/(running|success)/)
end
end
After clicking the link 'run_now', a job is launched and the user is redirected to the launched_jobs_path, which has a Datatable that uses some AJAX to pull currently running jobs. This test passes. However, I wanted to add to the test to check to make sure that the job wasn't already running before clicking the 'run_now' button (which would give me a false positive). I started playing around with it, but then I noticed that even simply putting visit launched_jobs_path before visit job_specs_path would cause the test to fail with the error
Failure/Error: expect(find('tr', text: #job_spec.name)).to have_content(/(running|success)/)
Capybara::ElementNotFound:
Unable to find css "tr" with text "job_spec_collection-1"
I'm fairly certain that this is an issue with the Javascript not running at the right time, but I don't quite know how to resolve it. Any ideas? This is Rails 4.1 with Capybara 2.4, and Capybara-webkit 1.3.
Thanks!
Update
In the parent context of the spec, I do have :js => true
feature 'User runs a JobSpec now', :js => true do
before do
Capybara.current_driver = :webkit
#job_spec = FactoryGirl.create(:job_spec, :with_client_spaces)
end
after { Capybara.use_default_driver }
context 'from the JobSpec#index page' do
...
end
end
I think it might be as simple as adding :js => true to your scenario header so that it knows to launch webkit / selenium to test the js / ajax.
scenario 'clicks the Run now link', :js => true do
Hope that helps!
UPDATE
Well here's one more idea. You could try adding this to your 'rails_helper' or 'spec_helper' file to set the :js driver.
Capybara.javascript_driver = :webkit
Do you have other :js tests that are working properly with :webkit? Have you tried using save_and_open_page to see if the element you are looking for is actually on the page and Capybara just isn't finding it?
Ok, pretty sure I've got something that will at least work for now. If anyone has more elegant solutions I'd love to hear them.
I think the issue is in the complexity of what happens in the 'run_now' controller. It adds a job to a Rufus scheduler process, which then has to start the job (supposed to start immediately). Only after the launched job is running via Rufus does an entry in the launched_jobs table get created.
While developing, I would see the job just launched as soon as I clicked 'Run now'. However, I'm guessing that WebKit is so fast that the job hasn't been launched by the time it gets to the launched_jobs#index page. The extra visit current_path there seem to help at first because it would refresh the page, but it only worked occasionally. Wrapping these last two steps in a synchronize block seems to work reliably (I've run it about 20 times now without any failures):
context 'from the JobSpec#index page' do
scenario 'clicks the Run now link' do
# First make sure that the job isn't already running
visit launched_jobs_path
expect(page).to have_no_selector('tr', text: #job_spec.name)
# Now launch the job
visit job_specs_path
within( find('tr', text: #job_spec.name) ) { click_link 'run_now' }
page.document.synchronize do
visit current_path
expect(find('tr', text: #job_spec.name)).to have_content(/(running|success)/)
end
end
end

what's the best practice to control linux process from web page( Rails)?

currently I am facing to a problem that: we need to control the linux scripts running in background from web page. e.g. we could start/stop a 'script' via the button on the webpage.
also another good example is:
I am an expert of web development, and very familiar with javascript(setTimeout to refresh the progress), ruby on rails.
thanks a lot.
if it is linux systems then you could use
system(path_to_script.sh)
or
`path_to_script.sh`
in your controller
thanks to #xvidun, I found the solution: using god.
the key process is: how to make a non-daemon process start as a daemon. "nohup ... &" doesn't work for me. so I tried god and it is great!
in my case, the process I want to run is:
$ cd /sg552/workspace/m-video-fetcher
$ nohup ruby script/start_fetch.rb &
here is how I did the job:
step1 : create a god config file:
# file name: fetcher.god
RAILS_ROOT = '/sg552/workspace/m-video-fetcher'
God.watch do |w|
w.name = 'fetcher'
w.dir = RAILS_ROOT
w.start = "ruby script/start_fetch.rb"
w.log = "#{RAILS_ROOT}/fetcher_stdout.log"
w.keepalive
end
step2:
$ god start -c fetcher.god
step3:
# in the view, give users the interface to restart this job.
<a href='/settings/restart_fetch_for_all_plans'>restart</A>
# in the controller:
def restart_fetch_for_all_plans
result = `god stop fetcher && god start fetcher`
redirect_to :back, :notice => "fetcher restarted, #{result}"
end

Error calling Javascript in Camping App

I want to learn about webapps. I decided to learn by doing and chose to start simple with Camping as (i). it is small & (ii). i know some ruby.
I think I am comfortable with the HTML and CSS side of things and using MAB. So i decided to step up and add some javascript which fails to work. Here is the offending code snippet from my View:
div.image do
link :rel => 'stylesheet', :href => 'styles.css'
script :type =>"text/javascript", :src => 'display_date.js'
# The hash argument always go at the end.
button 'Display Date', :type => "button", :onclick => "displayDate"
end
The javascript file looks like this:
function displayDate()
{
document.getElementById("demo").innerHTML=Date();
}
When I click the 'Display Date' button on the screen Firefox webconsole reports
-- [18:32:46.762] ReferenceError: displayDate is not defined
It does not work on IE either. The camping file and the Javascript file are in the same directory. Please advise. The result I get is however better than adding the Javascript inline which results in a stack error, I can live without inline js but it would be handy to know how to code this also.
I'm new to camping and Ruby too, so this may be horrible. It works though ;)
Give the controllers module a route to serve display_date.js to the client:
class MyJS < R '/display_date\.js'
JS = File.read('display_date.js')
def get
#headers['Content-Type'] = 'application/javascript; charset=utf-8'
JS
end
end
Call displayDate as a function, with parens: :onclick => displayDate().
To get the function to be called you must put in onclick = "javascript:displayData()"

Categories

Resources