I am trying to extract mixed mode content using Capybara. I did it using Nokogiri, but wonder why similar is not possible with Capybara.
require 'nokogiri'
doc = Nokogiri::HTML("<h1><em>Name</em>A Johnson </h1>")
puts doc.at_xpath("//h1/text()").content
It works, but when I try same XPath selector in Capybara it doesn't work.
visit('http://stackoverflow.com')
puts find(:xpath, "//h1/text()").text
It raises error:
[remote server] file:///tmp/webdriver-profile20120915-8089-kxrvho/extensions/fxdriver#googlecode.com/components/driver_component.js:6582:in `unknown': The given selector //h1/text() is either invalid or does not result in a WebElement. The following error occurred: (Selenium::WebDriver::Error::InvalidSelectorError)
[InvalidSelectorError] The result of the xpath expression "//h1/text()" is: [object Text]. It should be an element.
How to extract this text?
Capybara requires a driver, and the XPath will be executed by the driver. From your error message, it is clear you are using selenium-webdriver, which will use a browser's native XPath implementation where available. For IE, it usees its own.
You appear to be using a combination where the XPath implementation is not fully compliant. You can try to change the driver or browser, but if you really want to use Nokogiri to extract content, you should be able to do the following:
doc = Nokogiri::HTML(page.html)
puts doc.at_xpath("//h1/text()").content
I do not believe Capybara or Selenium-Webdriver have any support for directly accessing text nodes. However, if you do not want to use nokogiri, you can use selenium-webdriver to execute javascript.
You can do this (in Capybara using Selenium-Webdriver):
element = page.find('h1').native
puts page.driver.browser.execute_script("return arguments[0].childNodes[1].textContent", element)
#=> A Johnson
Related
I have a select2 v4 that loads options through AJAX.
I am running a Cucumber test where I need to select 2 options of the list, but I can't seem to make the list open up and load (which normally gets populated when I type 2 or characters).
I have tried:
As suggested here:
#session.execute_script("$('#publish_to').select2('open')")
and
#session.first(".input.publish_to .select2-container").click
and
#session.first("#publish_to").find(".select2-choice").click
which do not give me an error, but I am not getting the options to select, so I am assuming that the click is not really working. Things I have tried to select the options:
# This one cannot find the css:
#session.find(".select2-results__options", text: client.email).click
# This one gives me a Timeout error
#session.evaluate_script "$('#publish_to').val(#{client.id}).trigger('change')"
# This one gives me a Timeout error
#session.evaluate_script "$('.select2-search__field').trigger('keydown').val('#{client.email}').trigger('keyup')";
sleep 10
#session.find('.select2-search__option', text: client.email).click
Anything with trigger gives me a Timeout error, so I tried waiting for jQuery.active but I never got a true even waiting for 2 minutes:
counter = 0
timeout_in_sec = 120
while counter < timeout_in_sec && #session.evaluate_script('jQuery.active').zero?
sleep 1.second
counter+=1
end
I tried using the gem capybara-select2 running:
#session.select2 client.email, css: '#publish_to', search: true
but I get the error undefined methodselect2' for #and I haveWorld(CapybaraSelect2)in myenv.rb`
I am using Cucumber v3.1.2 with ruby gem 'cucumber-rails'
The poltergeist driver is roughly equivalent to a 7 year old version of Safari which means it doesn't support a lot of current JS/CSS. This means your issue could simply be that select2 is no longer compatible with Poltergeist (without a lot of polyfilling). You're going to be much better off updating to using a real browser (stable - chrome via selenium, etc) or one of the direct to Chrome drivers (highly beta) that have spun off Poltergeist (Apparition is one of them). Those will allow you to run with a visible browser (useful for debugging) or headless.
The following code uses Chrome via selenium and interacts with the select2 demo site to select an entry that is loaded via Ajax.
require "selenium/webdriver"
require "capybara/dsl"
sess = Capybara::Session.new(:selenium_chrome)
sess.visit("https://select2.org/data-sources/ajax")
sess.first('.select2-container', minimum: 1).click
sess.find('.select2-dropdown input.select2-search__field').send_keys("capy")
sleep 5 # just to watch the browser search
sess.find('.select2-results__option', text: 'teamcapybara/capybara').click
sess.assert_selector(:css, '.select2-selection__rendered', text: 'teamcapybara/capybara')
sleep 5 # just to see the effect
I test following assumption And I click on the text "2018/2019" within ".year" with capybara/headless_chrome and constantly get the error
element not visible
(Session info: headless chrome=67.0.3396.87)
(Driver info: chromedriver=2.40.565386 (45a059dc425e08165f9a10324bd1380cc13ca363),platform=Mac OS X 10.13.5 x86_64) (Selenium::WebDriver::Error::ElementNotVisibleError)
I've already tried to adjust the window size, as suggested here. But it didn't work.
My step definition:
When(/^(?:|I )click on the text "([^"]*)"(?: within "([^"]*)")?$/) do |link, selector|
begin
page.find(:css, selector).click
end
end
The element is actually visible and found by Capybara
[1] pry(#<Cucumber::Rails::World>)> page.find(:css, ".year")
=> #<Capybara::Node::Element tag="a" path="/html/body/div[2]/div[2]/section/div[2]/div/div[1]/div[1]/div[2]/div/div[2]/ul/li/a">
But click fails
[2] pry(#<Cucumber::Rails::World>)> page.find(:css, ".year").click
Selenium::WebDriver::Error::ElementNotVisibleError: element not visible
Why doesn't click work here?
EDIT:
The HAML of the link is
%ul.facet_values
- unselected.each do |facet_value|
%li.filtered{data: {hayf: {text: facet_value.name.downcase}}}
= link_to facet_value.path, title: facet_value.name, class: 'year' do
=truncate("#{facet_value.name}", length: 24)
- if facet.has_counts?
%span.small="(#{facet_value.count})"
I tried headless testing with Poltergeist and got Poltergeist detected another element with CSS selector 'html body header div.container-fluid' at this position which was solved with .trigger('click') and the test passed.
Since this doesn't work in non-headless Chrome or Poltergeist, then the simplest answer is that the element isn't actually visible. Firstly remove the Capybara.ignore_hidden_elements = false setting since that makes no sense when testing an app, and will screw up all sorts of waiting behaviors. Secondly use save_and_open_screenshot to get a picture of what the page actually looks like before you are trying to click the link.
I'm guessing you'll see that the link isn't actually visible at that point and your test is missing a step a user would have to do to make the link visible, is it in a menu where you have to hover over a different element first? is it in a popup where you have to click on something else first?, does it need to be scrolled to? etc.
Finally, you'll generally have less issues if you stick with Chrome via Selenium. Poltergeist uses PhantomJS which hasn't been updated in quite some time and doesn't support modern JS or CSS which can lead to all sorts of strangeness.
I am using Rails 4.2, Capybara 2.5, and Poltergeist 1.8. I have an input tag inside a div whose id is #foobar, and in my JS, I have a click event handler for #foobar.
<div id='foobar'><input type=submit></div>
<script>$('#foobar').click(function() {})</script>
Everything works as expected in a browser but my tests fail if I send a Capybara click to the div instead of the input tag:
Test works
page.find('#foobar input').click
Test does not work
page.find('#foobar').click
Is this a known problem, or am I understanding the expected behavior incorrectly?
Like the title says capybara is having trouble finding double_click:
undefined method `double_click' for #<Capybara::Node::Element:...>
Click works just fine as do most other methods. I'm using capybara 2.1.0, poltergeist 1.6.0 and phantomjs 1.9.7. Any ideas?
For posterity here's the solution:
I set javascript_driver:
Capybara.javascript_driver = :poltergeist
but not default_driver:
Capybara.default_driver = :poltergeist
Are you sure you have a valid Capybara element that can be clicked on?
page.find('#lst-ib').double_click
The above works on Google.com
For double click event, the below approach might help you solve your problem.
I know its bit late but for people who might face similar issue in their automation work
On latest ruby capybara and selenium version use
element = page.find(:xpath,"//a[contains(text(), locator)]")
page.driver.browser.action.double_click(element.native).perform
for Old version pls use the below code:
element = page.find(:xpath,"//a[contains(text(), locator)]")
page.driver.browser.mouse.double_click(element.native)
Find the element with either xpath or css.
Use page.driver.browser.mouse.double_click(element.native) to perform the action
I am trying to get Nokogiri to behave when using it with delayed jobs but haven't been very successful so far.
Basically I am trying to run a parsing task in the background, but when the background worker hits my perform method, it fails in the following line:
HTML_page = Nokogiri::HTML(open('http://www.mysite.com'))
The error message is:
Nokogiri::HTML::Document#inspect failed with ArgumentError: Requires a Node, NodeSet or String argument, and cannot accept a Delayed::Backend::ActiveRecord::Job.
This happens with both Delayed::Jobs.enqueue and delay methods.
If I try the line below in the console, I get the same error:
Nokogiri::HTML(open('http://www.mysite.com')).delay
It might be a silly oversight as I am fairly new to Ruby and Rails, so any help would be greatly appreciated.
Since Nokogiri "Requires a Node, NodeSet or String argument", why not give it one?
Instead of:
HTML_page = Nokogiri::HTML(open('http://www.mysite.com'))
try:
HTML_page = Nokogiri::HTML(open('http://www.mysite.com').read)
That will cause IO to read the file handle created by open and pass Nokogiri the string content of the URL being read.
An alternate way to help debug the problem, which I don't think lies within Nokogiri, is to split your command up a bit:
body = open('http://www.mysite.com').read
HTML_page = Nokogiri::HTML(body)