visit "/whatever"
expect(page).to have_no_content "This is loaded in asynchronously"
This should fail, but passes. Why? When the page first loads, the content isn't there, and Capybara reasonably doesn't wait for it.
What is the blessed way to wait for this content, now that Capybara 2.0 has declared wait_until bad news?
visit "/whatever"
wait_for_ajax do
expect(page).to have_no_content "This is loaded in asynchronously"
end
Then, in spec_helper:
def wait_for_ajax
sleep(Capybara.default_wait_time)
yield
end
Note that this waits to the end of Capybara's default_wait_time every time it runs this spec. But I'm not sure how else you'd check for something like that. If your ajax executes quickly, you can compensate for this by setting the default_wait_time to something like 1 second.
visit "/whatever"
wait_for_ajax do
expect(page).to have_no_content "This is loaded in asynchronously"
end
Then, in spec_helper:
def wait_for_ajax
page.evaluate_script("jQuery.active") == 0
yield
end
This will evaluate the script until it is true, then run your assertion.
Related
I have set up a very simple rails 5 project to narrow down my problem:
https://github.com/benedikt-voigt/capybara_js_demo
In this project the data mutation done by the Capybara JS is not deleted, neither by Rails nor by the Database cleaner I added.
The following great blog argues, that no DatabaseCleaner is needed:
http://brandonhilkert.com/blog/7-reasons-why-im-sticking-with-minitest-and-fixtures-in-rails
but this works only for fixtures, not for the mutation done by an out-of-thread Capybara test.
I added the Database cleaner, but this also needed work.
Does anybody has a sample setup?
From a quick look at your test I would say it's leaving data because the data is actually being added after DatabaseCleaner cleans. The click_on call occurs asynchronously, so when your assert_no_content call happens there's no guarantee the app has handled the request yet or the page has changed yet and since the current page doesn't have the text 'Name has already been taken' on it the assertion passes and the database gets cleaned. While that is happening the click gets processed by the app and the new data is created after the cleaning has occurred. You need to check/wait for content that will appear on the page after the click - something like
page.assert_text('New Foo Created')
You should only be asserting there is no content once you already know the page has changed, or you're expecting something to disappear from the current page.
I solved now the problem by setting the DB connection to one
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || ConnectionPool::Wrapper.new(:size => 1) { retrieve_connection }
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
as describe here:
https://mattbrictson.com/minitest-and-rails
I uploaded the working repo here:
https://github.com/benedikt-voigt/capybara_js_demo_working
I have this problem with my code.This page inside my application is using http basic authentication. I'm testing my application with Rspec and Capybara, but I have problem with data confirmation when deleting comment, it just can't seem to pass.
Here is my test code:
before(:each) do
page.driver.browser.authorize ENV['ADMIN_LOGIN'], ENV['ADMIN_PASSWORD']
...
end
...
it "should redirect to create new inspiring post and create new post on click" do
visit admin_path
click_link("create_inspi")
fill_in("Title",:with => "Test title")
fill_in("Short",:with => "Test short desc")
fill_in("Body",:with => "Test body desc")
fill_in("Date",:with => "12.12.2012")
click_button("Create new post")
test(
have_content("Test title"),
have_content("12.12.2012")
)
end
example "when inside Link to all... you can delete particular comment",:js => true do
visit admin_path
click_link("Link to all comments in Web development and Motivation category")
click_link(#webdev_comment.id)
page.driver.browser.accept_js_confirms
end
Now for my test by default I use Capybara::RackTest::Driver, but now since I can't find a way to confirm data confirmation with RackTest I have to use Selenium driver to make this data confirmed. But now when using this Selenium driver for my last example test I found next problem:
1) testing admin webpage when inside Link to all... you can delete particular comment
Failure/Error: page.driver.browser.authorize ENV['ADMIN_LOGIN'], ENV['ADMIN_PASSWORD']
NoMethodError:
undefined method `authorize' for #<Selenium::WebDriver::Driver:0x00000003e6ad68>
# ./spec/features/admin.rb:13:in `block (2 levels) in <top (required)>'
MY QUESTION IS:
How to authenticate basic http with selenium?
-------------- EDITED ------------------
I found a way to log in into admin with selenium driver by using this code
example "when inside Link to all... you can delete particular comment",:js => true do
visit root_path
#url = current_url
#tablica = #url.split("http://")
#correct_url = #tablica[1]
admin_selenium_path = "http://#{ENV["ADMIN_LOGIN"]}:#{ENV["ADMIN_PASSWORD"]}##{#correct_url}admin"
page.visit admin_selenium_path
click_link("Link to all comments in Web development and Motivation category")
click_link(#webdev_comment.id)
page.driver.browser.accept_js_confirms
end
But now , even though it's logging in , there is literally no data inside test database, and it's throwing errors around saying that particular object couldn't be found.
before(:each) do
# page.driver.browser.authorize ENV['ADMIN_LOGIN'], ENV['ADMIN_PASSWORD']
#mystory = Mystory.create(name: "My story:",body: "My name is Matthew...")
end
This code above inside before block is not creating a required #mystory record inside database for capybara with selenium driver.
Any ideas how to populate database for capybara with selenium driver?
I'm not sure if it's exactly the same issue but I add this somewhere above/before to skip over confirm dialogs with Capybara
page.evaluate_script('window.confirm = function() { return true; }')
I'm in the process of setting up Selinium to work with Cucumber and Capybara in my Rails app.Currently, my env.rb configuration file contains only:
Capybara.default_driver = :selenium
Cucumber::Rails::World.use_transactional_fixtures = false
I'm not even sure if the second line is necessary ?
The point is, I have Given step definition that creates Model data
Given /^a question named Question1$/ do
#question = Question.create!(name: 'question1')
end
And, in view checkbox and label are created for that 'question1' entry.Now, When step definition is checking that checkbox:
check "question1"
and that scenario passes.
The problem is that when driver is switched to Selenium, the label and checkbox are not rendered on the page, as if #question = Question.create!(name: 'question1') is not executed, and that scenario fails:
Unable to find checkbox "question1" (Capybara::ElementNotFound)
Found the solution. env.rb should contain
DatabaseCleaner.strategy = :truncation
I am using Selenium to get div value, but the fallowing code is not waiting for the page, just for URL. I used time.sleep, which is very primitive and totally not flexible. I want to change it on the explicit, but I am not too experienced in Python and I have a problem with that.
The website name has been changed just in case :
def repeat():
import wx
while True:
botloc = driver.find_element_by_id('botloc').text
print botloc
botX,botY = map(int,botloc.split(','))
print botX
print botY
wx.Yield()
def checker():
if driver.current_url == 'logged.example.com':
time.sleep(5)
repeat()
else:
checker()
checker()
How can I replace time.sleep with something flexible to wait the shortest time as possible after the page will be loaded? How to use explicit correctly with my code?
I know that's possible with using an element from the website, but I can't write anything sensible, I just need an example.
Is possibility to use element_by_id('botloc') for wait till it will be visible then start repeat() ?
How can i replace time.sleep with something flexible to wait shortest
time as possible after the page will be loaded?
I suppose you use get(url) to load the page. Generally you don't have to do anything, WebDriver automatically waits until page is being loaded. So you can remove time.sleep(). However there are some issues reported when loading the page using get with firefox driver, because of that you will have to wait for some target element which is supposed to be in the loaded page as mentioned below.
How to use explicit correctly with my code?
Have you checked Selenium webdriver documentation ? you can wait for botloc element explicitly as below
//assuming you have a valid webdriver reference
//Ex: DEFAULT_WAIT = 10 means
//waits up to 10 seconds before throwing a TimeoutException or if it finds the element will return it in 0 - 10 seconds.
element = WebDriverWait(webdriver, DEFAULT_WAIT).until(EC.presence_of_element_located((By.ID, "botloc")))
Refer this page for more information
Can I execute a javascript in a link with capybara click_link('next_page') ?
The link looks like this:
<a onclick="$('#submit_direction').attr('value', '1');$('#quizForm').submit()" id="next_page" href="#">Next Question</a>
I read at capybara at github that I can submit a form by click at its submit button like this:
click_on('Submit Answer')
But, in my case, I need to submit the form using javascript in a link, so, how to test the link that has javascript inside ? isn't click_link('next_page') sufficient ?
EDIT
after setting :js=> true my test looks like this:
it "should pass when answering all correct", :js=>true do
login_as(student, :scope => :student)
visit ("/student_courses")
#page.execute_script("$('#submit_direction').attr('value', '1');$('#quizForm').submit()")
trace "HTML:------------", page.html
end
Before :js=> true, I could visit the page normally, But, I've noticed that the page cannot be visited after :js=> true, here is the error I got after visiting the page:
Started GET "/student_courses" for 127.0.0.1 at 2012-01-23 06:29:26
+0200 (5010.7ms) UPDATE "students" SET "last_sign_in_at" = '2012-01-23 04:29:26.274285', "current_sign_in_at" = '2012-01-23
04:29:26.274285', "last_sign_in_ip" = '127.0.0.1',
"current_sign_in_ip" = '127.0.0.1', "sign_in_count" = 1, "updated_at"
= '2012-01-23 04:29:26.276279' WHERE "students"."id" = 1 SQLite3::BusyException: database is locked: UPDATE "students" SET
"last_sign_in_at" = '2012-01-23 04:29:26.274285', "current_sign_in_at"
= '2012-01-23 04:29:26.274285', "last_sign_in_ip" = '127.0.0.1', "current_sign_in_ip" = '127.0.0.1', "sign_in_count" = 1, "updated_at"
= '2012-01-23 04:29:26.276279' WHERE "students"."id" = 1 HTML:------------__
Internal
Server Error
Internal Server Error
cannot rollback transaction - SQL statements in progress
WEBrick/1.3.1 (Ruby/1.9.3/2011-10-30) at
127.0.0.1:34718
so, why SQLite3::BusyException: database is locked now ?!
I just spent 8 hours resolving a similar issue, and I found the solution. The fix is so simple, I could cry.
First, diagnosis
The reason you're getting "SQLite3::BusyException: database is locked" is that you are launching an asynchronous thread, namely a form submission, that ends up losing the "database write" race to your test's main thread. In effect, as your test has already completed and is running your "after each" database cleanup routine (defined in your spec_helper), the form action has only just begun trying to run the business logic (which relies on the data that your test after:each hook is busy destroying).
This problem is a lot more likely to occur with tests that click on an AJAX POST button and then terminate without asserting something about the view change.
Second, the fix
As it turns out, Capybara is designed to "synchronize" all your requests. But only if you implicitly let it. Notice that after your form submission, you're not having Capybara look at your page. Therefore it thinks you're done and takes your data out of scope (while your form submission thread is hanging in the background.)
Simply add the following line of code to the end of your test, and it should suddenly work:
page.should_not have_content "dude, you forgot to assert anything about the view"
Third, make it pretty
Don't use execute_script. That's what Capybara is for. But also don't rely on "click_on" because it's not a great abstraction. You need to know too much about its internals. Instead, use CSS selectors like so:
page.find("#submit_button").click
And one more thing - your test shouldn't try to manipulate the DOM. A good Selenium test assumes to have to follow the same steps a normal user follows.
So in conclusion
it "should pass when answering all correct", :js => true do
login_as(student, :scope => :student)
visit ("/student_courses")
page.find(".my-js-enabled-button").click
page.find("#submit_button").click
# Synchronizes your view to your database state before exiting test,
# Therefore makes sure no threads remain unfinished before your teardown.
page.should_not have_content "dude, you forgot to expect / assert anything."
end
To expand on Alex's comment Capybara won't execute JS unless it's explicitly turned on for the given tests.
To do this, use :js => true on either your describe block or individual test eg.
describe "in such and such a context", :js => true do
# some stuff
end
It should work. with capybara, you are essentially test your application through the browser, and it will behave the same.