Monday, August 8, 2011

CSS selector objects and methods for testing in Ruby

Let's say, for a given web page, you are writing some tests. And, supposing, you want to test whether the page contains a single DIV, of CSS class, 'only'.

And, you want to know whether the DIV (or the page) contains a single FORM, of CSS class, 'good-form'.

And further, you want to know whether the FORM (or the page) contains (among other things) a single INPUT, of CSS class, 'my-input'.

And further, whether that INPUT has CSS attribute, 'type=text'.

So, a CSS selector, useful for finding that on a web page, would be:

    div.only > form.good-form > input.my-input [type=text]

Right?

Maybe (in your test) specifying descendents instead of children would have a better flexibility, allowing later intervening DIV's (for instance). With this difference, the new CSS selector would be:

    div.only  form.good-form  input.my-input [type=text]

How might one like to specify these selectors, speculatively and ideally? Even better than the CSS selector strings in Ruby above might be... what?

(A.)   HTML elements

Well, it might be nice if code for DIV's could work simply like this:

    D.class 'only'

(Unfortunately, this would clobber Object#class, probably interfering with essential Ruby functionality elsewhere in our application.) We could compromise and give it a longer method name:

    D.css_class 'only'

(B.)   HTML Attributes

A web page might also, in addition to an INPUT element carrying all of the desired attributes, supply some other INPUT with fewer of them, or indeed another kind of INPUT entirely.

So, obviously, it is good to test for:

o The desired number of INPUT's (in total), and
o The presence of each INPUT, carrying all the attributes desired.

In light of this, wouldn't it be nice if one easy method invocation could assert the unique presence of exactly one INPUT, plus its unique presence carrying a specific set of attributes?

Another thing possibly useful might be checking the attributes one at a time; I don't know.

The same would probably apply also to any other kind of HTML element having attributes.

A seemingly good way to invoke the method would be:

    attribute 'type', 'text', 'value', 'your name here'

And, for unpaired attributes:

    attribute 'hidden'

So: how could these semantics really work? Here's an example, carrying the idea further:

(C.)   An implementation

# Fuller Example of Web Page Testing, Using CSS Selector Objects and Methods:

module CssSelectorConstants; D,F,I = %w[ div form input ].map{|e| CssString e} end

class SomeTestClass

    extend CssSelectorConstants

    def test_web_page
        d_o = D. css_class 'only'
        fg = d_o.descend(F).css_class 'good-form'
        im = fg. descend(I).css_class 'my-input'

        a_t = im.attribute 'type', 'text'
        atv = im.attribute 'type', 'text', 'value', 'your name here'
        av = im.attribute 'value', 'your name here'

# There should be exactly one DIV, 'div.only':
        assert_select d_o, 1 do

# And within that, exactly one FORM, 'form.good-form':
            assert_select fg, 1 do
#                                                                         I.e., exactly one:
#                                                                                   div.only form.good-form

# And within that, exactly one INPUT, 'input.my-input':
                assert_select im, 1
#                                                                         I.e., exactly one:
#                                                                                   div.only form.good-form input.my-input

# And exactly one INPUT, 'input.my-input[type=text]':
                assert_select a_t, 1
#                                                                         I.e., exactly one:
#                                                                                   div.only form.good-form input.my-input
#                                                                                   [type=text]

# And exactly one INPUT, 'input.my-input[value=your name here]':
                assert_select av, 1
#                                                                         I.e., exactly one:
#                                                                                   div.only form.good-form input.my-input
#                                                                                   [value=your name here]

# And exactly one INPUT, 'input.my-input[type=text,value=your name here]':
                assert_select atv, 1
#                                                                         I.e., exactly one:
#                                                                                   div.only form.good-form input.my-input
#                                                                                   [type=text,value=your name here]
            end #form
        end #div
    end #def
end #class

See also:
CSS selector objects & methods (for Rails; GitHub)

Copyright (c) 2011 Mark D. Blackwell.

No comments:

Post a Comment

Thanks for commenting on my post!