locator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/README.textile +5 -0
  2. data/Rakefile +19 -0
  3. data/lib/core_ext/string/underscore.rb +5 -0
  4. data/lib/locator/boolean.rb +50 -0
  5. data/lib/locator/dom/nokogiri/element.rb +49 -0
  6. data/lib/locator/dom/nokogiri/page.rb +25 -0
  7. data/lib/locator/dom/nokogiri.rb +10 -0
  8. data/lib/locator/dom.rb +21 -0
  9. data/lib/locator/element/area.rb +9 -0
  10. data/lib/locator/element/button.rb +11 -0
  11. data/lib/locator/element/elements_list.rb +15 -0
  12. data/lib/locator/element/field.rb +10 -0
  13. data/lib/locator/element/form.rb +9 -0
  14. data/lib/locator/element/label.rb +9 -0
  15. data/lib/locator/element/labeled_element.rb +17 -0
  16. data/lib/locator/element/link.rb +9 -0
  17. data/lib/locator/element/select.rb +9 -0
  18. data/lib/locator/element/select_option.rb +9 -0
  19. data/lib/locator/element/text_area.rb +9 -0
  20. data/lib/locator/element.rb +45 -0
  21. data/lib/locator/version.rb +3 -0
  22. data/lib/locator/xpath.rb +38 -0
  23. data/lib/locator.rb +52 -0
  24. data/test/all.rb +3 -0
  25. data/test/locator/boolean_test.rb +40 -0
  26. data/test/locator/element/button_test.rb +26 -0
  27. data/test/locator/element/field_test.rb +60 -0
  28. data/test/locator/element/form_test.rb +30 -0
  29. data/test/locator/element/label_test.rb +26 -0
  30. data/test/locator/element/link_test.rb +34 -0
  31. data/test/locator/element/select_option_test.rb +34 -0
  32. data/test/locator/element/select_test.rb +26 -0
  33. data/test/locator/element/text_area_test.rb +26 -0
  34. data/test/locator/element_test.rb +40 -0
  35. data/test/locator/xpath_test.rb +50 -0
  36. data/test/locator_test.rb +31 -0
  37. data/test/test_helper.rb +46 -0
  38. 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,5 @@
1
+ class String
2
+ def underscore
3
+ self[0, 1].downcase + self[1..-1].gsub(/[A-Z]/) { |c| "_#{c.downcase}" }
4
+ end
5
+ end unless ''.respond_to?(:underscore)
@@ -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
@@ -0,0 +1,10 @@
1
+ require 'nokogiri'
2
+
3
+ class Locator
4
+ module Dom
5
+ module Nokogiri
6
+ autoload :Element, 'locator/dom/nokogiri/element'
7
+ autoload :Page, 'locator/dom/nokogiri/page'
8
+ end
9
+ end
10
+ end
@@ -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,9 @@
1
+ class Locator
2
+ class Element
3
+ class Area < Element
4
+ def initialize
5
+ super('area', [:id, :alt])
6
+ end
7
+ end
8
+ end
9
+ 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,15 @@
1
+ class Locator
2
+ class Element
3
+ class ElementsList
4
+ attr_reader :elements
5
+
6
+ def initialize(*elements)
7
+ @elements = elements
8
+ end
9
+
10
+ def xpath(*args)
11
+ elements.map { |element| element.xpath(*args) }.join(' | ')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ class Locator
2
+ class Element
3
+ class Field < ElementsList
4
+ def initialize
5
+ types = [:text, :password , :email, :url, :search, :tel, :color]
6
+ super(LabeledElement.new('input', [:id, :name], :type => types), TextArea.new)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ class Locator
2
+ class Element
3
+ class Form < Element
4
+ def initialize
5
+ super('form', [:id, :name])
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Locator
2
+ class Element
3
+ class Label < Element
4
+ def initialize
5
+ super('label', [:id, :content])
6
+ end
7
+ end
8
+ end
9
+ 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,9 @@
1
+ class Locator
2
+ class Element
3
+ class Link < Element
4
+ def initialize
5
+ super('a', [:id, :content], :href => true)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Locator
2
+ class Element
3
+ class Select < LabeledElement
4
+ def initialize
5
+ super('select', [:id, :name])
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Locator
2
+ class Element
3
+ class SelectOption < Element
4
+ def initialize
5
+ super('option', [:id, :value, :content])
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Locator
2
+ class Element
3
+ class TextArea < LabeledElement
4
+ def initialize
5
+ super('textarea', [:id, :name])
6
+ end
7
+ end
8
+ end
9
+ 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,3 @@
1
+ class Locator
2
+ VERSION = "0.0.1"
3
+ 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,3 @@
1
+ Dir[File.dirname(__FILE__) + '/**/*_test.rb'].each do |filename|
2
+ require filename
3
+ end
@@ -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
@@ -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