rails 3.1, capybara-webkit, how to execute javascript inside link?
Asked Answered
B

2

2

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

Beneath answered 22/1, 2012 at 20:44 Comment(3)
do you have js turned on in your test suite?Verduzco
Maybe the problem was that you were using transactional strategies instead of eg. database_cleaner?Yseulta
I'm also getting SQLite3::BusyException: database is locked errors when running cucumber/capybara. Only happens when I turn on javascript. Did you ever figure this out?Grandsire
P
8

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
Proleg answered 6/3, 2013 at 23:31 Comment(3)
Thank you for detailed answer, I asked this question before a year, wow .. time passes fast ..Beneath
This almost completely solved the problem, but I still get the dreaded database is locked error unless I set DatabaseCleaner.strategy = :truncation for all of my specs... which makes them way slower. I wish capybara just waited for the database!Longevity
Thanks! The "Second, the fix" part of your answer saved me a lot of head scratching!Lenssen
D
1

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
Dickens answered 22/1, 2012 at 23:19 Comment(3)
strange.. try resetting your DB/server etc?Dickens
how can I reset the DB or server ? where do you advice me to read?Beneath
@SamirSabri to restart your server, just hit ctrl-C and start it again with 'rails server' .. for your database, use 'rake db:reset'Dickens

© 2022 - 2024 — McMap. All rights reserved.