locator 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.textile +5 -0
- data/Rakefile +19 -0
- data/lib/core_ext/string/underscore.rb +5 -0
- data/lib/locator/boolean.rb +50 -0
- data/lib/locator/dom/nokogiri/element.rb +49 -0
- data/lib/locator/dom/nokogiri/page.rb +25 -0
- data/lib/locator/dom/nokogiri.rb +10 -0
- data/lib/locator/dom.rb +21 -0
- data/lib/locator/element/area.rb +9 -0
- data/lib/locator/element/button.rb +11 -0
- data/lib/locator/element/elements_list.rb +15 -0
- data/lib/locator/element/field.rb +10 -0
- data/lib/locator/element/form.rb +9 -0
- data/lib/locator/element/label.rb +9 -0
- data/lib/locator/element/labeled_element.rb +17 -0
- data/lib/locator/element/link.rb +9 -0
- data/lib/locator/element/select.rb +9 -0
- data/lib/locator/element/select_option.rb +9 -0
- data/lib/locator/element/text_area.rb +9 -0
- data/lib/locator/element.rb +45 -0
- data/lib/locator/version.rb +3 -0
- data/lib/locator/xpath.rb +38 -0
- data/lib/locator.rb +52 -0
- data/test/all.rb +3 -0
- data/test/locator/boolean_test.rb +40 -0
- data/test/locator/element/button_test.rb +26 -0
- data/test/locator/element/field_test.rb +60 -0
- data/test/locator/element/form_test.rb +30 -0
- data/test/locator/element/label_test.rb +26 -0
- data/test/locator/element/link_test.rb +34 -0
- data/test/locator/element/select_option_test.rb +34 -0
- data/test/locator/element/select_test.rb +26 -0
- data/test/locator/element/text_area_test.rb +26 -0
- data/test/locator/element_test.rb +40 -0
- data/test/locator/xpath_test.rb +50 -0
- data/test/locator_test.rb +31 -0
- data/test/test_helper.rb +46 -0
- metadata +104 -0
data/README.textile
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
h1. Locator
|
2
|
+
|
3
|
+
This library 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.
|
4
|
+
|
5
|
+
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.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
require File.expand_path("../lib/locator/version", __FILE__)
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
Jeweler::Tasks.new do |s|
|
8
|
+
s.name = "locator"
|
9
|
+
s.version = Locator::VERSION
|
10
|
+
s.summary = "Generic html element locators for integration testing"
|
11
|
+
s.email = "svenfuchs@artweb-design.de"
|
12
|
+
s.homepage = "http://github.com/svenfuchs/locator"
|
13
|
+
s.description = "Generic html element locators for integration testing"
|
14
|
+
s.authors = ['Sven Fuchs']
|
15
|
+
s.files = FileList["[A-Z]*", "{lib,test,vendor}/**/*"]
|
16
|
+
end
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
19
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class Locator
|
2
|
+
module Boolean
|
3
|
+
class Terms < Array
|
4
|
+
def and!(other)
|
5
|
+
replace(self.dup.and(other))
|
6
|
+
end
|
7
|
+
|
8
|
+
def and(other)
|
9
|
+
other.empty? ? self : And.new(self, other)
|
10
|
+
end
|
11
|
+
alias & and
|
12
|
+
|
13
|
+
def or!(other)
|
14
|
+
replace(self.dup.or(other))
|
15
|
+
end
|
16
|
+
|
17
|
+
def or(other)
|
18
|
+
other.empty? ? self : Or.new(self, other)
|
19
|
+
end
|
20
|
+
alias | and
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
flatten.join(Or.operator)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Accessors
|
28
|
+
def operator=(operator); @operator = operator end
|
29
|
+
def operator; @operator end
|
30
|
+
end
|
31
|
+
|
32
|
+
class And < Terms
|
33
|
+
extend Accessors
|
34
|
+
self.operator = ' AND '
|
35
|
+
|
36
|
+
def initialize(lft, rgt)
|
37
|
+
replace(lft.map { |l| rgt.map { |r| "#{l}#{self.class.operator}#{r}" } })
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Or < Terms
|
42
|
+
extend Accessors
|
43
|
+
self.operator = ' OR '
|
44
|
+
|
45
|
+
def initialize(lft, rgt)
|
46
|
+
replace([lft, rgt])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class Locator
|
2
|
+
module Dom
|
3
|
+
module Nokogiri
|
4
|
+
class Element
|
5
|
+
attr_reader :element
|
6
|
+
|
7
|
+
def initialize(element)
|
8
|
+
@element = element
|
9
|
+
end
|
10
|
+
|
11
|
+
def xpath
|
12
|
+
element.path.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def css_path
|
16
|
+
element.css_path.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def inner_html
|
20
|
+
element.inner_html
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
element.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def tag_name
|
28
|
+
element.description.name
|
29
|
+
end
|
30
|
+
|
31
|
+
def ancestor_of?(other)
|
32
|
+
other.element.ancestors.include?(element)
|
33
|
+
end
|
34
|
+
|
35
|
+
def attribute(name)
|
36
|
+
element.attribute(name).to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
def attributes(names)
|
40
|
+
names.map { |name| attribute(name) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def elements_by_xpath(xpath)
|
44
|
+
element.xpath(xpath).map { |element| Element.new(element) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Locator
|
2
|
+
module Dom
|
3
|
+
module Nokogiri
|
4
|
+
class Page
|
5
|
+
attr_reader :dom
|
6
|
+
|
7
|
+
def initialize(html)
|
8
|
+
@dom = ::Nokogiri::HTML::Document.parse(html)
|
9
|
+
end
|
10
|
+
|
11
|
+
def element_by_id(id)
|
12
|
+
elements_by_xpath("//*[@id='#{id}']").first
|
13
|
+
end
|
14
|
+
|
15
|
+
def elements_by_xpath(xpath)
|
16
|
+
@dom.xpath(xpath).map { |element| Element.new(element) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
@dom.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/locator/dom.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
class Locator
|
4
|
+
module Dom
|
5
|
+
autoload :Nokogiri, 'locator/dom/nokogiri'
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def adapter
|
9
|
+
@@adapter ||= Nokogiri
|
10
|
+
end
|
11
|
+
|
12
|
+
def adapter=(adapter)
|
13
|
+
@@adapter = adapter
|
14
|
+
end
|
15
|
+
|
16
|
+
def page(html)
|
17
|
+
adapter::Page.new(html)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class Locator
|
2
|
+
class Element
|
3
|
+
class Button < ElementsList
|
4
|
+
def initialize
|
5
|
+
input = Element.new('input', [:id, :name, :value, :alt], :type => %w(submit button image))
|
6
|
+
button = Element.new('button', [:id, :name, :content])
|
7
|
+
super(input, button)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Locator
|
2
|
+
class Element
|
3
|
+
class LabeledElement < Element
|
4
|
+
attr_reader :labeled
|
5
|
+
|
6
|
+
def initialize(name = '*', locatables = [], attributes = {})
|
7
|
+
@labeled = Element.new(name, [], attributes)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def compose(selector, attributes)
|
12
|
+
super
|
13
|
+
replace(self + labeled.and("[@id=//label[contains(.,\"#{selector}\")]/@for]")) if selector
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Locator
|
2
|
+
class Element < Xpath
|
3
|
+
autoload :Area, 'locator/element/area'
|
4
|
+
autoload :Button, 'locator/element/button'
|
5
|
+
autoload :ElementsList, 'locator/element/elements_list'
|
6
|
+
autoload :Field, 'locator/element/field'
|
7
|
+
autoload :Form, 'locator/element/form'
|
8
|
+
autoload :Label, 'locator/element/label'
|
9
|
+
autoload :LabeledElement, 'locator/element/labeled_element'
|
10
|
+
autoload :Link, 'locator/element/link'
|
11
|
+
autoload :Select, 'locator/element/select'
|
12
|
+
autoload :SelectOption, 'locator/element/select_option'
|
13
|
+
autoload :TextArea, 'locator/element/text_area'
|
14
|
+
|
15
|
+
attr_reader :locatables
|
16
|
+
|
17
|
+
def initialize(name = '*', locatables = [], attributes = {})
|
18
|
+
super(name)
|
19
|
+
@content = !!locatables.delete(:content)
|
20
|
+
@locatables = locatables
|
21
|
+
@attributes = attributes
|
22
|
+
end
|
23
|
+
|
24
|
+
def xpath(*args)
|
25
|
+
attributes = Hash === args.last ? args.pop : {}
|
26
|
+
compose(args.pop, attributes)
|
27
|
+
to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def content?
|
33
|
+
@content
|
34
|
+
end
|
35
|
+
|
36
|
+
def compose(selector, attributes)
|
37
|
+
attributes = @attributes.merge(attributes)
|
38
|
+
attributes.each { |name, value| and! equals(name, value) }
|
39
|
+
terms = []
|
40
|
+
terms << equals(locatables, selector) if selector
|
41
|
+
terms << contains(selector) if content? && selector
|
42
|
+
and!(terms) unless terms.empty?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Locator
|
2
|
+
autoload :Boolean, 'locator/boolean'
|
3
|
+
|
4
|
+
Boolean::Or.operator, Boolean::And.operator = ' | ', ''
|
5
|
+
|
6
|
+
class Xpath < Boolean::Terms
|
7
|
+
def initialize(name = nil, attributes = {})
|
8
|
+
names = Array(name || '*').map { |name| xpath?(name) ? name : ".//#{name}" }
|
9
|
+
super(names)
|
10
|
+
attributes.each { |name, value| and!(equals(name, value)) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def equals(names, values)
|
14
|
+
case values
|
15
|
+
when TrueClass
|
16
|
+
"[@#{names}]"
|
17
|
+
else
|
18
|
+
values = Array(values).map { |value| xpath?(value) ? value : "\"#{value}\"" }
|
19
|
+
expr = Array(names).map { |name| values.map { |value| "@#{name}=#{value}" } }
|
20
|
+
expr.empty? ? '' : '[' + expr.flatten.join(' or ') + ']'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def matches(name, value)
|
25
|
+
"[contains(@#{name},\"#{value}\")]"
|
26
|
+
end
|
27
|
+
|
28
|
+
def contains(value)
|
29
|
+
"/descendant-or-self::*[contains(.,\"#{value}\")]"
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def xpath?(string)
|
35
|
+
string.to_s[0, 1] == '/'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/locator.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'core_ext/string/underscore'
|
2
|
+
|
3
|
+
class Locator
|
4
|
+
autoload :Actions, 'locator/actions'
|
5
|
+
autoload :Dom, 'locator/dom'
|
6
|
+
autoload :Element, 'locator/element'
|
7
|
+
autoload :Xpath, 'locator/xpath'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def [](type)
|
11
|
+
locators[type.to_sym]
|
12
|
+
end
|
13
|
+
|
14
|
+
def xpath(type, *args)
|
15
|
+
Locator[type].new.xpath(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def locators
|
21
|
+
@locators ||= Hash[*Element.constants.map do |name|
|
22
|
+
[name.underscore.to_sym, Element.const_get(name)]
|
23
|
+
end.flatten]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :dom, :scopes
|
28
|
+
|
29
|
+
def initialize(dom)
|
30
|
+
@dom = dom.respond_to?(:elements_by_xpath) ? dom : Dom.page(dom)
|
31
|
+
@scopes = []
|
32
|
+
end
|
33
|
+
|
34
|
+
def locate(type, *args)
|
35
|
+
options = Hash === args.last ? args.last : {}
|
36
|
+
if scope = options.delete(:within)
|
37
|
+
within(scope) { locate(type, *args) }
|
38
|
+
else
|
39
|
+
path = type.is_a?(Symbol) ? Locator.xpath(type, *args) : type
|
40
|
+
scope = scopes.pop || dom
|
41
|
+
scope.elements_by_xpath(path).first
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# TODO currently only take an xpath or element
|
46
|
+
def within(scope, &block)
|
47
|
+
scope = scope.is_a?(Hash) ? scope.delete(:xpath) : scope
|
48
|
+
scope = locate(scope) unless scope.respond_to?(:xpath)
|
49
|
+
scopes.push(scope)
|
50
|
+
instance_eval(&block)
|
51
|
+
end
|
52
|
+
end
|
data/test/all.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
require 'locator/xpath'
|
3
|
+
|
4
|
+
class XpathBooleanTest < Test::Unit::TestCase
|
5
|
+
Or, And = Locator::Boolean::Or, Locator::Boolean::And
|
6
|
+
|
7
|
+
def setup
|
8
|
+
Or.operator, And.operator = ' | ', ' & '
|
9
|
+
end
|
10
|
+
|
11
|
+
test "a | b => a | b" do
|
12
|
+
path = Or.new('a', 'b').to_s
|
13
|
+
assert_equal "a | b", path
|
14
|
+
end
|
15
|
+
|
16
|
+
test "a & b => a & b" do
|
17
|
+
path = And.new('a', 'b').to_s
|
18
|
+
assert_equal "a & b", path
|
19
|
+
end
|
20
|
+
|
21
|
+
test "(a & b) | c => a & b | c" do
|
22
|
+
path = And.new('a', 'b').or('c').to_s
|
23
|
+
assert_equal "a & b | c", path
|
24
|
+
end
|
25
|
+
|
26
|
+
test "(a | b) & c => a & c | b & c" do
|
27
|
+
path = Or.new('a', 'b').and('c').to_s
|
28
|
+
assert_equal "a & c | b & c", path
|
29
|
+
end
|
30
|
+
|
31
|
+
test "(a & b | c) & d => a & b & d | c & d" do
|
32
|
+
path = And.new('a', 'b').or('c').and('d').to_s
|
33
|
+
assert_equal "a & b & d | c & d", path
|
34
|
+
end
|
35
|
+
|
36
|
+
test "(a & b | c) & (d | e) => a & b & d | a & b & e | c & d | c & e" do
|
37
|
+
path = And.new('a', 'b').or('c').and(Or.new('d', 'e')).to_s
|
38
|
+
assert_equal "a & b & d | a & b & e | c & d | c & e", path
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require 'locator/element'
|
3
|
+
|
4
|
+
class ElementButtonTest < Test::Unit::TestCase
|
5
|
+
Button = Locator::Element::Button
|
6
|
+
|
7
|
+
test "finds a button" do
|
8
|
+
assert_locate '<button></button>', Button.new.xpath
|
9
|
+
end
|
10
|
+
|
11
|
+
test "finds a submit input" do
|
12
|
+
assert_locate '<input type="submit">', Button.new.xpath
|
13
|
+
end
|
14
|
+
|
15
|
+
test "finds a button input" do
|
16
|
+
assert_locate '<input type="button">', Button.new.xpath
|
17
|
+
end
|
18
|
+
|
19
|
+
test "finds an image input" do
|
20
|
+
assert_locate '<input type="image">', Button.new.xpath
|
21
|
+
end
|
22
|
+
|
23
|
+
test "does not find a checkbox input" do
|
24
|
+
assert_no_locate '<input type="checkbox">', Button.new.xpath
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require 'locator/element'
|
3
|
+
|
4
|
+
class ElementFieldTest < Test::Unit::TestCase
|
5
|
+
Field = Locator::Element::Field
|
6
|
+
|
7
|
+
test "finds a textarea" do
|
8
|
+
assert_locate '<textarea></textarea>', Field.new.xpath
|
9
|
+
end
|
10
|
+
|
11
|
+
test "finds a text input" do
|
12
|
+
assert_locate '<input type="text">', Field.new.xpath
|
13
|
+
end
|
14
|
+
|
15
|
+
test "finds a password input" do
|
16
|
+
assert_locate '<input type="password">', Field.new.xpath
|
17
|
+
end
|
18
|
+
|
19
|
+
test "finds an email input" do
|
20
|
+
assert_locate '<input type="email">', Field.new.xpath
|
21
|
+
end
|
22
|
+
|
23
|
+
test "finds a url input" do
|
24
|
+
assert_locate '<input type="url">', Field.new.xpath
|
25
|
+
end
|
26
|
+
|
27
|
+
test "finds a search input" do
|
28
|
+
assert_locate '<input type="search">', Field.new.xpath
|
29
|
+
end
|
30
|
+
|
31
|
+
test "finds a tel input" do
|
32
|
+
assert_locate '<input type="tel">', Field.new.xpath
|
33
|
+
end
|
34
|
+
|
35
|
+
test "finds a color input" do
|
36
|
+
assert_locate '<input type="color">', Field.new.xpath
|
37
|
+
end
|
38
|
+
|
39
|
+
test "finds a text input by name" do
|
40
|
+
assert_locate '<input type="text" name="foo">', Field.new.xpath('foo')
|
41
|
+
end
|
42
|
+
|
43
|
+
test "finds a text input by class attribute" do
|
44
|
+
assert_locate '<input type="text" class="foo">', Field.new.xpath(:class => 'foo')
|
45
|
+
end
|
46
|
+
|
47
|
+
test "finds an input by label content" do
|
48
|
+
html = '<label for="bar">foo</label><input type="text" id="bar">'
|
49
|
+
assert_locate html, Field.new.xpath('foo'), '<input type="text" id="bar">'
|
50
|
+
end
|
51
|
+
|
52
|
+
test "finds an input by label content and input class" do
|
53
|
+
html = '<label for="bar">foo</label><input type="text" id="bar" class="baz">'
|
54
|
+
assert_locate html, Field.new.xpath('foo', :class => 'baz'), '<input type="text" id="bar" class="baz">'
|
55
|
+
end
|
56
|
+
|
57
|
+
test "does not find a checkbox input" do
|
58
|
+
assert_no_locate '<input type="checkbox">', Field.new.xpath
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require 'locator/element'
|
3
|
+
|
4
|
+
class ElementFormTest < Test::Unit::TestCase
|
5
|
+
Form = Locator::Element::Form
|
6
|
+
|
7
|
+
test "finds a form" do
|
8
|
+
assert_locate '<form></form>', Form.new.xpath
|
9
|
+
end
|
10
|
+
|
11
|
+
test "finds a form by id" do
|
12
|
+
assert_locate '<form id="foo"></form>', Form.new.xpath('foo')
|
13
|
+
end
|
14
|
+
|
15
|
+
test "finds a form by name" do
|
16
|
+
assert_locate '<form name="foo"></form>', Form.new.xpath('foo')
|
17
|
+
end
|
18
|
+
|
19
|
+
test "finds a form by class" do
|
20
|
+
assert_locate '<form class="foo"></form>', Form.new.xpath(:class => 'foo')
|
21
|
+
end
|
22
|
+
|
23
|
+
test "does not find a form when id does not match" do
|
24
|
+
assert_no_locate '<form id="bar"></form>', Form.new.xpath(:class => 'foo')
|
25
|
+
end
|
26
|
+
|
27
|
+
test "does not find a form when class does not match" do
|
28
|
+
assert_no_locate '<form class="bar"></form>', Form.new.xpath(:class => 'foo')
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require 'locator/element'
|
3
|
+
|
4
|
+
class ElementLabelTest < Test::Unit::TestCase
|
5
|
+
Label = Locator::Element::Label
|
6
|
+
|
7
|
+
test "finds a label" do
|
8
|
+
assert_locate '<label></label>', Label.new.xpath
|
9
|
+
end
|
10
|
+
|
11
|
+
test "finds a label by id" do
|
12
|
+
assert_locate '<label id="foo"></label>', Label.new.xpath('foo')
|
13
|
+
end
|
14
|
+
|
15
|
+
test "finds a label by class" do
|
16
|
+
assert_locate '<label class="foo"></label>', Label.new.xpath(:class => 'foo')
|
17
|
+
end
|
18
|
+
|
19
|
+
test "does not find a label when id does not match" do
|
20
|
+
assert_no_locate '<label id="bar"></label>', Label.new.xpath(:class => 'foo')
|
21
|
+
end
|
22
|
+
|
23
|
+
test "does not find a label when class does not match" do
|
24
|
+
assert_no_locate '<label class="bar"></label>', Label.new.xpath(:class => 'foo')
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require 'locator/element'
|
3
|
+
|
4
|
+
class ElementLinkTest < Test::Unit::TestCase
|
5
|
+
Link = Locator::Element::Link
|
6
|
+
|
7
|
+
test "finds a link" do
|
8
|
+
assert_locate '<a href=""></a>', Link.new.xpath
|
9
|
+
end
|
10
|
+
|
11
|
+
test "finds a link by id" do
|
12
|
+
assert_locate '<a href="" id="foo"></a>', Link.new.xpath('foo')
|
13
|
+
end
|
14
|
+
|
15
|
+
test "finds a link by class" do
|
16
|
+
assert_locate '<a href="" class="foo"></a>', Link.new.xpath(:class => 'foo')
|
17
|
+
end
|
18
|
+
|
19
|
+
test "finds an link by content" do
|
20
|
+
assert_locate '<a href="">foo</a>', Link.new.xpath('foo')
|
21
|
+
end
|
22
|
+
|
23
|
+
test "does not find an anchor" do
|
24
|
+
assert_no_locate '<a name=""></a>', Link.new.xpath
|
25
|
+
end
|
26
|
+
|
27
|
+
test "does not find a link when id does not match" do
|
28
|
+
assert_no_locate '<a href="" id="bar"></a>', Link.new.xpath(:class => 'foo')
|
29
|
+
end
|
30
|
+
|
31
|
+
test "does not find a link when class does not match" do
|
32
|
+
assert_no_locate '<a href="" class="bar"></a>', Link.new.xpath(:class => 'foo')
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require 'locator/element'
|
3
|
+
|
4
|
+
class ElementSelectOptionOptionTest < Test::Unit::TestCase
|
5
|
+
SelectOption = Locator::Element::SelectOption
|
6
|
+
|
7
|
+
test "finds an option" do
|
8
|
+
assert_locate '<option></option>', SelectOption.new.xpath
|
9
|
+
end
|
10
|
+
|
11
|
+
test "finds an option by id" do
|
12
|
+
assert_locate '<option id="foo"></option>', SelectOption.new.xpath('foo')
|
13
|
+
end
|
14
|
+
|
15
|
+
test "finds an option by value" do
|
16
|
+
assert_locate '<option value="foo"></option>', SelectOption.new.xpath('foo')
|
17
|
+
end
|
18
|
+
|
19
|
+
test "finds an option by content" do
|
20
|
+
assert_locate '<option>foo</option>', SelectOption.new.xpath('foo')
|
21
|
+
end
|
22
|
+
|
23
|
+
test "finds an option by class attribute" do
|
24
|
+
assert_locate '<option class="foo"></option>', SelectOption.new.xpath(:class => 'foo')
|
25
|
+
end
|
26
|
+
|
27
|
+
test "does not find an option when id does not match" do
|
28
|
+
assert_no_locate '<option id="bar"></option>', SelectOption.new.xpath(:class => 'foo')
|
29
|
+
end
|
30
|
+
|
31
|
+
test "does not find an option when class does not match" do
|
32
|
+
assert_no_locate '<option class="bar"></option>', SelectOption.new.xpath(:class => 'foo')
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require 'locator/element'
|
3
|
+
|
4
|
+
class ElementSelectTest < Test::Unit::TestCase
|
5
|
+
Select = Locator::Element::Select
|
6
|
+
|
7
|
+
test "finds a select" do
|
8
|
+
assert_locate '<select></select>', Select.new.xpath
|
9
|
+
end
|
10
|
+
|
11
|
+
test "finds a select by id" do
|
12
|
+
assert_locate '<select id="foo"></select>', Select.new.xpath('foo')
|
13
|
+
end
|
14
|
+
|
15
|
+
test "finds a select by class" do
|
16
|
+
assert_locate '<select class="foo"></select>', Select.new.xpath(:class => 'foo')
|
17
|
+
end
|
18
|
+
|
19
|
+
test "does not find a select when id does not match" do
|
20
|
+
assert_no_locate '<select id="bar"></select>', Select.new.xpath(:class => 'foo')
|
21
|
+
end
|
22
|
+
|
23
|
+
test "does not find a select when class does not match" do
|
24
|
+
assert_no_locate '<select class="bar"></select>', Select.new.xpath(:class => 'foo')
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
+
require 'locator/element'
|
3
|
+
|
4
|
+
class ElementTextAreaTest < Test::Unit::TestCase
|
5
|
+
TextArea = Locator::Element::TextArea
|
6
|
+
|
7
|
+
test "finds a textarea" do
|
8
|
+
assert_locate '<textarea></textarea>', TextArea.new.xpath
|
9
|
+
end
|
10
|
+
|
11
|
+
test "finds a textarea by id" do
|
12
|
+
assert_locate '<textarea id="foo"></textarea>', TextArea.new.xpath('foo')
|
13
|
+
end
|
14
|
+
|
15
|
+
test "finds a textarea by class" do
|
16
|
+
assert_locate '<textarea class="foo"></textarea>', TextArea.new.xpath(:class => 'foo')
|
17
|
+
end
|
18
|
+
|
19
|
+
test "does not find a textarea when id does not match" do
|
20
|
+
assert_no_locate '<textarea id="bar"></textarea>', TextArea.new.xpath(:class => 'foo')
|
21
|
+
end
|
22
|
+
|
23
|
+
test "does not find a textarea when class does not match" do
|
24
|
+
assert_no_locate '<textarea class="bar"></textarea>', TextArea.new.xpath(:class => 'foo')
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
require 'locator/element'
|
3
|
+
|
4
|
+
class ElementTest < Test::Unit::TestCase
|
5
|
+
def xpath(*args)
|
6
|
+
Locator::Element.new('foo', [:id, :content]).xpath(*args)
|
7
|
+
end
|
8
|
+
|
9
|
+
test "no selector given => finds all element" do
|
10
|
+
assert_locate '<foo></foo><foo></foo>', xpath, ['<foo></foo>', '<foo></foo>']
|
11
|
+
end
|
12
|
+
|
13
|
+
test "given selector equals content => finds the element" do
|
14
|
+
assert_locate '<foo>foo</foo>', xpath('foo')
|
15
|
+
end
|
16
|
+
|
17
|
+
test "given selector contained in content => finds the element" do
|
18
|
+
assert_locate '<foo>foo!</foo>', xpath('foo')
|
19
|
+
end
|
20
|
+
|
21
|
+
test "given selector equals id => finds the element" do
|
22
|
+
assert_locate '<foo id="foo"></foo>', xpath('foo')
|
23
|
+
end
|
24
|
+
|
25
|
+
test "given selector contained in id => does not find the element" do
|
26
|
+
assert_no_locate '<foo id="foobar"></foo>', xpath('foo')
|
27
|
+
end
|
28
|
+
|
29
|
+
test "given attribute equals attribute => finds the element" do
|
30
|
+
assert_locate '<foo class="foo">a</foo>', xpath(:class => 'foo')
|
31
|
+
end
|
32
|
+
|
33
|
+
test "given attribute contained in attribute => does not find the element" do
|
34
|
+
assert_no_locate '<foo class="foo-bar">a</foo>', xpath(:class => 'foo')
|
35
|
+
end
|
36
|
+
|
37
|
+
test "selector + attributes => finds the element" do
|
38
|
+
assert_locate '<foo class="foo" title="bar">bar</foo>', xpath('bar', :class => 'foo', :title => 'bar')
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
require 'locator/xpath'
|
3
|
+
|
4
|
+
class XpathTest < Test::Unit::TestCase
|
5
|
+
Xpath = Locator::Xpath
|
6
|
+
|
7
|
+
def setup
|
8
|
+
Locator::Boolean::Or.operator, Locator::Boolean::And.operator = ' | ', ''
|
9
|
+
end
|
10
|
+
|
11
|
+
test "simple xpath" do
|
12
|
+
path = Xpath.new('input').to_s
|
13
|
+
assert_equal ".//input", path
|
14
|
+
end
|
15
|
+
|
16
|
+
test "xpath with alternate nodenames" do
|
17
|
+
path = Xpath.new(['input', 'button']).to_s
|
18
|
+
assert_equal ".//input | .//button", path
|
19
|
+
end
|
20
|
+
|
21
|
+
test "xpath with attributes" do
|
22
|
+
path = Xpath.new(['input', 'button'], :type => 'text', :id => 'foo').to_s
|
23
|
+
assert_equal %(.//input[@type="text"][@id="foo"] | .//button[@type="text"][@id="foo"]), path
|
24
|
+
end
|
25
|
+
|
26
|
+
test "xpath with alternate nodenames and attributes" do
|
27
|
+
path = Xpath.new(['input', 'button']).to_s
|
28
|
+
assert_equal ".//input | .//button", path
|
29
|
+
end
|
30
|
+
|
31
|
+
test "xpath or xpath" do
|
32
|
+
path = Xpath.new('input', :type => 'text').or!(Xpath.new('textarea')).to_s
|
33
|
+
assert_equal %(.//input[@type="text"] | .//textarea), path
|
34
|
+
end
|
35
|
+
|
36
|
+
test "attribute equals to one of multiple values" do
|
37
|
+
path = Xpath.new('input', :type => [:text, :password]).to_s
|
38
|
+
assert_equal %(.//input[@type="text" or @type="password"]), path
|
39
|
+
end
|
40
|
+
|
41
|
+
test "one of multiple attributes equals to value" do
|
42
|
+
path = Xpath.new('input', [:id, :name] => 'foo').to_s
|
43
|
+
assert_equal %(.//input[@id="foo" or @name="foo"]), path
|
44
|
+
end
|
45
|
+
|
46
|
+
test "one of multiple attributes equals to one of multiple values" do
|
47
|
+
path = Xpath.new('input', [:id, :name] => ['foo', 'bar']).to_s
|
48
|
+
assert_equal %(.//input[@id="foo" or @id="bar" or @name="foo" or @name="bar"]), path
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
require 'locator/element'
|
3
|
+
|
4
|
+
class LocatorTest < Test::Unit::TestCase
|
5
|
+
test "looks up a locator/element class by type" do
|
6
|
+
assert_equal Locator::Element::Field, Locator[:field]
|
7
|
+
end
|
8
|
+
|
9
|
+
test "generates an xpath for type and given args" do
|
10
|
+
path = Locator.xpath(:form, 'foo', :class => 'bar')
|
11
|
+
assert_equal './/form[@class="bar"][@id="foo" or @name="foo"]', path
|
12
|
+
end
|
13
|
+
|
14
|
+
test "locates an element from html" do
|
15
|
+
html = '<html><body><h1></h1><form class="bar"></form></body></html>'
|
16
|
+
element = Locator.new(html).locate(:form, :class => 'bar')
|
17
|
+
assert_equal '<form class="bar"></form>', element.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
test "within scopes to an xpath" do
|
21
|
+
html = '<form id="foo"></form><div id="bar"><form></form></div>'
|
22
|
+
element = Locator.new(html).within(:xpath => '//div[@id="bar"]') { locate(:form) }
|
23
|
+
assert_equal '<form></form>', element.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
test "locates scopes to :within option" do
|
27
|
+
html = '<form id="foo"></form><div id="bar"><form></form></div>'
|
28
|
+
element = Locator.new(html).locate(:form, :within => '//div[@id="bar"]')
|
29
|
+
assert_equal '<form></form>', element.to_s
|
30
|
+
end
|
31
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
$:.unshift File.expand_path("../lib", File.dirname(__FILE__))
|
2
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'test/unit'
|
5
|
+
require 'locator'
|
6
|
+
|
7
|
+
module TestMethod
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval do
|
10
|
+
def test(name, &block)
|
11
|
+
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
|
12
|
+
defined = instance_method(test_name) rescue false
|
13
|
+
raise "#{test_name} is already defined in #{self}" if defined
|
14
|
+
if block_given?
|
15
|
+
define_method(test_name, &block)
|
16
|
+
else
|
17
|
+
define_method(test_name) do
|
18
|
+
flunk "No implementation provided for #{name}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Module
|
27
|
+
include TestMethod
|
28
|
+
end
|
29
|
+
|
30
|
+
class Test::Unit::TestCase
|
31
|
+
include TestMethod
|
32
|
+
|
33
|
+
def dom(html)
|
34
|
+
Locator::Dom.page(html)
|
35
|
+
end
|
36
|
+
|
37
|
+
def assert_locate(html, path, elements = nil)
|
38
|
+
expected = Array(elements || html)
|
39
|
+
result = dom(html).elements_by_xpath(path).map(&:to_s)
|
40
|
+
assert_equal expected, result
|
41
|
+
end
|
42
|
+
|
43
|
+
def assert_no_locate(html, path)
|
44
|
+
assert_locate(html, path, [])
|
45
|
+
end
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: locator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sven Fuchs
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-04 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Generic html element locators for integration testing
|
17
|
+
email: svenfuchs@artweb-design.de
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.textile
|
24
|
+
files:
|
25
|
+
- README.textile
|
26
|
+
- Rakefile
|
27
|
+
- lib/core_ext/string/underscore.rb
|
28
|
+
- lib/locator.rb
|
29
|
+
- lib/locator/boolean.rb
|
30
|
+
- lib/locator/dom.rb
|
31
|
+
- lib/locator/dom/nokogiri.rb
|
32
|
+
- lib/locator/dom/nokogiri/element.rb
|
33
|
+
- lib/locator/dom/nokogiri/page.rb
|
34
|
+
- lib/locator/element.rb
|
35
|
+
- lib/locator/element/area.rb
|
36
|
+
- lib/locator/element/button.rb
|
37
|
+
- lib/locator/element/elements_list.rb
|
38
|
+
- lib/locator/element/field.rb
|
39
|
+
- lib/locator/element/form.rb
|
40
|
+
- lib/locator/element/label.rb
|
41
|
+
- lib/locator/element/labeled_element.rb
|
42
|
+
- lib/locator/element/link.rb
|
43
|
+
- lib/locator/element/select.rb
|
44
|
+
- lib/locator/element/select_option.rb
|
45
|
+
- lib/locator/element/text_area.rb
|
46
|
+
- lib/locator/version.rb
|
47
|
+
- lib/locator/xpath.rb
|
48
|
+
- test/all.rb
|
49
|
+
- test/locator/boolean_test.rb
|
50
|
+
- test/locator/element/button_test.rb
|
51
|
+
- test/locator/element/field_test.rb
|
52
|
+
- test/locator/element/form_test.rb
|
53
|
+
- test/locator/element/label_test.rb
|
54
|
+
- test/locator/element/link_test.rb
|
55
|
+
- test/locator/element/select_option_test.rb
|
56
|
+
- test/locator/element/select_test.rb
|
57
|
+
- test/locator/element/text_area_test.rb
|
58
|
+
- test/locator/element_test.rb
|
59
|
+
- test/locator/xpath_test.rb
|
60
|
+
- test/locator_test.rb
|
61
|
+
- test/test_helper.rb
|
62
|
+
has_rdoc: true
|
63
|
+
homepage: http://github.com/svenfuchs/locator
|
64
|
+
licenses: []
|
65
|
+
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options:
|
68
|
+
- --charset=UTF-8
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: "0"
|
82
|
+
version:
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.3.5
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Generic html element locators for integration testing
|
90
|
+
test_files:
|
91
|
+
- test/all.rb
|
92
|
+
- test/locator/boolean_test.rb
|
93
|
+
- test/locator/element/button_test.rb
|
94
|
+
- test/locator/element/field_test.rb
|
95
|
+
- test/locator/element/form_test.rb
|
96
|
+
- test/locator/element/label_test.rb
|
97
|
+
- test/locator/element/link_test.rb
|
98
|
+
- test/locator/element/select_option_test.rb
|
99
|
+
- test/locator/element/select_test.rb
|
100
|
+
- test/locator/element/text_area_test.rb
|
101
|
+
- test/locator/element_test.rb
|
102
|
+
- test/locator/xpath_test.rb
|
103
|
+
- test/locator_test.rb
|
104
|
+
- test/test_helper.rb
|