locator 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +175 -4
- data/TODO +1 -1
- data/lib/core_ext/hash/except.rb +11 -0
- data/lib/core_ext/hash/slice.rb +14 -0
- data/lib/locator.rb +2 -1
- data/lib/locator/dom.rb +1 -0
- data/lib/locator/dom/htmlunit.rb +10 -0
- data/lib/locator/dom/htmlunit/element.rb +80 -0
- data/lib/locator/dom/htmlunit/page.rb +11 -0
- data/lib/locator/dom/nokogiri/element.rb +15 -15
- data/lib/locator/dom/nokogiri/page.rb +3 -2
- data/lib/locator/element.rb +9 -4
- data/lib/locator/element/area.rb +1 -1
- data/lib/locator/element/button.rb +2 -2
- data/lib/locator/element/form.rb +1 -1
- data/lib/locator/element/form_element.rb +7 -1
- data/lib/locator/element/input.rb +2 -3
- data/lib/locator/element/label.rb +1 -1
- data/lib/locator/element/link.rb +1 -1
- data/lib/locator/element/select.rb +1 -1
- data/lib/locator/element/select_option.rb +1 -1
- data/lib/locator/element/text_area.rb +1 -1
- data/lib/locator/result.rb +22 -6
- data/lib/locator/version.rb +1 -1
- data/lib/locator/xpath.rb +4 -0
- data/test/locator/dom/htmlunit_test.rb +77 -0
- data/test/locator/dom/nokogiri_test.rb +16 -0
- data/test/locator/element/button_test.rb +4 -4
- data/test/locator/element/field_test.rb +12 -12
- data/test/locator/element/form_test.rb +4 -4
- data/test/locator/element/hidden_field_test.rb +36 -0
- data/test/locator/element/label_test.rb +3 -3
- data/test/locator/element/select_option_test.rb +5 -5
- data/test/locator/element/select_test.rb +3 -3
- data/test/locator/element/text_area_test.rb +3 -3
- data/test/locator/element_test.rb +17 -7
- data/test/locator_test.rb +8 -8
- metadata +13 -2
data/README.textile
CHANGED
@@ -1,13 +1,184 @@
|
|
1
|
+
<a name="readme"></a>
|
2
|
+
|
1
3
|
h1. Locator
|
2
4
|
|
3
|
-
This
|
5
|
+
This Gem aims to extract common html element selection from testing tools such as "Webrat":http://github.com/brynary/webrat/blob/master/lib/webrat/core/locators.rb, "Capybara":http://github.com/jnicklas/capybara/blob/master/lib/capybara/xpath.rb or "Steam":http://github.com/svenfuchs/steam/blob/master/lib/steam/locators.rb. At its core it constructs "XPath":http://github.com/svenfuchs/locator/blob/master/lib/locator/xpath.rb objects using a simple "boolean expression engine":http://github.com/svenfuchs/locator/blob/master/lib/locator/boolean.rb in order to locate elements in a Nokogiri DOM. It provides a bunch of "Element classes":http://github.com/svenfuchs/locator/blob/master/lib/locator/element.rb that are targeted at implementing a Webrat-style DSL for convenience.
|
6
|
+
|
7
|
+
"See below":#why-another-library for why I strongly believe that this behavior should be implemented as a stand-alone library.
|
4
8
|
|
5
9
|
h2. Usage
|
6
10
|
|
7
|
-
Of course you can use the underlying implementation, too, but there are three main public methods which are supposed to give you access to all you need
|
11
|
+
Of course you can use the underlying implementation, too, but there are three main public methods which are supposed to give you access to all you need:
|
12
|
+
|
13
|
+
* @Locator.locate(html, *args)@
|
14
|
+
* @Locator.within(*args)@
|
15
|
+
* @Locator.xpath(*args)@
|
16
|
+
|
17
|
+
The following code examples all assume that you include the Locator module like so:
|
18
|
+
|
19
|
+
pre. include Locator
|
8
20
|
|
9
|
-
|
21
|
+
All the examples should work without doing so though. You should be able to statically call the same methods on the Locator module instead.
|
10
22
|
|
11
23
|
h3. Locating an element
|
12
24
|
|
13
|
-
|
25
|
+
There are three APIs for locating elements:
|
26
|
+
|
27
|
+
* using a locator type (e.g. @:link@) and/or required node attributes (e.g. @:id => 'foo'@) and/or a selector (e.g. @"The link"@)
|
28
|
+
* using an xpath
|
29
|
+
* using a css selector
|
30
|
+
|
31
|
+
In all cases you can use the Locator.locate method which wants you to pass the HTML code as the first argument:
|
32
|
+
|
33
|
+
pre. locate(html, *args)
|
34
|
+
|
35
|
+
h4. Locating elements using a locator type
|
36
|
+
|
37
|
+
If you pass a *Symbol* as a first argument, then Locator will interpret this as a *locator type*. For a list of "supported locator types":#locator-types see below, these sometimes but do not always equal HTML tag node names.
|
38
|
+
|
39
|
+
E.g. the @:form@ tag will simply locate HTML form tags. Locator types like @:button@, @:field@ and @:link@ bake in some more knowledge about how we want to build a DSL (e.g. a helper locator_button could use the Button locator type and match both HTML button tags and clickable input tags such as submit input tags).
|
40
|
+
|
41
|
+
So this will simply return the first link tag on the page:
|
42
|
+
|
43
|
+
pre. locate(html, :link)
|
44
|
+
|
45
|
+
h4. Locating elements using a selector
|
46
|
+
|
47
|
+
You can (alternatively or additionally) specify a *selector*. Selectors will be matched against matchable values depending on the locator type, "see below":#matchable-values for details.
|
48
|
+
|
49
|
+
E.g. to find a link that has the text @"Click here!"@ you could use any of the following calls:
|
50
|
+
|
51
|
+
<pre>locate(html, :link, "Click here!")
|
52
|
+
locate(html, :link, "Click")
|
53
|
+
locate(html, :link, "here!")
|
54
|
+
locate(html, "Click")</pre>
|
55
|
+
|
56
|
+
And so on. Note that Locator will pick the *outermost* element with the *shortest matching value*, see below for details about "matchable values":#matchable-values.
|
57
|
+
|
58
|
+
h4. Locating elements using attributes
|
59
|
+
|
60
|
+
You can (alternatively or additionally) specify required attributes. E.g. to find a link with the id @"foo"@ you can use:
|
61
|
+
|
62
|
+
pre. locate(html, :link, :id => "foo")
|
63
|
+
|
64
|
+
You can combine that with a selector. E.g. this will locate a link that has a text "click" and a class "foo":
|
65
|
+
|
66
|
+
pre. locate(html, :link, "click", :class => "foo")
|
67
|
+
|
68
|
+
All attributes are matched by equality. I.e. when you pass @:id => "foo"@ then only elements are matched that have the exact id attribute "foo". The only *exception* from this is the @:class@ attribute. When you pass @:class => "foo"@ then this is semantically equivalent to saying "an element that has the class 'foo'".
|
69
|
+
|
70
|
+
(Technically the class attribute is matched using an xpath @*[contains(concat(' ', @class, ' '), concat(' ', "foo", ' '))]@, i.e. leading and tailing spaces are added to the element's class attribute value and the requested value, and then it checks that the requested value is contained in the actual class attribute value.)
|
71
|
+
|
72
|
+
h4. Locating elements using an XPath or CSS selector
|
73
|
+
|
74
|
+
Instead using the API described above you can also specify an XPath or CSS selector like so:
|
75
|
+
|
76
|
+
<pre>locate(html, :xpath => "//div[@id='foo']")
|
77
|
+
locate(html, :xpath => "div#foo")</pre>
|
78
|
+
|
79
|
+
These both would lookup the same div element with the id "foo".
|
80
|
+
|
81
|
+
h3. Scoping to an element
|
82
|
+
|
83
|
+
You can scope the element lookup to certain parts of your HTML Dom. E.g. this is useful when you have a couple of forms with the same input elements and you want to fill in a particular one.
|
84
|
+
|
85
|
+
You can use both the Locator.locate and Locator.within methods for this:
|
86
|
+
|
87
|
+
pre. within(:form, "login_form") { locate(html, :field, "password") }
|
88
|
+
|
89
|
+
Or:
|
90
|
+
|
91
|
+
pre. locate(html, :form, "login_form") { locate(html, :field, "password") }
|
92
|
+
|
93
|
+
h2. Appendix
|
94
|
+
|
95
|
+
<a name="locator-types"></a>
|
96
|
+
|
97
|
+
h3. Supported locator types
|
98
|
+
|
99
|
+
All types will match the id attribute, all form elements (input, textarea etc.) additionally match the name attribute. Some locators additionally match the value attribute and/or the node content (inner_text). Also see below for "matchable values":#matchable-values.
|
100
|
+
|
101
|
+
All form element locator types (like :field, :checkbox, :file etc.) can additionally be located through their label tag. E.g. when you have a label tag "Name" that points to a text input tag then you can locate the text input using the text selector "Name". E.g.:
|
102
|
+
|
103
|
+
pre. locate(html, :field, "Name")
|
104
|
+
|
105
|
+
Here's a list of supported locator types and any additional matchable values:
|
106
|
+
|
107
|
+
| *Type* | *Locates* | *Extra matchable* |
|
108
|
+
| :button | a button element or a submit, button or image input tag | content (for buttons), value (for inputs) |
|
109
|
+
| :check_box | a checkbox input tag | |
|
110
|
+
| :field | an input element (see below) or a textarea | |
|
111
|
+
| :file | a file input tag | |
|
112
|
+
| :form | a form tag | |
|
113
|
+
| :hidden_field | a hidden input tag | |
|
114
|
+
| :input | an input tag with a type of text, password , email, url, search, tel or color | |
|
115
|
+
| :label | a label tag | content |
|
116
|
+
| :link | a link (i.e. an :a tag with an href attribute) | content |
|
117
|
+
| :radio_button | a radio input tag | |
|
118
|
+
| :select | a select box | |
|
119
|
+
| :select_option | a select option tag | value, content |
|
120
|
+
| :text_area | a textarea tag | |
|
121
|
+
|
122
|
+
<a name="matchable-values"></a>
|
123
|
+
|
124
|
+
h3. Matchable values
|
125
|
+
|
126
|
+
When given a *selector* Locator (as in @locate("The link")@) will match it against different node values depending on the locator type. Node attributes like id, name and value need to equal the given selector. The node content needs to include the selector text (case sensitive).
|
127
|
+
|
128
|
+
Locator will then pick the *outermost* element with the *shortest* matching value.
|
129
|
+
|
130
|
+
| *Matchable* | *Looks at* | *Match type* |
|
131
|
+
| content | the element's full inner text | contained in content |
|
132
|
+
| id | the elements id attribute | equals |
|
133
|
+
| name | the elements name attribute | equals |
|
134
|
+
| value | the elements value attribute | equals |
|
135
|
+
|
136
|
+
E.g. @locate(html, :link, "link")@ will find both a link with the text @"link"@ as well as @"The link"@ but it will select and return the first one because it has the shorter matching value (i.e. @"link"@).
|
137
|
+
|
138
|
+
Locator regards the inner text of an element as its content. That means that the given HTML structure looks like this:
|
139
|
+
|
140
|
+
<pre><div>
|
141
|
+
<div>
|
142
|
+
Some <span>text</span>
|
143
|
+
</div>
|
144
|
+
</div></pre>
|
145
|
+
|
146
|
+
... then @locate(:div, "Some text")@ will locate the outer div because it a) ignores the nested span so that the content (inner text) is @"Some text"@ and b) the contents of both div elements are the same.
|
147
|
+
|
148
|
+
The same locator will return the inner div though when given the following HTML structure:
|
149
|
+
|
150
|
+
<pre><div>
|
151
|
+
<div>
|
152
|
+
Some <span>text</span>
|
153
|
+
</div>
|
154
|
+
here
|
155
|
+
</div></pre>
|
156
|
+
|
157
|
+
... because the content of the outer div now is @"Some text here"@ which is longer than @"Some text"@ and Locator returns the *outermost* element with the *shortest*@ matching value.
|
158
|
+
|
159
|
+
This might seem complicated at first but it is required and quite consistent if you think about it.
|
160
|
+
|
161
|
+
One obvious reason for returning the outermost element is that one wants @locate@ and @within@ behave the same way. If within would refer to the innermost element though then the following locator would not find any element in the HTML above which would be highly confusing:
|
162
|
+
|
163
|
+
<pre>within(:div) { within(:div) { locate(:span) } }</pre>
|
164
|
+
|
165
|
+
And an obvious reason for returning an element with the shortest matching value is that in the following case one will want @locate("The Link")@ to return the second element, not the first one:
|
166
|
+
|
167
|
+
<pre><a href="#">The link with extra text</a>
|
168
|
+
<a href="http://www.some-very-long-url.com">The link!</a></pre>
|
169
|
+
|
170
|
+
<a name="why-another-library"></a>
|
171
|
+
|
172
|
+
h3. Why another library?
|
173
|
+
|
174
|
+
In my opinion each of the libraries mentioned above do way to much. Amongst many other things they all implement tools for locating HTML elements from a Dom in some way. When we look at the details of the implementation they all do it differently though. So moving a test suite from Webrat to Capybara or Steam might be easy if only the simplest and most common helpers are used. But it can also be a huge pita because of all the tiny differences in those libraries: the APIs are simply not the same even if they seem to be at the first glimpse.
|
175
|
+
|
176
|
+
Thus, having a library that does nothing but locating elements but does this one thing well and can then be used by other solutions that add other features is the way to go.
|
177
|
+
|
178
|
+
By now Locator has been integrated to Steam (which already very much streamlined Steam's API). Obviously I would be humbled if other solutions would pick up Locator, too, and I do offer my help for working on this.
|
179
|
+
|
180
|
+
|
181
|
+
TODO
|
182
|
+
|
183
|
+
* add notes about using Locator::Element classes directly
|
184
|
+
|
data/TODO
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
class Hash
|
2
|
+
def slice!(*keys)
|
3
|
+
omit = slice(*self.keys - keys)
|
4
|
+
hash = slice(*keys)
|
5
|
+
replace(hash)
|
6
|
+
omit
|
7
|
+
end
|
8
|
+
|
9
|
+
def slice(*keys)
|
10
|
+
hash = self.class.new
|
11
|
+
keys.each { |k| hash[k] = self[k] if has_key?(k) }
|
12
|
+
hash
|
13
|
+
end
|
14
|
+
end unless Hash.method_defined?(:slice)
|
data/lib/locator.rb
CHANGED
data/lib/locator/dom.rb
CHANGED
@@ -0,0 +1,80 @@
|
|
1
|
+
module Locator
|
2
|
+
module Dom
|
3
|
+
module Htmlunit
|
4
|
+
class Element
|
5
|
+
attr_reader :element, :matches
|
6
|
+
|
7
|
+
def initialize(element)
|
8
|
+
@element = element || raise('nil passed as an element')
|
9
|
+
@matches = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def <=>(other)
|
13
|
+
to_s.length <=> other.to_s.length
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
element.getNodeName
|
18
|
+
end
|
19
|
+
|
20
|
+
def xpath
|
21
|
+
element.getCanonicalXPath
|
22
|
+
end
|
23
|
+
|
24
|
+
def css_path
|
25
|
+
raise 'not implemented'
|
26
|
+
end
|
27
|
+
|
28
|
+
def content
|
29
|
+
# TODO HtmlUnit has asText and getTextContent
|
30
|
+
element.getTextContent # Celerity normalizes this
|
31
|
+
end
|
32
|
+
|
33
|
+
# def inner_html
|
34
|
+
# element.inner_html
|
35
|
+
# end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
element.asXml
|
39
|
+
end
|
40
|
+
|
41
|
+
def attribute(name)
|
42
|
+
element.getAttribute(name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def element_by_id(id)
|
46
|
+
element_by_xpath("//*[@id='#{id}']")
|
47
|
+
end
|
48
|
+
|
49
|
+
def element_by_xpath(xpath)
|
50
|
+
if element = self.element.getFirstByXPath(xpath)
|
51
|
+
Element.new(element)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def elements_by_xpath(*xpaths)
|
56
|
+
xpaths.map do |xpath|
|
57
|
+
element.getByXPath(xpath).toArray.map { |e| Element.new(e) }
|
58
|
+
end.flatten
|
59
|
+
end
|
60
|
+
|
61
|
+
def elements_by_css(*rules)
|
62
|
+
elements_by_xpath(*::Nokogiri::CSS.xpath_for(*rules))
|
63
|
+
end
|
64
|
+
|
65
|
+
def ancestors
|
66
|
+
ancestors, node = [], element.getParentNode
|
67
|
+
until node.getNodeName == '#document'
|
68
|
+
ancestors.unshift(Element.new(node))
|
69
|
+
node = node.getParentNode
|
70
|
+
end
|
71
|
+
ancestors
|
72
|
+
end
|
73
|
+
|
74
|
+
def ancestor_of?(other)
|
75
|
+
other.element.ancestors.include?(element)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -14,7 +14,7 @@ module Locator
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def name
|
17
|
-
|
17
|
+
element.name
|
18
18
|
end
|
19
19
|
|
20
20
|
def xpath
|
@@ -37,32 +37,32 @@ module Locator
|
|
37
37
|
element.to_s
|
38
38
|
end
|
39
39
|
|
40
|
-
def tag_name
|
41
|
-
element.description.name
|
42
|
-
end
|
43
|
-
|
44
|
-
def ancestor_of?(other)
|
45
|
-
other.element.ancestors.include?(element)
|
46
|
-
end
|
47
|
-
|
48
40
|
def attribute(name)
|
49
41
|
element.attribute(name).to_s
|
50
42
|
end
|
51
43
|
|
52
|
-
def attributes(names)
|
53
|
-
names.map { |name| attribute(name) }
|
54
|
-
end
|
55
|
-
|
56
44
|
def element_by_id(id)
|
57
45
|
elements_by_xpath("//*[@id='#{id}']").first
|
58
46
|
end
|
59
47
|
|
48
|
+
def element_by_xpath(xpath)
|
49
|
+
elements_by_xpath(xpath).first
|
50
|
+
end
|
51
|
+
|
52
|
+
def elements_by_xpath(*xpaths)
|
53
|
+
element.xpath(*xpaths).map { |element| Element.new(element) }
|
54
|
+
end
|
55
|
+
|
60
56
|
def elements_by_css(*rules)
|
61
57
|
element.css(*rules).map { |element| Element.new(element) }
|
62
58
|
end
|
63
59
|
|
64
|
-
def
|
65
|
-
element.
|
60
|
+
def ancestors
|
61
|
+
element.ancestors
|
62
|
+
end
|
63
|
+
|
64
|
+
def ancestor_of?(other)
|
65
|
+
other.ancestors.include?(element)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
data/lib/locator/element.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'core_ext/hash/except'
|
2
|
+
require 'core_ext/hash/slice'
|
3
|
+
|
1
4
|
module Locator
|
2
5
|
class Element
|
3
6
|
autoload :Area, 'locator/element/area'
|
@@ -20,10 +23,11 @@ module Locator
|
|
20
23
|
|
21
24
|
attr_reader :name, :css, :locatables, :attributes
|
22
25
|
|
23
|
-
def initialize(
|
26
|
+
def initialize(*args)
|
27
|
+
attributes, name = args.last.is_a?(Hash) ? args.pop : {}, args.pop
|
24
28
|
@name = name
|
25
|
-
@
|
26
|
-
@
|
29
|
+
@attributes = attributes
|
30
|
+
@locatables = ((attributes.delete(:matches) || [:content]) + [:id]).uniq
|
27
31
|
end
|
28
32
|
|
29
33
|
def locate(*args)
|
@@ -46,7 +50,8 @@ module Locator
|
|
46
50
|
def lookup(scope, selector, attributes = {})
|
47
51
|
scope = scope.respond_to?(:elements_by_xpath) ? scope : Locator::Dom.page(scope)
|
48
52
|
xpath, css = attributes.delete(:xpath), attributes.delete(:css)
|
49
|
-
|
53
|
+
xpath = ::Nokogiri::CSS.xpath_for(*css).first if css
|
54
|
+
elements = scope.elements_by_xpath(xpath || xpath(attributes))
|
50
55
|
Result.new(elements).filter!(selector, locatables)
|
51
56
|
end
|
52
57
|
end
|
data/lib/locator/element/area.rb
CHANGED
@@ -2,8 +2,8 @@ module Locator
|
|
2
2
|
class Element
|
3
3
|
class Button < ElementsList
|
4
4
|
def initialize
|
5
|
-
input = Element.new(:input,
|
6
|
-
button = Element.new(:button, :
|
5
|
+
input = Element.new(:input, :matches => [:value], :type => %w(submit button image))
|
6
|
+
button = Element.new(:button, :matches => [:content])
|
7
7
|
super(input, button)
|
8
8
|
end
|
9
9
|
end
|
data/lib/locator/element/form.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
module Locator
|
2
2
|
class Element
|
3
3
|
class FormElement < Element
|
4
|
+
def initialize(name, attributes = {})
|
5
|
+
attributes[:matches] ||= []
|
6
|
+
attributes[:matches] << :name
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
4
10
|
def lookup(dom, selector, attributes)
|
5
|
-
super(dom, selector, attributes) + LabeledElement.new(name).send(:lookup, dom, selector, attributes)
|
11
|
+
super(dom, selector, attributes) + LabeledElement.new(name).send(:lookup, dom, selector, attributes.dup)
|
6
12
|
end
|
7
13
|
end
|
8
14
|
end
|
@@ -2,9 +2,8 @@ module Locator
|
|
2
2
|
class Element
|
3
3
|
class Input < FormElement
|
4
4
|
def initialize(attributes = {})
|
5
|
-
|
6
|
-
|
7
|
-
super(:input, { :equals => [:id, :name] }, attributes)
|
5
|
+
defaults = { :type => [:text, :password , :email, :url, :search, :tel, :color] }
|
6
|
+
super(:input, defaults.merge(attributes))
|
8
7
|
end
|
9
8
|
end
|
10
9
|
end
|
data/lib/locator/element/link.rb
CHANGED
data/lib/locator/result.rb
CHANGED
@@ -1,13 +1,29 @@
|
|
1
1
|
module Locator
|
2
2
|
class Result < Array
|
3
|
+
MATCH_TYPES = {
|
4
|
+
:alt => :contains,
|
5
|
+
:title => :contains,
|
6
|
+
:content => :contains
|
7
|
+
}
|
8
|
+
|
3
9
|
class << self
|
10
|
+
def matches?(name, value, selector)
|
11
|
+
value = normalize_whitespace(value)
|
12
|
+
case selector
|
13
|
+
when Regexp
|
14
|
+
value =~ selector
|
15
|
+
else
|
16
|
+
type = MATCH_TYPES[name] || :equals
|
17
|
+
send("#{type}?", value, selector)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
4
21
|
def equals?(value, selector)
|
5
22
|
value == selector
|
6
23
|
end
|
7
24
|
|
8
|
-
def
|
9
|
-
value
|
10
|
-
Regexp === selector ? value =~ selector : value.include?(selector)
|
25
|
+
def contains?(value, selector)
|
26
|
+
value.include?(selector)
|
11
27
|
end
|
12
28
|
|
13
29
|
def normalize_whitespace(value)
|
@@ -20,7 +36,7 @@ module Locator
|
|
20
36
|
end
|
21
37
|
|
22
38
|
def filter(selector, locatables)
|
23
|
-
selector ?
|
39
|
+
selector ? filter_by(selector, locatables) : self
|
24
40
|
end
|
25
41
|
|
26
42
|
def sort!
|
@@ -34,11 +50,11 @@ module Locator
|
|
34
50
|
|
35
51
|
protected
|
36
52
|
|
37
|
-
def filter_by(
|
53
|
+
def filter_by(selector, attributes)
|
38
54
|
select do |element|
|
39
55
|
Array(attributes).any? do |name|
|
40
56
|
value = name == :content ? element.content : element.attribute(name.to_s)
|
41
|
-
element.matches << value if self.class.
|
57
|
+
element.matches << value if self.class.matches?(name, value, selector)
|
42
58
|
end
|
43
59
|
end
|
44
60
|
end
|
data/lib/locator/version.rb
CHANGED
data/lib/locator/xpath.rb
CHANGED
@@ -0,0 +1,77 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
$: << '~/Development/projects/steam/lib'
|
4
|
+
require 'steam'
|
5
|
+
include Steam
|
6
|
+
|
7
|
+
Java.load(Dir["#{Steam.config[:html_unit][:java_path]}/*.jar"].join(':'))
|
8
|
+
Java.import 'com.gargoylesoftware.htmlunit.WebClient'
|
9
|
+
Java.import 'com.gargoylesoftware.htmlunit.BrowserVersion'
|
10
|
+
|
11
|
+
class HtmlunitElementTest < Test::Unit::TestCase
|
12
|
+
include Locator, Java::Com::Gargoylesoftware::Htmlunit
|
13
|
+
|
14
|
+
def setup
|
15
|
+
connection = Connection::Mock.new
|
16
|
+
client = WebClient.new(BrowserVersion.FIREFOX_3)
|
17
|
+
client.setWebConnection(Rjb::bind(Browser::HtmlUnit::Connection.new(connection), 'com.gargoylesoftware.htmlunit.WebConnection'))
|
18
|
+
|
19
|
+
connection.mock(:get, 'http://localhost:3000/', '<a href="#" class="foo">the link</a><div id="bar"></div>')
|
20
|
+
@dom = Dom::Htmlunit::Page.new(client.getPage('http://localhost:3000/'))
|
21
|
+
end
|
22
|
+
|
23
|
+
test "Element takes a Htmlunit::Page adapter" do
|
24
|
+
element = Element::Link.new.locate(@dom, 'the link', :class => 'foo')
|
25
|
+
assert element.name
|
26
|
+
end
|
27
|
+
|
28
|
+
test "responds to name" do
|
29
|
+
element = Element::Link.new.locate(@dom, 'the link')
|
30
|
+
assert_equal 'a', element.name
|
31
|
+
end
|
32
|
+
|
33
|
+
test "responds to xpath" do
|
34
|
+
element = Element::Link.new.locate(@dom, 'the link')
|
35
|
+
assert_equal '/html/body/a', element.xpath
|
36
|
+
end
|
37
|
+
|
38
|
+
test "responds to to_s" do
|
39
|
+
element = Element::Link.new.locate(@dom, 'the link')
|
40
|
+
assert_equal "<a href=\"#\" class=\"foo\">\n the link\n</a>\n", element.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
test "responds to content" do
|
44
|
+
element = Element::Link.new.locate(@dom, 'the link')
|
45
|
+
assert_equal "the link", element.content
|
46
|
+
end
|
47
|
+
|
48
|
+
test "responds to attribute" do
|
49
|
+
element = Element::Link.new.locate(@dom, 'the link')
|
50
|
+
assert_equal 'foo', element.attribute('class')
|
51
|
+
end
|
52
|
+
|
53
|
+
test "responds to element_by_id" do
|
54
|
+
element = Element.new(:html).locate(@dom)
|
55
|
+
assert_equal 'bar', element.element_by_id('bar').attribute('id')
|
56
|
+
end
|
57
|
+
|
58
|
+
test "responds to element_by_xpath" do
|
59
|
+
element = Element.new(:html).locate(@dom)
|
60
|
+
assert_equal 'bar', element.element_by_xpath('//html/body/div[@id="bar"]').attribute('id')
|
61
|
+
end
|
62
|
+
|
63
|
+
test "responds to elements_by_xpath" do
|
64
|
+
element = Element::Link.new.locate(@dom, 'the link')
|
65
|
+
assert_equal 'foo', element.attribute('class')
|
66
|
+
end
|
67
|
+
|
68
|
+
test "responds to ancestors" do
|
69
|
+
element = Element.new(:a).locate(@dom)
|
70
|
+
assert_equal %w(html body), element.ancestors.map { |e| e.name }
|
71
|
+
end
|
72
|
+
|
73
|
+
test "responds to ancestor_of?" do
|
74
|
+
element = Element.new(:a).locate(@dom)
|
75
|
+
assert_equal %w(html body), element.ancestors.map { |e| e.name }
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
class NokogiriTest < Test::Unit::TestCase
|
5
|
+
include Locator
|
6
|
+
|
7
|
+
# test "Element takes a Nokogiri dom" do
|
8
|
+
# dom = Nokogiri::HTML::Document.parse('<a href="#">the link</a>')
|
9
|
+
# assert Element.new.locate(dom, 'the link')
|
10
|
+
# end
|
11
|
+
|
12
|
+
test "Element takes a Nokogiri page adapter" do
|
13
|
+
dom = Locator::Dom::Nokogiri::Page.new('<a href="#">the link</a>')
|
14
|
+
assert Element.new.locate(dom, 'the link')
|
15
|
+
end
|
16
|
+
end
|
@@ -6,22 +6,22 @@ class ElementButtonTest < Test::Unit::TestCase
|
|
6
6
|
|
7
7
|
test "finds a button" do
|
8
8
|
html = '<button></button>'
|
9
|
-
assert_equal 'button', Button.new.locate(html).
|
9
|
+
assert_equal 'button', Button.new.locate(html).name
|
10
10
|
end
|
11
11
|
|
12
12
|
test "finds a submit input" do
|
13
13
|
html = '<input type="submit">'
|
14
|
-
assert_equal 'input', Button.new.locate(html).
|
14
|
+
assert_equal 'input', Button.new.locate(html).name
|
15
15
|
end
|
16
16
|
|
17
17
|
test "finds a button input" do
|
18
18
|
html = '<input type="button">'
|
19
|
-
assert_equal 'input', Button.new.locate(html).
|
19
|
+
assert_equal 'input', Button.new.locate(html).name
|
20
20
|
end
|
21
21
|
|
22
22
|
test "finds an image input" do
|
23
23
|
html = '<input type="image">'
|
24
|
-
assert_equal 'input', Button.new.locate(html).
|
24
|
+
assert_equal 'input', Button.new.locate(html).name
|
25
25
|
end
|
26
26
|
|
27
27
|
test "does not find a checkbox input" do
|
@@ -6,62 +6,62 @@ class ElementFieldTest < Test::Unit::TestCase
|
|
6
6
|
|
7
7
|
test "finds a textarea" do
|
8
8
|
html = '<textarea></textarea>'
|
9
|
-
assert_equal 'textarea', Field.new.locate(html).
|
9
|
+
assert_equal 'textarea', Field.new.locate(html).name
|
10
10
|
end
|
11
11
|
|
12
12
|
test "finds a text input" do
|
13
13
|
html = '<input type="text">'
|
14
|
-
assert_equal 'input', Field.new.locate(html).
|
14
|
+
assert_equal 'input', Field.new.locate(html).name
|
15
15
|
end
|
16
16
|
|
17
17
|
test "finds a password input" do
|
18
18
|
html = '<input type="password">'
|
19
|
-
assert_equal 'input', Field.new.locate(html).
|
19
|
+
assert_equal 'input', Field.new.locate(html).name
|
20
20
|
end
|
21
21
|
|
22
22
|
test "finds an email input" do
|
23
23
|
html = '<input type="email">'
|
24
|
-
assert_equal 'input', Field.new.locate(html).
|
24
|
+
assert_equal 'input', Field.new.locate(html).name
|
25
25
|
end
|
26
26
|
|
27
27
|
test "finds a url input" do
|
28
28
|
html = '<input type="url">'
|
29
|
-
assert_equal 'input', Field.new.locate(html).
|
29
|
+
assert_equal 'input', Field.new.locate(html).name
|
30
30
|
end
|
31
31
|
|
32
32
|
test "finds a search input" do
|
33
33
|
html = '<input type="search">'
|
34
|
-
assert_equal 'input', Field.new.locate(html).
|
34
|
+
assert_equal 'input', Field.new.locate(html).name
|
35
35
|
end
|
36
36
|
|
37
37
|
test "finds a tel input" do
|
38
38
|
html = '<input type="tel">'
|
39
|
-
assert_equal 'input', Field.new.locate(html).
|
39
|
+
assert_equal 'input', Field.new.locate(html).name
|
40
40
|
end
|
41
41
|
|
42
42
|
test "finds a color input" do
|
43
43
|
html = '<input type="color">'
|
44
|
-
assert_equal 'input', Field.new.locate(html).
|
44
|
+
assert_equal 'input', Field.new.locate(html).name
|
45
45
|
end
|
46
46
|
|
47
47
|
test "finds a text input by name" do
|
48
48
|
html = '<input type="text" name="foo">'
|
49
|
-
assert_equal 'input', Field.new.locate(html, 'foo').
|
49
|
+
assert_equal 'input', Field.new.locate(html, 'foo').name
|
50
50
|
end
|
51
51
|
|
52
52
|
test "finds a text input by class attribute" do
|
53
53
|
html = '<input type="text" class="foo">'
|
54
|
-
assert_equal 'input', Field.new.locate(html, :class => 'foo').
|
54
|
+
assert_equal 'input', Field.new.locate(html, :class => 'foo').name
|
55
55
|
end
|
56
56
|
|
57
57
|
test "finds an input by label content" do
|
58
58
|
html = '<label for="bar">foo</label><input type="text" id="bar">'
|
59
|
-
assert_equal 'input', Field.new.locate(html, 'foo').
|
59
|
+
assert_equal 'input', Field.new.locate(html, 'foo').name
|
60
60
|
end
|
61
61
|
|
62
62
|
test "finds an input by label content and input class" do
|
63
63
|
html = '<label for="bar">foo</label><input type="text" id="bar" class="baz">'
|
64
|
-
assert_equal 'input', Field.new.locate(html, 'foo', :class => 'baz').
|
64
|
+
assert_equal 'input', Field.new.locate(html, 'foo', :class => 'baz').name
|
65
65
|
end
|
66
66
|
|
67
67
|
test "does not find a checkbox input" do
|
@@ -6,22 +6,22 @@ class ElementFormTest < Test::Unit::TestCase
|
|
6
6
|
|
7
7
|
test "finds a form" do
|
8
8
|
html = '<form></form>'
|
9
|
-
assert_equal 'form', Form.new.locate(html).
|
9
|
+
assert_equal 'form', Form.new.locate(html).name
|
10
10
|
end
|
11
11
|
|
12
12
|
test "finds a form by id" do
|
13
13
|
html = '<form id="foo"></form>'
|
14
|
-
assert_equal 'form', Form.new.locate(html, 'foo').
|
14
|
+
assert_equal 'form', Form.new.locate(html, 'foo').name
|
15
15
|
end
|
16
16
|
|
17
17
|
test "finds a form by name" do
|
18
18
|
html = '<form name="foo"></form>'
|
19
|
-
assert_equal 'form', Form.new.locate(html, 'foo').
|
19
|
+
assert_equal 'form', Form.new.locate(html, 'foo').name
|
20
20
|
end
|
21
21
|
|
22
22
|
test "finds a form by class" do
|
23
23
|
html = '<form class="foo"></form>'
|
24
|
-
assert_equal 'form', Form.new.locate(html, :class => 'foo').
|
24
|
+
assert_equal 'form', Form.new.locate(html, :class => 'foo').name
|
25
25
|
end
|
26
26
|
|
27
27
|
test "does not find a form when id does not match" do
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require 'locator/element'
|
3
|
+
|
4
|
+
class ElementHiddenFieldTest < Test::Unit::TestCase
|
5
|
+
HiddenField = Locator::Element::HiddenField
|
6
|
+
|
7
|
+
test "finds a hidden input" do
|
8
|
+
html = '<input type="hidden" />'
|
9
|
+
assert_equal 'input', HiddenField.new.locate(html).name
|
10
|
+
end
|
11
|
+
|
12
|
+
test "finds a hidden input by id" do
|
13
|
+
html = '<input type="hidden" id="foo" />'
|
14
|
+
assert_equal 'input', HiddenField.new.locate(html, 'foo').name
|
15
|
+
end
|
16
|
+
|
17
|
+
test "finds a hidden input by name" do
|
18
|
+
html = '<input type="hidden" name="foo" />'
|
19
|
+
assert_equal 'input', HiddenField.new.locate(html, 'foo').name
|
20
|
+
end
|
21
|
+
|
22
|
+
test "finds a hidden input by class" do
|
23
|
+
html = '<input type="hidden" class="foo" />'
|
24
|
+
assert_equal 'input', HiddenField.new.locate(html, :class => 'foo').name
|
25
|
+
end
|
26
|
+
|
27
|
+
test "does not find a hidden input when id does not match" do
|
28
|
+
html = '<input type="hidden" id="bar" />'
|
29
|
+
assert_nil HiddenField.new.locate(html, :class => 'foo')
|
30
|
+
end
|
31
|
+
|
32
|
+
test "does not find a hidden input when class does not match" do
|
33
|
+
html = '<input type="hidden" class="bar" />'
|
34
|
+
assert_nil HiddenField.new.locate(html, :class => 'foo')
|
35
|
+
end
|
36
|
+
end
|
@@ -5,17 +5,17 @@ class ElementLabelTest < Test::Unit::TestCase
|
|
5
5
|
|
6
6
|
test "finds a label" do
|
7
7
|
html = '<label></label>'
|
8
|
-
assert_equal 'label', Label.new.locate(html).
|
8
|
+
assert_equal 'label', Label.new.locate(html).name
|
9
9
|
end
|
10
10
|
|
11
11
|
test "finds a label by id" do
|
12
12
|
html = '<label id="foo"></label>'
|
13
|
-
assert_equal 'label', Label.new.locate(html, 'foo').
|
13
|
+
assert_equal 'label', Label.new.locate(html, 'foo').name
|
14
14
|
end
|
15
15
|
|
16
16
|
test "finds a label by class" do
|
17
17
|
html = '<label class="foo"></label>'
|
18
|
-
assert_equal 'label', Label.new.locate(html, :class => 'foo').
|
18
|
+
assert_equal 'label', Label.new.locate(html, :class => 'foo').name
|
19
19
|
end
|
20
20
|
|
21
21
|
test "does not find a label when id does not match" do
|
@@ -6,27 +6,27 @@ class ElementSelectOptionOptionTest < Test::Unit::TestCase
|
|
6
6
|
|
7
7
|
test "finds an option" do
|
8
8
|
html = '<option></option>'
|
9
|
-
assert_equal 'option', SelectOption.new.locate(html).
|
9
|
+
assert_equal 'option', SelectOption.new.locate(html).name
|
10
10
|
end
|
11
11
|
|
12
12
|
test "finds an option by id" do
|
13
13
|
html = '<option id="foo"></option>'
|
14
|
-
assert_equal 'option', SelectOption.new.locate(html, 'foo').
|
14
|
+
assert_equal 'option', SelectOption.new.locate(html, 'foo').name
|
15
15
|
end
|
16
16
|
|
17
17
|
test "finds an option by value" do
|
18
18
|
html = '<option value="foo"></option>'
|
19
|
-
assert_equal 'option', SelectOption.new.locate(html, 'foo').
|
19
|
+
assert_equal 'option', SelectOption.new.locate(html, 'foo').name
|
20
20
|
end
|
21
21
|
|
22
22
|
test "finds an option by content" do
|
23
23
|
html = '<option>foo</option>'
|
24
|
-
assert_equal 'option', SelectOption.new.locate(html, 'foo').
|
24
|
+
assert_equal 'option', SelectOption.new.locate(html, 'foo').name
|
25
25
|
end
|
26
26
|
|
27
27
|
test "finds an option by class attribute" do
|
28
28
|
html = '<option class="foo"></option>'
|
29
|
-
assert_equal 'option', SelectOption.new.locate(html, :class => 'foo').
|
29
|
+
assert_equal 'option', SelectOption.new.locate(html, :class => 'foo').name
|
30
30
|
end
|
31
31
|
|
32
32
|
test "does not find an option when id does not match" do
|
@@ -5,17 +5,17 @@ class ElementSelectTest < Test::Unit::TestCase
|
|
5
5
|
|
6
6
|
test "finds a select" do
|
7
7
|
html = '<select id="foo" class="foo"></select><select id="bar" class="bar"></select>'
|
8
|
-
assert_equal 'select', Select.new.locate(html).
|
8
|
+
assert_equal 'select', Select.new.locate(html).name
|
9
9
|
end
|
10
10
|
|
11
11
|
test "finds a select by id" do
|
12
12
|
html = '<select id="foo"></select>'
|
13
|
-
assert_equal 'select', Select.new.locate(html, 'foo').
|
13
|
+
assert_equal 'select', Select.new.locate(html, 'foo').name
|
14
14
|
end
|
15
15
|
|
16
16
|
test "finds a select by class" do
|
17
17
|
html = '<select class="foo"></select>'
|
18
|
-
assert_equal 'select', Select.new.locate(html, :class => 'foo').
|
18
|
+
assert_equal 'select', Select.new.locate(html, :class => 'foo').name
|
19
19
|
end
|
20
20
|
|
21
21
|
test "does not find a select when id does not match" do
|
@@ -6,17 +6,17 @@ class ElementTextAreaTest < Test::Unit::TestCase
|
|
6
6
|
|
7
7
|
test "finds a textarea" do
|
8
8
|
html = '<textarea></textarea>'
|
9
|
-
assert_equal 'textarea', TextArea.new.locate(html).
|
9
|
+
assert_equal 'textarea', TextArea.new.locate(html).name
|
10
10
|
end
|
11
11
|
|
12
12
|
test "finds a textarea by id" do
|
13
13
|
html = '<textarea id="foo"></textarea>'
|
14
|
-
assert_equal 'textarea', TextArea.new.locate(html, 'foo').
|
14
|
+
assert_equal 'textarea', TextArea.new.locate(html, 'foo').name
|
15
15
|
end
|
16
16
|
|
17
17
|
test "finds a textarea by class" do
|
18
18
|
html = '<textarea class="foo"></textarea>'
|
19
|
-
assert_equal 'textarea', TextArea.new.locate(html, :class => 'foo').
|
19
|
+
assert_equal 'textarea', TextArea.new.locate(html, :class => 'foo').name
|
20
20
|
end
|
21
21
|
|
22
22
|
test "does not find a textarea when id does not match" do
|
@@ -15,39 +15,49 @@ class LocatorElementTest < Test::Unit::TestCase
|
|
15
15
|
end
|
16
16
|
|
17
17
|
test "xpath with attributes" do
|
18
|
-
xpath = Element.new(nil,
|
18
|
+
xpath = Element.new(nil, :type => 'type', :class => 'class').xpath
|
19
19
|
assert_equal ".//*[@type=\"type\"][contains(concat(' ', @class, ' '), concat(' ', \"class\", ' '))]", xpath
|
20
20
|
end
|
21
21
|
|
22
22
|
test "xpath with node name and attributes" do
|
23
|
-
xpath = Element.new(:div,
|
23
|
+
xpath = Element.new(:div, :type => 'type', :class => 'class').xpath
|
24
24
|
assert_equal ".//div[@type=\"type\"][contains(concat(' ', @class, ' '), concat(' ', \"class\", ' '))]", xpath
|
25
25
|
end
|
26
26
|
|
27
|
+
test "xpath with multiple node name and attributes" do
|
28
|
+
xpath = Element.new([:div, :p], :type => 'type').xpath
|
29
|
+
assert_equal ".//div[@type=\"type\"] | .//p[@type=\"type\"]", xpath
|
30
|
+
end
|
31
|
+
|
32
|
+
test "xpath merges given attributes with element attributes" do
|
33
|
+
xpath = Element.new(:div, :foo => 'foo').xpath(:bar => 'bar')
|
34
|
+
assert_equal ".//div[@foo=\"foo\"][@bar=\"bar\"]", xpath
|
35
|
+
end
|
36
|
+
|
27
37
|
# all
|
28
38
|
|
29
39
|
test "all selects all elements when given no attributes" do
|
30
40
|
html = '<a class="foo"></a><p class="bar"></p>'
|
31
41
|
elements = Element.new.all(html)
|
32
|
-
assert_equal %w(html body a p), elements.map { |element| element.
|
42
|
+
assert_equal %w(html body a p), elements.map { |element| element.name }
|
33
43
|
end
|
34
44
|
|
35
45
|
test "all selects all nodes with given node name" do
|
36
46
|
html = '<a class="foo"></a><p class="bar"></p>'
|
37
47
|
elements = Element.new('a').all(html)
|
38
|
-
assert_equal %w(a), elements.map { |element| element.
|
48
|
+
assert_equal %w(a), elements.map { |element| element.name }
|
39
49
|
end
|
40
50
|
|
41
51
|
test "all selects all nodes with attribute given to initialize" do
|
42
52
|
html = '<a class="foo"></a><p class="bar"></p>'
|
43
|
-
elements = Element.new(
|
44
|
-
assert_equal %w(a), elements.map { |element| element.
|
53
|
+
elements = Element.new(:class => 'foo').all(html)
|
54
|
+
assert_equal %w(a), elements.map { |element| element.name }
|
45
55
|
end
|
46
56
|
|
47
57
|
test "all selects all nodes with attribute given to all" do
|
48
58
|
html = '<a class="foo"></a><p class="bar"></p>'
|
49
59
|
elements = Element.new.all(html, :class => 'foo')
|
50
|
-
assert_equal %w(a), elements.map { |element| element.
|
60
|
+
assert_equal %w(a), elements.map { |element| element.name }
|
51
61
|
end
|
52
62
|
|
53
63
|
# locate
|
data/test/locator_test.rb
CHANGED
@@ -20,16 +20,16 @@ class LocatorTest < Test::Unit::TestCase
|
|
20
20
|
|
21
21
|
# locate
|
22
22
|
|
23
|
-
test "locates
|
24
|
-
html = '<html><body><form></form></body></html>'
|
23
|
+
test "locates the first element by node name" do
|
24
|
+
html = '<html><body><form id="foo"></form><form id="bar"></form></body></html>'
|
25
25
|
element = Locator.locate(html, :form)
|
26
|
-
assert_equal '
|
26
|
+
assert_equal 'foo', element.attribute('id')
|
27
27
|
end
|
28
28
|
|
29
|
-
test "locates
|
30
|
-
html = '<html><body><form></form></body></html>'
|
29
|
+
test "locates the first element by xpath" do
|
30
|
+
html = '<html><body><form id="foo"></form><form id="bar"></form></body></html>'
|
31
31
|
element = Locator.locate(html, :xpath => '//form')
|
32
|
-
assert_equal '
|
32
|
+
assert_equal 'foo', element.attribute('id')
|
33
33
|
end
|
34
34
|
|
35
35
|
# within
|
@@ -77,9 +77,9 @@ class LocatorTest < Test::Unit::TestCase
|
|
77
77
|
end
|
78
78
|
|
79
79
|
test "locate when given a block scopes the block to the located element" do
|
80
|
-
html = '<form></form><div><form><p id="
|
80
|
+
html = '<p id="foo"><p><form></form><div><form><p id="bar"><p></form></div>'
|
81
81
|
element = locate(html, :div) { locate(html, :form) { locate(html, :p) } }
|
82
|
-
assert_equal '
|
82
|
+
assert_equal 'bar', element.attribute('id')
|
83
83
|
end
|
84
84
|
|
85
85
|
test "locate does not yield the block when no element was found (would otherwise locate in global scope)" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: locator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sven Fuchs
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-21 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -26,10 +26,15 @@ files:
|
|
26
26
|
- README.textile
|
27
27
|
- Rakefile
|
28
28
|
- TODO
|
29
|
+
- lib/core_ext/hash/except.rb
|
30
|
+
- lib/core_ext/hash/slice.rb
|
29
31
|
- lib/core_ext/string/underscore.rb
|
30
32
|
- lib/locator.rb
|
31
33
|
- lib/locator/boolean.rb
|
32
34
|
- lib/locator/dom.rb
|
35
|
+
- lib/locator/dom/htmlunit.rb
|
36
|
+
- lib/locator/dom/htmlunit/element.rb
|
37
|
+
- lib/locator/dom/htmlunit/page.rb
|
33
38
|
- lib/locator/dom/nokogiri.rb
|
34
39
|
- lib/locator/dom/nokogiri/element.rb
|
35
40
|
- lib/locator/dom/nokogiri/page.rb
|
@@ -56,9 +61,12 @@ files:
|
|
56
61
|
- lib/locator/xpath.rb
|
57
62
|
- test/all.rb
|
58
63
|
- test/locator/boolean_test.rb
|
64
|
+
- test/locator/dom/htmlunit_test.rb
|
65
|
+
- test/locator/dom/nokogiri_test.rb
|
59
66
|
- test/locator/element/button_test.rb
|
60
67
|
- test/locator/element/field_test.rb
|
61
68
|
- test/locator/element/form_test.rb
|
69
|
+
- test/locator/element/hidden_field_test.rb
|
62
70
|
- test/locator/element/label_test.rb
|
63
71
|
- test/locator/element/link_test.rb
|
64
72
|
- test/locator/element/select_option_test.rb
|
@@ -99,9 +107,12 @@ summary: Generic html element locators for integration testing
|
|
99
107
|
test_files:
|
100
108
|
- test/all.rb
|
101
109
|
- test/locator/boolean_test.rb
|
110
|
+
- test/locator/dom/htmlunit_test.rb
|
111
|
+
- test/locator/dom/nokogiri_test.rb
|
102
112
|
- test/locator/element/button_test.rb
|
103
113
|
- test/locator/element/field_test.rb
|
104
114
|
- test/locator/element/form_test.rb
|
115
|
+
- test/locator/element/hidden_field_test.rb
|
105
116
|
- test/locator/element/label_test.rb
|
106
117
|
- test/locator/element/link_test.rb
|
107
118
|
- test/locator/element/select_option_test.rb
|