How to find last matching element in capybara? - capybara

I wanted to do find by class_name and return last element among all elements.
In jquery, we can write something like below but that don't seems to work in capybara
find(".delete:last")
If you are using webkit driver you can write something like -
find(".delete:last-child")
In capybara, you can get last element using below -
all(:css, ".delete").last
Is there any alternative to above?

I had the same problem and ended up using this method in Capybara 2.4
all('.delete').last

Building on #Sharagoz answer and #tgf's comment about all (unlike find) not waiting for async to finish, this combination seems to work for me:
wait_for_ajax
all('.selector').last
With this added to rspec/capybara:
module RspecFeatureHelpers
def wait_for_ajax
Timeout.timeout(Capybara.default_max_wait_time) do
loop do
active = page.evaluate_script('jQuery.active')
break if active == 0
end
end
end
end

Related

Is there a built-in Capybara method to click on an element multiple times?

I have a form with a '+' icon to increase the number value inside of another tag.
So with Capybara I can just click on it like this:
all('.qty-input')[0].find('.more').click
But I want to be able to set the amount of clicks through a variable. I tried doing this but it raises an error because click does not accept any parameters.
all('.qty-input')[0].find('.more').click(number_of_clicks)
I think I can create a simple method to this, like this, but is it necessary? Doesn't Capybara have anything built-in for several clicks?
def multiple_clicks element, number_of_clicks
number_of_clicks.times{|n| element.click}
end
Edit:
The original code I put there was
def multiple_clicks element, number_of_clicks
number_of_clicks.map{|n| element.click}
end
Which doesn't make sense, so I edited it using times instead of map method.
No, Capybara doesn't have anything like that built-in

click element if it exists in capybara

I wish to click a popup message that appears on my test app if it is present. I am new to capybara and cant seem to find a way to do this. I have previous experience with watir and if I were doing it with watir it would be something like:
if browser.link(:text, "name").exists? do
browser.link(:text, "name").click
end
How can I do the same in capybara? Note this link will not always appear hence why I wish to have the if statement.
A straight of the head code is to just invoke a has_link? matcher and then click_link action:
if page.has_link?('name')
page.click_link('name')
end
But it will be not the fastest solution as Capybara will make two queries to driver to get element: first one in has_link? and the second one in click_link.
A better variant may be to make only one query to get an element:
# This code doesn't check that an element exists only at one place and just chooses the first one
link = first('name')
link.click if link
or
# This code checks that element exists only at one place
links = all('name')
unless links.empty?
links.count.should == 1
link = links.first
link.click
end
Personally I would go with has_link?/click_link implementation as the second variant does't check that element exists only at one place and the third one is too long.
In case I used has_css? query :
if page.has_css?("button #popUpButton")
click_button(#popUpButton")
end
You can use first, with option minimum: 0
item = first ".dropdown-item", minimum: 0
item.click if item&.visible?
Have you tried doing something like:
if page.find('.id') do
click_link('Some Link') # or page.find('.id').click
else
page.should_not have_selector('.id') # or something like that
end

Is there a better alternative to using sleep in Capybara?

In my test I have a step where I fill out a field and press enter. This field then returns a result set on the next page.
Here is my helper method:
def search(term)
fill_in('query-text', :with => term)
click_button('search-button')
end
After that I have a step that simply says:
page.should have_content(tutor)
However, the issue is that even though the page after my page loads with the results, the step after it passes even if it should be false. I have set a debugger in the step and when I manually check it, the assertion fails as I expect it too. My assumption is that the the next step is checking before the page even reloads. I placed a sleep at the end of my search method to make it look like:
def search(term)
fill_in('query-text', :with => term)
click_button('search-button')
sleep 5
end
But I feel like using sleep is a hacky way to resolve this issue. I am using Capybara 2 so the use of wait_until is removed. Is there a better way to handle this issue rather than relying on sleep?
Do you have tutor text in your HTML page hidden? has_content? returns true for any text present in html, including hidden text which is not visible. So I would rather replace it with expect(page).to have_text(tutor), which checks only for visible text on a page. .text is also pretty slow method, so these extra split seconds may be handy for you.
Another approach is to restore wait_until method in your spec helpers, since it's really handy in most cases:
def wait_until(timeout = DEFAULT_WAIT_TIME)
Timeout.timeout(timeout) do
sleep(0.1) until value = yield
value
end
end
In any case it will be better than waiting for a fixed timeout each time.
This test passes as tutor is already present on the page.
So you should check not for tutor but for something else, e.g. for element with text tutor that is present only after page reload.
yes, you're right wait_until is removed, new method is #synchronize, but I don't know for now how to use it:)
look into
http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara
https://github.com/jnicklas/capybara/blob/master/lib/capybara/node/base.rb#L44

