0

Just to give you a background, I'm using Ruby for creating automated tests along with Selenium, Cucumber, Capybara and SitePrism. I have some tests that need to check the text of a certain element on the page, for example:

def get_section_id
  return section.top.course.section_id.text
end

However, I would like to check if all the parent elements exist before calling .text on the nested course_and_section_id element. For example, to check the text of this particular element I would do:

if(has_section? && section.has_top? && section.top.has_course? && section.top.course.has_section_id?)
  return section.top.course.section_id.text
end

Is there any way to recursively check if something exists in Ruby like this? Something that could be called like: has_text?(section.top.course.section_id) maybe?

Andrew Allison
  • 1,122
  • 2
  • 13
  • 30
  • Thanks @CarySwoveland, that certainly does help. However I was looking for a more generic solution where I could get the text attribute from ```n``` number of nested elements. I'm not sure it's possible in Ruby but I know in JS you can do something like this: http://web.archive.org/web/20161108071447/http://blog.osteele.com/posts/2007/12/cheap-monads/ – Andrew Allison Mar 08 '18 at 19:05
  • Please let me know if my answer is what you are look for. If it isn't I (and probably others) do not understand that question. – Cary Swoveland Mar 08 '18 at 19:28
  • @CarySwoveland What you're suggesting won't work because `&.` requires a nil response for it's failure case - the element methods raise an exception when they can't find the element – Thomas Walpole Mar 08 '18 at 20:19
  • @Thomas, thanks for setting me straight. I deleted my comment. Based on your understanding of the question please tell me if my modified answer is now correct. – Cary Swoveland Mar 08 '18 at 20:57
  • Someone has been systematically going through my profile and downvoting all questions and answers I've provided on Stackoverflow. The admins seem to be content letting it happen as well. Therefore, I would ask that any subsequent visitors of this post please vote genuinely and leave a comment explaining why you did or did not find the post useful. I'm not trying to indulge in some pathetic tit-for-tat, I just want to help the next person. Thank you! – Andrew Allison Oct 16 '18 at 14:55

3 Answers3

2

It sounds like you may want something like the following.

arr = [section, :top, :course, :section_id, :text]
arr.reduce { |e,m| e && e.respond_to?(m) && e.public_send(m) } 

Because reduce has no argument the initial value of the memo e is section. If e becomes nil or false it will remain that value.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
  • This still has the issue that `public_send(m)` is going to raise Capybara::ElementNotFound if the element is not yet visible on the page (checking respond_to? doesn't change that). See my answer for what I think the OP actually wants (although it's not strictly what was asked - but what was asked for doesn't really make a lot of sense when using site_prism and writing automated apps) – Thomas Walpole Mar 08 '18 at 21:20
  • @ThomasWalpole, I see your point, but I know almost nothing of Rails, so was tripped up by your point about the `Copybara` exception. – Cary Swoveland Mar 09 '18 at 00:21
2

There is nothing builtin to ruby that would do this because the methods you're calling return the element, or raise an exception. If they returned the element or nil then the suggestion of Cary Swoveland to use &. would be the answer.

The critical thing to remember here is what you're actually trying to do. Since you're writing automated tests, you're (most likely) not trying to check whether or not the elements exist (tests should be predictable and repeatable so you should know the elements are going to exist) but rather just wait for the elements to exist before getting the text. This means what you really want is probably more like

def get_section_id
  wait_until_section_visible
  section.wait_until_top_visible
  section.top.wait_until_course_visible
  section.top.course.wait_until_section_id_visible
  return section.top.course.section_id.text
end

You can write a helper method to make that easier, something like

def get_text_from_nested_element(*args)
  args.reduce(self) do |scope, arg| 
    scope.send("wait_until_#{arg}_visible")
    scope.send(arg)
  end.text
end

which could be called as

def get_section_id
  get_text_from_nested_element(:section, :top, :course, :section_id)
end
Thomas Walpole
  • 48,548
  • 5
  • 64
  • 78
  • This is what I was looking for @thomas-walpole. I think it wasn't so clear from my question, but it was a tricky thing for me to try to put into words. I want to be sure every parent element exists before checking the child element's text. Thank you and Cary Swoveland for your help on this. – Andrew Allison Mar 09 '18 at 16:24
1

Whilst this is a bit outdated, the fact that &. won't work here when it is the most elegant perhaps gives rise for this being a useful feature

If you can raise it on GH with a sample page where this would be useful then we could look at getting it introduced

Luke

Luke Hill
  • 460
  • 2
  • 10