I am using capybara with Selenium as its driver. I am trying to click on an element, which when clicked it will reveal a div, but the click never invokes javascript to do just that.
Below is the code I have
scenario 'currently used transport mode cannot be re-selected' do
expect(page).to have_css("h2.summary")
expect(find('h2.summary').text).to eq("Single event")
expect(page).to have_content("Change journey")
page.click_link("Change journey")
expect(find('#travel-times-preview').visible?).to be_truthy # FAILS here because of previous step not working
end
error message
Capybara::ElementNotFound: Unable to find css "#travel-times-preview"
html
<a class="change-journey gray-text" href="#">Change journey</a>
javascript code to execute
$(".change-journey").on("click", function(e){
var target = $(this).data("preview-target");
$('[data-preview-toggle="'+ target +'"]').toggleClass("hidden");
if($(this).text().indexOf('Change journey') > -1){
$(this).text("Close Preview");
}else{
$(this).text("Change journey");
}
e.preventDefault();
});
database cleaner setup
config.before(:suite) do
if config.use_transactional_fixtures?
raise(<<-MSG)
Delete line `config.use_transactional_fixtures = true` from rails_helper.rb
(or set it to false) to prevent uncommitted transactions being used in
JavaScript-dependent specs.
During testing, the Ruby app server that the JavaScript browser driver
connects to uses a different database connection to the database connection
used by the spec.
This Ruby app server database connection would not be able to see data that
has been setup by the spec's database connection inside an uncommitted
transaction.
Disabling the use_transactional_fixtures setting helps avoid uncommitted
transactions in JavaScript-dependent specs, meaning that the Ruby app server
database connection can see any data set up by the specs.
MSG
end
end
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, type: :feature) do
# :rack_test driver's Rack app under test shares database connection
# with the specs, so we can use transaction strategy for speed.
driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test
if driver_shares_db_connection_with_specs
DatabaseCleaner.strategy = :transaction
else
# Non-:rack_test driver is probably a driver for a JavaScript browser
# with a Rack app under test that does *not* share a database
# connection with the specs, so we must use truncation strategy.
DatabaseCleaner.strategy = :truncation
end
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
While i can see the link being clicked, the underlying javascript is not executed.
Assuming you've looked into the dev console in the firefox window that opens and there are no JS errors, there are a few potential reasons for the behavior you're seeing, none of which have anything to do with e.preventDefault() (When you hear hoofs think horses, not zebras)
The link you show doesn't have a data-preview-target attribute so there is nothing for the click handler to read and append to data-preview-toggle= so nothing gets toggled. It's possible that data is added by other JS in your app so is set as a property and wasn't an attribute in the HTML (or you just chose to leave that detail out of the element you showed) which moves to #2
Your .on JS code is running before the element exists, and therefore not attaching. This can show up in test mode when it concatenates all the JS into one file because the JS is loaded faster than when multiple requests are made in dev mode. This would show up as the link text not changing since the click handler isn't run at all. If that is the case delay attaching the listener until the DOM is loaded.
This is the first JS supporting test you're writing, the elements being hidden/shown are generated from database records, and you haven't disabled transactional tests and/or configured database_cleaner correctly. This leads to objects you create in your tests not actually being visible in your app, so the element you're expecting to be on the page isn't actually there. You can verify that by pausing the test and looking in the HTML to see if it is or is not actually on the page.
Based on the error you provided this is not the cause of your issue, just adding for completeness of the answer: click_link clicks the link and returns immediately. That means the test continues running while the action triggered by the click continues. This can lead to a race condition in your code (if you have Capybara.ignore_hidden_elements = false set - bad idea) where your code finds the element and checks its visibility before it's changed. Because of that your final step should be written as below because it will wait/retry for the element to become visible
expect(page).to have_css('#travel-times-preview', visible: true)
As an aside, your test code can be improved and sped up by using the features Capybara provides
scenario 'currently used transport mode cannot be re-selected' do
expect(page).to have_css("h2.summary", text: "Single event") # do it in one query
page.click_link("Change journey") # click_link will find the content anyway so no need to check for it before
expect(page).to have_css('#travel-times-preview') # By default Capybara only finds visible elements so checking visibility is pointeless, if you've changed that default see #4
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With