How to say "any_instance" "should_receive" any number of times in RSpec

I've got an import controller in rails that imports several csv files with multiple records into my database. I would like to test in RSpec if the records are actually saved by using RSpec:
<Model>.any_instance.should_receive(:save).at_least(:once)
However i get the error saying:
The message 'save' was received by <model instance> but has already been received by <another model instance>
A contrived example of the controller:
rows = CSV.parse(uploaded_file.tempfile, col_sep: "|")
ActiveRecord::Base.transaction do
rows.each do |row|
mutation = Mutation.new
row.each_with_index do |value, index|
Mutation.send("#{attribute_order[index]}=", value)
end
mutation.save
end
Is it possible to test this using RSpec or is there any workaround?
Here's a better answer that avoids having to override the :new method:
save_count = 0
<Model>.any_instance.stub(:save) do |arg|
# The evaluation context is the rspec group instance,
# arg are the arguments to the function. I can't see a
# way to get the actual <Model> instance :(
save_count+=1
end
.... run the test here ...
save_count.should > 0
Seems that the stub method can be attached to any instance w/o the constraint, and the do block can make a count that you can check to assert it was called the right number of times.
Update - new rspec version requires this syntax:
save_count = 0
allow_any_instance_of(Model).to receive(:save) do |arg|
# The evaluation context is the rspec group instance,
# arg are the arguments to the function. I can't see a
# way to get the actual <Model> instance :(
save_count+=1
end
.... run the test here ...
save_count.should > 0
There's a new syntax for this:
expect_any_instance_of(Model).to receive(:save).at_least(:once)
This is Rob's example using RSpec 3.3, which no longer supports Foo.any_instance. I found this useful when in a loop creating objects
# code (simplified version)
array_of_hashes.each { |hash| Model.new(hash).write! }
# spec
it "calls write! for each instance of Model" do
call_count = 0
allow_any_instance_of(Model).to receive(:write!) { call_count += 1 }
response.process # run the test
expect(call_count).to eq(2)
end
I finally managed to make a test that works for me:
mutation = FactoryGirl.build(:mutation)
Mutation.stub(:new).and_return(mutation)
mutation.should_receive(:save).at_least(:once)
The stub method returns one single instance that receives the save method multiple times. Because it is a single instance i can drop the any_instance method and use the at_least method normally.
Stub like this
User.stub(:save) # Could be any class method in any class
User.any_instance.stub(:save) { |*args| User.save(*args) }
Then expect like this:
# User.any_instance.should_receive(:save).at_least(:once)
User.should_receive(:save).at_least(:once)
This is a simplification of this gist, to use any_instance, since you don't need to proxy to the original method. Refer to that gist for other uses.
My case was a bit different, but I ended up at this question to figured to drop my answer here too. In my case I wanted to stub any instance of a given class. I got the same error when I used expect_any_instance_of(Model).to. When I changed it to allow_any_instance_of(Model).to, my problem was solved.
Check out the documentation for some more background: https://github.com/rspec/rspec-mocks#settings-mocks-or-stubs-on-any-instance-of-a-class
You may try to count the number of new on the class. That is not actually tests the number of saves but may be enough
expect(Mutation).to receive(:new).at_least(:once)
If there is the only expectation of how many times it was saved. Then you probably want to use spy() instead of fully functioning factory, as in Harm de Wit own answer
allow(Mutation).to receive(:new).and_return(spy)
...
expect(Mutation.new).to have_received(:save).at_least(:once)

What is the best practice when it comes to testing "infinite loops"?

