click element if it exists in capybara - 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

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

What's the best way to reuse your step definition in ruby siteprism

im having some issue trying to create a reusable step definition using siteprism
let say feature file is
Given that im on the site
Then i should see a "stack over" text
And i should see a "ask" text
And i should see a "question" text
then on my step definition will be
I want to have arg1 to be dynamic and this logic will check if its true
Then (/^i should see a "(.*?)" text$/) do |arg1|
#common_page = CommonLib.new
#ref = arg1.gsub(/\s+/,'')
expect(#common_page.*#ref*.text).to eq (arg1)
end
Then on my page def will be
class CommonLib < siteprism::page
element :stackover, "#text_header"
element :ask, "#text_ask"
element :question, "#text_question"
the issue im having is this expect(#common_page.#ref.text).to eq (arg1)
the mapping is wrong #ref need to use the data it got like 'stackover', 'ask' and 'question' and map in the CommonLib page def
Calling #text and using the eq matcher is generally a bad idea since it bypasses Capybaras builtin retry behavior and can cause flaky tests on dynamically changing pages. Instead you should use have_text or the :text option passed to the finder
expect(#common_page.send(#ref)).to have_text(arg1)
or
expect(#common_page.send(#ref, text: arg1)).to be
Also, is there a reason you've made #common_page and #ref instance variables, they seem like they should just be regular variables that go out of scope at the end of the test.

RSpec Capybara Element at X no longer present in the DOM

I have a RSpec test with this:
within all('tr')[1] do
expect(page).to have_content 'Title'
expect(page).to have_content 'Sub Title'
end
And it's failing at expect(page).to have_content 'Title' with the following error message:
Element at 54 no longer present in the DOM
I have not been able to find the exact meaning of what this error message means and this test is flakey, sometimes it passes, sometimes not.
Unlike other capybara finders, all doesn't really wait for elements to appear in the DOM. So if your table is not fully loaded, it simply goes straight to the expectation within that block and could potentially fail. That could easily explain why sometimes it fails and other times it succeeds.
I suggest using another expectation before this block and ensure that the table is fully loaded first. I don't know what your DOM looks like, but you can try something like:
expect(page).to have_css('tr td', :count => 15)
So at this point, you've waited for 15 rows to show up in the DOM prior to moving onto your next steps. Hope that helps.
As the other answer details, #all by default doesn't wait for elements to appear so it's possible you're not actually getting the elements you think you are. Rather than the solution in the other answer, it is possible to make #all wait for elements to appear by specifying one of the :count, :minimum, :maximum, or :between options.
within all('tr', minimum: 10)[1] do
...
end
for instance. This is all assuming that the action before the #within in your test isn't triggering an ajax action that is replacing existing rows in a table with other rows. If row replacement is happening then you may be running into one of the biggest downsides of using #all -- when using #all the returned elements cannot automatically be re-queried if they leave the page and are replaced, since their entire query can't be stored with them (no index into the results). In that case you're better off changing the code to
within find(:xpath, './/tr[2]') do
...
end
This way the element you're searching within can be reloaded automatically if needed

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

Capybara find all links on page and check url

I want to check that all links on a page contain a certain element. This is the current web_step I have but it is not working. Any ideas?
Then /^all links above footer should countain "([^"]+)"$/ do |parameter|
al = page.all('a')
al.each do |i|
i.include?(parameter).should be_true
end
end
You probably need to assert against a particular attribute of each a element - if you're checking that the 'src' attribute contains 'parameter' then:
i[:href].include?(parameter).should be_true
Or, to make better use of the rspec matchers (and get better failure messages):
i[:href].should include parameter
[:src] no longer appears to work. Use [:href] instead.

Resources