Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to find any elements by text in Capybara?

I want to find any element with a given text on my page, but when I pass it to find without an element it gives me back an error

find(material.attachment_filename) #material.attachment_filename is "01. pain killer.mp3"

But if i do:

find('a',text: material.attachment_filename)

It works fine, and the given error is:

Selenium::WebDriver::Error::InvalidSelectorError:

Given css selector expression "01. pain killer.mp3" is invalid: SyntaxError: An invalid or illegal string was specified

like image 950
João Vitor Avatar asked Oct 17 '25 18:10

João Vitor


1 Answers

Capybara's find takes 3 arguments (a Capybara selector type, a locator string, and an options hash). If the selector type isn't specified it defaults to :css which means the locator string needs to be a CSS selector. This means that find(material.attachment_filename) in your case is equivalent to

find(:css, "01. pain killer.mp3")

which will raise an error as you've seen because "01. pain killer.mp3" isn't valid CSS. If you want to find any element containing the text you could do something like

find('*', text: "01. pain killer.mp3") 

which will find any element containing the text, however that's also going to find all the ancestor elements too since they also contain the text, So what you'd probably want is to use a regex to make sure the element contains only that content

find('*', /\A#{Regexp.escape(material.attachment_filename)}\z/)

which should be interpreted as

find('*', /\A01\. pain killer\.mp3\z/)

Note: That is going to be pretty slow if your page has anything more than simple content on it because it means transferring all the elements from selenium to capybara to check the text content.

A more performant solution would be to use XPath which has support for finding elements by text content (CSS does not)

find(:xpath, XPath.descendant[XPath.string.n.is(material.attachment_filename)]) #using the XPath library - contains (assuming Capybara.exact == false)
find(:xpath, XPath.descendant[XPath.string.n.is(material.attachment_filename)], exact: true) #using the XPath library - equals (you could also pass exact:false to force contains)

If the text won't contain XPath special characters (ex. apostrophes) that need escaping, you can use a string to define the XPath:

find(:xpath, ".//*[contains(., '#{material.attachment_filename}')]") #contains the text
find(:xpath, ".//*[text()='#{material.attachment_filename}']") #equals the text

If the element is actually a link you're looking for though then you would probably want to use

find_link("01. pain killer.mp3")

or

find(:link, "01. pain killer.mp3")
like image 66
Thomas Walpole Avatar answered Oct 21 '25 06:10

Thomas Walpole



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!