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.
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