My basic logic is to have an infinite loop running somewhere and test it as best as possible. The reason for having an infinite loop is not important (main loop for games, daemon-like logic...) and I'm more asking about best practices regarding a situation like that.
Let's take this code for example:
module Blah
extend self
def run
some_initializer_method
loop do
some_other_method
yet_another_method
end
end
end
I want to test the method Blah.run using Rspec (also I use RR, but plain rspec would be an acceptable answer).
I figure the best way to do it would be to decompose a bit more, like separating the loop into another method or something:
module Blah
extend self
def run
some_initializer_method
do_some_looping
end
def do_some_looping
loop do
some_other_method
yet_another_method
end
end
end
... this allows us to test run and mock the loop... but at some point the code inside the loop needs to be tested.
So what would you do in such a situation?
Simply not testing this logic, meaning test some_other_method & yet_another_method but not do_some_looping ?
Have the loop break at some point via a mock?
... something else?
What might be more practical is to execute the loop in a separate thread, assert that everything is working correctly, and then terminate the thread when it is no longer required.
thread = Thread.new do
Blah.run
end
assert_equal 0, Blah.foo
thread.kill
in rspec 3.3, add this line
allow(subject).to receive(:loop).and_yield
to your before hook will simple yield to the block without any looping
How about having the body of the loop in a separate method, like calculateOneWorldIteration? That way you can spin the loop in the test as needed. And it doesn’t hurt the API, it’s quite a natural method to have in the public interface.
You can not test that something that runs forever.
When faced with a section of code that is difficult (or impossible) to test you should:-
Refactor to isolate the difficult to test part of the code. Make the untestable parts tiny and trivial. Comment to ensure they are not later expanded to become non-trivial
Unit test the other parts, which are now separated from the difficult to test section
The difficult to test part would be covered by an integration or acceptance test
If the main loop in your game only goes around once, this will be immediately obvious when you run it.
What about mocking the loop so that it gets executed only the number of times you specify ?
Module Object
private
def loop
3.times { yield }
end
end
Of course, you mock this only in your specs.
I know this is a little old, but you can also use the yields method to fake a block and pass a single iteration to a loop method. This should allow you to test the methods you're calling within your loop without actually putting it into an infinite loop.
require 'test/unit'
require 'mocha'
class Something
def test_method
puts "test_method"
loop do
puts String.new("frederick")
end
end
end
class LoopTest < Test::Unit::TestCase
def test_loop_yields
something = Something.new
something.expects(:loop).yields.with() do
String.expects(:new).returns("samantha")
end
something.test_method
end
end
# Started
# test_method
# samantha
# .
# Finished in 0.005 seconds.
#
# 1 tests, 2 assertions, 0 failures, 0 errors
I almost always use a catch/throw construct to test infinite loops.
Raising an error may also work, but that's not ideal especially if your loop's block rescue all errors, including Exceptions. If your block doesn't rescue Exception (or some other error class), then you can subclass Exception (or another non-rescued class) and rescue your subclass:
Exception example
Setup
class RspecLoopStop < Exception; end
Test
blah.stub!(:some_initializer_method)
blah.should_receive(:some_other_method)
blah.should_receive(:yet_another_method)
# make sure it repeats
blah.should_receive(:some_other_method).and_raise RspecLoopStop
begin
blah.run
rescue RspecLoopStop
# all done
end
Catch/throw example:
blah.stub!(:some_initializer_method)
blah.should_receive(:some_other_method)
blah.should_receive(:yet_another_method)
blah.should_receive(:some_other_method).and_throw :rspec_loop_stop
catch :rspec_loop_stop
blah.run
end
When I first tried this, I was concerned that using should_receive a second time on :some_other_method would "overwrite" the first one, but this is not the case. If you want to see for yourself, add blocks to should_receive to see if it's called the expected number of times:
blah.should_receive(:some_other_method) { puts 'received some_other_method' }
Our solution to testing a loop that only exits on signals was to stub the exit condition method to return false the first time but true the second time, ensuring the loop is only executed once.
Class with infinite loop:
class Scheduling::Daemon
def run
loop do
if daemon_received_stop_signal?
break
end
# do stuff
end
end
end
spec testing the behaviour of the loop:
describe Scheduling::Daemon do
describe "#run" do
before do
Scheduling::Daemon.should_receive(:daemon_received_stop_signal?).
and_return(false, true) # execute loop once then exit
end
it "does stuff" do
Scheduling::Daemon.run
# assert stuff was done
end
end
end
:) I had this query a few months ago.
The short answer is there is no easy way to test that. You test drive the internals of the loop. Then you plonk it into a loop method & do a manual test that the loop works till the terminating condition occurs.
The easiest solution I found is to yield the loop one time and than return. I've used mocha here.
require 'spec_helper'
require 'blah'
describe Blah do
it 'loops' do
Blah.stubs(:some_initializer_method)
Blah.stubs(:some_other_method)
Blah.stubs(:yet_another_method)
Blah.expects(:loop).yields().then().returns()
Blah.run
end
end
We're expecting that the loop is actually executed and it's ensured it will exit after one iteration.
Nevertheless as stated above it's good practice to keep the looping method as small and stupid as possible.
Hope this helps!

Resources