Rails + Cucumber + Capybara: Matching exact link text - ruby-on-rails

Cucumber is complaining that I have an ambiguous match for when I tell it to click a link: Ambiguous match, found 4 elements matching link "Spirits" (Capybara::Ambiguous). So I am trying to match the "Spirits" link exactly.
I looked through this: https://github.com/jnicklas/capybara#exactness and found the exact: true option so I changed my code from:
When(/I click on the (.*) link/) do |link_name|
click_link(link_name)
end
to:
When(/I click on the (.*) link/) do |link_name|
click_link(link_name, exact: true)
end
But now it's complaining with wrong number of arguments (2 for 1) (ArgumentError). I'm on Rails 3.2.13, capybara (2.0.3), cucumber (1.2.3). Anyone know why there is an error?

Your Capybara version is not the latest.
exact method added in 2.1.0. https://github.com/jnicklas/capybara/blob/master/History.md
You need to upgrade the gem.

Related

ArgumentError when running Capybara tests on Ruby 3.0

I am really stuck. I am upgrading my Rails app to Ruby 3 (from 2.7). When running tests, I always run into this issue when I visit a path:
state = "new"
visit status_path(state: “state")
I receive the following error when running rspec:
Capybara starting Puma...
* Version 5.6.4 , codename: Birdie's Version
* Min threads: 0, max threads: 4
* Listening on http://127.0.0.1:58568
ArgumentError: wrong number of arguments (given 2, expected 1)
from ~/.rbenv/versions/3.0.5/lib/ruby/3.0.0/net/protocol.rb:116:in `initialize'
My Gemfile is as such:
gem "capybara" # 3.38.0
gem "selenium-webdriver" # 4.8.0
gem "webdrivers" # 5.2.0
(They're all on the latest version)
My setup doesn't look wrong:
require "webdrivers/chromedriver"
Webdrivers.cache_time = 86_400 # 1 day
Capybara.register_driver :headless_chrome do |app|
Capybara::Selenium::Driver.load_selenium
browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |options|
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-gpu")
end
Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
end
Capybara.javascript_driver = :headless_chrome
Troubleshooting:
I tried Puma 6 - same issue.
The controllers at status_path is not even hit. This errors occurs right after Puma loads up.
I do not think it's the Capybara setup, and I just cannot find where it is calling the Ruby 3 library wrong (net/protocol).
I downgraded capybara to 3.37.1, and same issue.
Thank you
FYI Upgrading from 2.7 to 3 you will more often than not see this error. It's highly likely that some code you were previously using in 2.7 will be not correctly hashing some args.
The first hit when googling this will take you back to SO (Won't share link as SO doesn't like links). But to paraphrase from the official ruby byline when updating
Separation of positional and keyword arguments in Ruby 3.0:
In most cases, you can avoid incompatibility by adding the double splat operator. It explicitly specifies passing keyword arguments instead of a Hash object. Likewise, you may add braces {} to explicitly pass a Hash object, instead of keyword arguments.
TL;DR - Try doing splatting your hash args collection kwargs -> **kwargs - Your rails path likely isn't a kwarg but a hash --> { key: value }
EDIT: Reasoning (If you're interested), is that prior to ruby3. Ruby would try assess and guesstimate what you meant. From ruby3 onwards it has made a change and fill forcibly use what you give it (A lot of people used to use kwargs but wanted them treated as a single hash, now you need to stipulate this!)

Error capybara js: true

Im having problems when i try to config my capybara test to respond js. This is my test:
test "creating_expense", js: true do
visit expenses_path
click_link('New Expense')
end
The error is:
/var/lib/gems/2.3.0/gems/activesupport-5.0.6/lib/active_support/testing/declarative.rb:11:in `test': wrong number of arguments (given 2, expected 1) (ArgumentError)
The default minitest test method doesn't support metadata on tests (the examples you're copying were probably using RSpec). To swap to the JS driver you need to change the current_driver in a setup blog as shown in the Capybara README - https://github.com/teamcapybara/capybara#using-capybara-with-minitest
If you want to add support for metadata on tests you can look at the minutest-minidata gem - https://github.com/wojtekmach/minitest-metadata#example-with-capybara

Activesupport / Multi json: "Did not recognize your adapter specification"

