Question

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 ?!

Was it helpful?

Solution

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

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top