4

I have elements that can be in one of two state class="icon" or class="icon active".

I thought that $browser.element(:class => /^icon$/).click would click the first button that isn't active but it just clicks the first one it finds regardless of whether or not it also contains "active."

Is the regex wrong? Or better yet, is there a non-regex way of doing it?

  • The problem with class is that the class attribute is effectively a space separated list of classes. So it is somewhat difficult as far as I know to do a direct match on x but not y where class is concerned. Is there another way to identify what you want? Also is the element type know, or can it be one of many types? – Chuck van der Linden Apr 15 '13 at 16:15
  • can you mirror the `icon` css class in a html `data-icon` attribute whose existence would guard the `click` call ? – collapsar Apr 15 '13 at 16:21
  • Which browser are you using? I tried in watir-webdriver using Firefox and your regex seemed to work. In watir-classic (IE), the regex does not (since the code checks that the regex matches at least one of the classes). – Justin Ko Apr 15 '13 at 16:25
  • `$browser.element(:class => /^icon$/).click` works the same way in Chrome and Firefox for me. It clicks the element without regard to whether or not it contains `active`. – thisisauserid Apr 15 '13 at 19:36

3 Answers3

3

This is theoretical, and I apologize for not having the time to construct a fake page and test to see if it works

browser.element(:class => /icon(?!active)$/).click  

This works in theory (the regex) matching a line like icon but not icon active but, there may be some under the hood magic that goes on with how class names are matched which might cause it to return the wrong line.

If that does not work let me know, I'll suggest an alternative approach, which while less elegant, ought to work.

For reference I used the Rubular online regex tester along with this SO answer Regular expression to match a line that doesn't contain a word? to some up with that.

Community
  • 1
  • 1
Chuck van der Linden
  • 6,660
  • 2
  • 28
  • 43
  • As far as I can tell (I tried a prototype and understanding the code base), this regex will return the same results as that in the question. In other words, it will work in watir-webdriver but still fail for watir-classic. – Justin Ko Apr 15 '13 at 16:33
  • That's what I was afraid of.. that instead of trying to match against "icon active" it would try to match each class in turn. So first try to match against "icon" which would succeed, even if the next class was active – Chuck van der Linden Apr 15 '13 at 16:38
  • Yeah, the button is still being clicked if the class is also `active`. – thisisauserid Apr 15 '13 at 19:10
3

As mentioned in the comments, the regex you used should work in watir-webdriver. However if you need a solution that will work in both watir-classic and watir-webdriver, you will need to use find.

b.elements.find{ |e| e.class_name == 'icon'}.click

This will only matches elements where the 'class' attribute is exactly 'icon'.

It is slower and less readable, but allows you to bypass watir-classic's method for matching classes. As seen below, watir-classic will check that the regex matches any of the element's classes.

def match_class? element, what
  classes = element.class_name.split(/\s+/)
  classes.any? {|clazz| what.matches(clazz)}
end
Justin Ko
  • 46,526
  • 5
  • 91
  • 101
  • `undefined method 'find'` so I added an `s` to element. That's better. Still clicks the element with `active` so I changed it to `$browser.elements.find { |e| (e.class_name == 'icon' && e.class_name != 'active')}.click`. _Still_ gets clicked! – thisisauserid Apr 15 '13 at 19:31
  • Thanks for catching the typo for `elements`. I double-checked and the solution seems to work for me. Can you please provide a sample html to test against? – Justin Ko Apr 15 '13 at 19:35
  • Oops, nope, yep, your're right! This works! A non-regex solution would be so much faster but this will do for now. Thanks! – thisisauserid Apr 15 '13 at 20:41
1

Failing the ability to use a regex, another option would be to get a collection of matching items, and then inspect them more closely, clicking when you find one that works and abandoning the collection at that point.

browswer.elements(:class => "icon").each do |possible|
  unless possible.attribute_value("class").include? "active"
    possible.click
    break
  end
end

I'm not always a big fan of unless, but in this case it results in readable code, so I used it

for troubleshooting, lets see what is being shown for the class info on the elements in that collection

browswer.elements(:class => "icon").each do |possible|
  puts possible.attribute_value("class")
end
Chuck van der Linden
  • 6,660
  • 2
  • 28
  • 43
  • This looked fool-proof to me (and as a bonus is faster than regex) but it _still_ gets clicked. I also tried 'include? "icon active"` (still clicked) and 'include? /active/ which gave me a `can't convert Regexp into String`. – thisisauserid Apr 15 '13 at 19:17
  • We might have to look at what gets returned by `.attribute_value("class")` – Chuck van der Linden Apr 15 '13 at 22:33