I have a Ruby 1.9.3 / Rails 3.1 project with the following in the gemfile:
gem 'rails', '3.1.12'
gem 'json'
gem 'multi_json', '1.7.7'
That version of rails sets activesupport to 3.1.12 as well. I'm not sure what the exact cause of the problem is, but when running bundle exec rake test, I got the error:
/home/user/.gem/ruby/1.9.3/gems/multi_json-1.7.7/lib/multi_json.rb:121:in 'rescue in load_adapter': Did not recognize your adapter specification. (ArgumentError)
...
(more stack trace, including activesupport methods)
Fortunately I found a solution! See below.
Edit: My original answer is outdated and incorrect; read it if you please, but please read the updated information at the bottom.
After viewing a ton of other questions such as these ones:
OmniAuth Login With Twitter - "Did not recognize your adapter specification." Error
Capistrano deploy: "Did not recognize your adapter specification" during assets:precompile
https://github.com/intridea/multi_json/issues/132
I hadn't found a solution, so I dove into the library and determined that load_adapter was receiving the parameter "JSONGem". The alias was failing, and the method attempted to load
/home/user/.gem/ruby/1.9.3/gems/multi_json-1.7.7/lib/multi_json/adapters/JSONGem.rb
This file doesn't exist, but .../json_gem.rb does exist! So I modified load adapter as follows:
def load_adapter(new_adapter)
# puts "new_adapter: #{new_adapter}" # Debugging
# puts "new_adapater.class: #{new_adapter.class}" # Debugging
case new_adapter
when String, Symbol
new_adapter = ALIASES.fetch(new_adapter.to_s, new_adapter)
new_adapter = "json_gem" if new_adapter =~ /^jsongem$/i # I added this line
# puts "final adapter: #{new_adapter}" # debugging
require "multi_json/adapters/#{new_adapter}"
klass_name = new_adapter.to_s.split('_').map(&:capitalize) * ''
MultiJson::Adapters.const_get(klass_name)
when NilClass, FalseClass
load_adapter default_adapter
when Class, Module
new_adapter
else
raise NameError
end
rescue NameError, ::LoadError
raise ArgumentError, 'Did not recognize your adapter specification.'
end
This fixed the problem for me. It's probably not an optimal solution (ideally I would understand WHY the ALIASES.fetch failed, if that is indeed what happened, and fix that), but if your problem is similar then hopefully this quick fix can help.
Update
It's not viable for deployability reasons to modify someone else's gem. Fortunately I found the root cause of the problem. In project_root/config/initializers/security_patches.rb, we had the line
ActiveSupport::JSON.backend = "JSONGem"
This was the recommended fix to a security bug in older versions of rails. Now that we are on a newer version of rails (i.e, > 3.0), we can simply replace "JSONGem" with "json_gem" (which is what my original modification was doing, in a roundabout way) and not worry about the security issue.

how to select the first element from a dropdown. RoR/Capybara/Selenium

So this code was working perfectly fine, until I recently updated my selenium webdriver:
When /^I search for (.*)$/ do |term|
term = " " if term == "blank"
step "I fill in search with #{term}"
within(".navbar-search") do
page.find(:css, "li:first").click
end
end
I updated, and now I get the following error:
An invalid or illegal string was specified (Selenium::WebDriver::Error::UnknownError)
./features/step_definitions/search_steps.rb:5:in `block (2 levels) in <top (required)>'
./features/step_definitions/search_steps.rb:4:in `/^I search for (.*)$/'
features/search_friend.feature:13:in `When I search for <term>'
Here is the cucumber feature:
#javascript
Scenario Outline: The search bar
Given I login
And I have a contact named ABC
And I have a contact named DEF
And I have a contact named GHI
When I search for <term>
Then I should see the message <message>
Examples:
| term | message |
| ... some examples ... | |
I guess you updated not only Webdriver but also Capybara.
Capybara 2.1 now uses driver's implementation of CSS selectors. In case of Selenium Webdriver it means that browser's implementation is used. :first pseudo selector is not standard and isn't supported by browsers so your CSS selector is not valid.
Previously it worked because Capybara converted CSS selector to XPath using Nokogiri. Nokogiri supports :first pseudo selector.
So you should change your invalid CSS selector to something valid like:
page.first(:css, 'li').click
page.find(:css, 'li', match: :first).click (the difference between previous variant and this one is that this variant waits for element to appear on the page but the first one doesn't wait. See this section of Capybara README to get more information on match)
page.find(:css, "li:first-child").click
page.find('li:first-child').click

cucumber contradictory error messages

Problem Background: "cucumber declarative step definitions using web_steps.rb" Stack Overflow Question
In troubleshooting the problem in question two contradictory error messages are arrived at; with the statement:
When /^(?:|I )uncheck "([^"]*)"$/ do |field|
check(field)
end
added to 'features/step_definitions/movie_steps.rb' execution of 'bundle exec cucumber features/filter_movie_list.feature' results in:
Ambiguous match of "I uncheck "ratings_G"":
features/step_definitions/movie_steps.rb:65:in '/^(?:|I )uncheck "([^"]*)"$/
features/step_definitions/web_steps.rb:65:in '/^(?:|I )uncheck "([^"]*)"$/
However, removal of the step does not result in the step definition from 'web_steps.rb' being utilized; rather, a different error message is displayed:
When I uncheck the following ratings: G, PG-13 # features/step_definitions/movie_steps.rb:44
Undefined step: "When I uncheck "ratings_G"" (Cucumber::Undefined)
./features/step_definitions/movie_steps.rb:52:in `block (2 levels) in <top (required)>'
./features/step_definitions/movie_steps.rb:49:in `each'
./features/step_definitions/movie_steps.rb:49:in `/I (un)?check the following ratings: (.*)/'
features/filter_movie_list.feature:30:in `When I uncheck the following ratings: G, PG-13'
How is it possible for Cucumber to complain that a step is redundant when there are two definitions in two places but then later complain that the very same step is not defined when its duplicate is removed? Is it possible the second error message really means something other than what is stated?
PS: The configuration was arrived at by way of a Cucumber installation with training wheels for CS169.x1 # edX...
It seems that you have one definition called When I uncheck "ratings_G" that is somewhat matched with the call When I uncheck the following ratings: G, P-13 that occurs in the error message. Cucumber is complaining that nothing matches the latter one. Does it exist in your code?
The somewhat matching would possibly explain the ambiguity warning you got initially. The definition matches but not what is found inside the string.

Resources