jarib / celerity

This project is no longer maintained.
http://celerity.rubyforge.org/
GNU General Public License v2.0
206 stars 38 forks source link

element[s]_by_xpath in Element #32

Closed jozip closed 14 years ago

jozip commented 14 years ago

It would be nice to be able to use xpath on a subset of a page. Any specific reason why this isn't included?

jarib commented 14 years ago

It's just something left over from Watir, I guess. Any reason you can't use the normal :xpath selector though?

  browser.div(:id => "foo").span(:xpath => "...")
jozip commented 14 years ago

The xpath selector forms a condition, right? If I'm not mistaken, the selector in the snippet would yield a specific span that matches the condition. What I mean is that I'd like to be able to use xpath on a subset of a page, and thus making that subset the root in a subsequent search. (HtmlUnit's HtmlPage and HtmlElement both inherit DomNode, so it shouldn't be very dramatic.)

jarib commented 14 years ago

I don't see what use case you have that isn't covered by the existing API. Could you explain in more detail (i.e. with code examples)?

element_by_xpath[s] is being considered for removal in future versions, so I'm interested to hear why you think it's needed.

qerub commented 14 years ago

I agree with toki and would also like to see a method that works like Element#search in Nokogiri and Hpricot. It would allow me to write code on this form:

tables = browser.elements_by_xpath("/html/body/table")

data = table[0].elements_by_xpath("tr").map do |tr|
  tr.elements_by_xpath("td").map(&:text)
end

AFAIK, it's not possible to do this with the current Celerity API but I would love to be proven wrong.

jozip commented 14 years ago

Oh, the functionality covers it as far as I know. The thing is that I don't understand what the :xpath selector means. Is it the xpath forming a condition for the spans? Is it an expression for selecting the sub-elements of a specific span? In the first case, you lose a lot of the expressive power of xpaths. The second case would be what I'm looking for, but having the xpath as a selector seems kind of awkward. Edit: Qerub said it nicely what I'm aiming at.

jarib commented 14 years ago

In my example, you choose a "subdocument" (the div) and then find a span matching the xpath provided. So e.g., for

<div id=container>
    <span class=greeting>Hello</span>, <span class=user>toki</span>.
</div> 

browser.div(:id => "container).span(:xpath => ".//span[text()='toki']").class_name #=> "user"

Not the best example maybe, but that's how it works.

I'm sorry if I'm being difficult, but I'm looking to slim down the API rather than grow it. Qerub's example could be done like this, using the actual API:

browser.table.rows.map do |row|
  row.cells.map(&:text)
end

or even simpler:

browser.table.to_a

th, #td, #tr equivalents are also part of the "Watir 2" API, being worked on in the watir-webdriver project. The plan is to have Celerity implement this API as well. If you really want to use XPaths, as part of the upcoming changes you'll be able to do:

browser.table.elements(:xpath => "//tr").map do |tr|
  tr.elements(:xpath => "//td").map(&:text)
end
qerub commented 14 years ago

The reason I would like good support for XPath queries is that I find them very handy for handling complex documents. browser.table.rows[0].cells is really nice, but sometimes the queries are more like /html/body/table/tr/td/form/div[@class=...]/table/tr[...]/td[2]/div[4]/table/tr/*/span* and in those cases I find XPath's expressive power hard to beat. Another good point is that many other tools use XPath and I find it practical to just extract a query from, say, Firebug and drop it directly in a Celerity script.

elements(:xpath => ...) will definitely add this functionality for me, so I would consider this feature request fulfilled.

I'm sorry if I'm being difficult, but I'm looking to slim down the API rather than grow it.

I think that's a great goal, so need to be sorry. :)

* "Real world" HTML made by "other people" can really suck...

jarib commented 14 years ago

Cool, thanks!

"Real world" HTML made by "other people" can really suck...

No kidding.

qerub commented 13 years ago

[...] If you really want to use XPaths, as part of the upcoming changes you'll be able to do:

browser.table.elements(:xpath => "//tr").map do |tr|
  tr.elements(:xpath => "//td").map(&:text)
end

What's the status of this change?

jarib commented 13 years ago

Status is still the same. Bringing Celerity to Watir 2 compatibility is not something I'm prioritizing at the moment. Feel free to implement this :)

qerub commented 13 years ago

For reference: I settled with adding Container#elements_by_xpath instead, even though Browser#elements_by_xpath is considered for removal because it was easier. If anybody else is interested, the patch is at qerub@650928b056d7551ad57c2811f146ed0b566b8005.