locator 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|