Problem
In HTML, the class
attribute is a space-separated list of classes for the element. The order of classes does not matter when applying style sheets. For example, in the following page, all 3 div
elements would look the same even though their class
value is different.
<html> <body> <div class="a b">1</div> <div class="b a">2</div> <div class="a b">3</div> </body> </html>
For many years, Watir’s :class
locator has been order dependent when using multiple classes:
# Only matches the exact order "a" and then "b" browser.divs(class: 'a b').map(&:text) #=> ["1", "3"] # Only matches the exact order "b" and then "a" browser.divs(class: 'b a').map(&:text) #=> ["2"]
Watir 6.5 introduced the ability to be order independent. This is done by providing an Array
of classes:
browser.divs(class: ['a', 'b']).map(&:text) #=> ["1", "2", "3"] browser.divs(class: ['b', 'a']).map(&:text) #=> ["1", "2", "3"]
For backwards compatibility, the decision was to leave the existing order dependent behaviour for String
values as-is. However, a deprecation warning will be generated:
browser.divs(class: 'a b').map(&:text) #=> 2017-08-16 21:50:17 WARN Watir [DEPRECATION] #=> Using the :class locator to locate multiple classes with #=> a String value is deprecated. Use Array instead.
Hopefully no one has to deal with a class order requirement, but if you do, what can you do to prevent the deprecation warning?
Solution
The new handling for Array
values still builds the XPath the same as when a String
value is used. The only difference is that the Array
value will “and” the items together. With only a single item in the Array
, the behaviour is the same as using a String
. As well, the Array
is not checked for items with spaces – ie is not subject to the deprecation warning.
This means that the order dependent classes can simply be passed as one of the Array
elements:
browser.divs(class: ['a b']).map(&:text) #=> ["1", "3"] browser.divs(class: ['b a']).map(&:text) #=> ["2"]
The XPath generated for the deprecated approach and this workaround only differ by an unimportant set of brackets:
# Using browser.divs(class: 'a b') {"using":"xpath","value":".//div[contains(concat(' ', @class, ' '), ' a b ')]"} # Using browser.divs(class: ['a b']) {"using":"xpath","value":".//div[(contains(concat(' ', @class, ' '), ' a b '))]"}