jarib-celerity 0.0.5

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 (69) hide show
  1. data/History.txt +42 -0
  2. data/License.txt +621 -0
  3. data/README.txt +64 -0
  4. data/Rakefile +12 -0
  5. data/lib/celerity.rb +59 -0
  6. data/lib/celerity/browser.rb +410 -0
  7. data/lib/celerity/clickable_element.rb +26 -0
  8. data/lib/celerity/collections.rb +148 -0
  9. data/lib/celerity/container.rb +488 -0
  10. data/lib/celerity/default_viewer.rb +10 -0
  11. data/lib/celerity/disabled_element.rb +27 -0
  12. data/lib/celerity/element.rb +241 -0
  13. data/lib/celerity/element_collections.rb +68 -0
  14. data/lib/celerity/element_locator.rb +167 -0
  15. data/lib/celerity/elements/button.rb +34 -0
  16. data/lib/celerity/elements/file_field.rb +17 -0
  17. data/lib/celerity/elements/form.rb +16 -0
  18. data/lib/celerity/elements/frame.rb +53 -0
  19. data/lib/celerity/elements/image.rb +57 -0
  20. data/lib/celerity/elements/label.rb +9 -0
  21. data/lib/celerity/elements/link.rb +12 -0
  22. data/lib/celerity/elements/meta.rb +6 -0
  23. data/lib/celerity/elements/non_control_elements.rb +93 -0
  24. data/lib/celerity/elements/option.rb +18 -0
  25. data/lib/celerity/elements/radio_check.rb +85 -0
  26. data/lib/celerity/elements/select_list.rb +81 -0
  27. data/lib/celerity/elements/table.rb +117 -0
  28. data/lib/celerity/elements/table_cell.rb +28 -0
  29. data/lib/celerity/elements/table_elements.rb +41 -0
  30. data/lib/celerity/elements/table_row.rb +36 -0
  31. data/lib/celerity/elements/text_field.rb +127 -0
  32. data/lib/celerity/exception.rb +40 -0
  33. data/lib/celerity/extra/method_generator.rb +158 -0
  34. data/lib/celerity/htmlunit.rb +41 -0
  35. data/lib/celerity/htmlunit/commons-codec-1.3.jar +0 -0
  36. data/lib/celerity/htmlunit/commons-collections-3.2.1.jar +0 -0
  37. data/lib/celerity/htmlunit/commons-httpclient-3.1.jar +0 -0
  38. data/lib/celerity/htmlunit/commons-io-1.4.jar +0 -0
  39. data/lib/celerity/htmlunit/commons-lang-2.4.jar +0 -0
  40. data/lib/celerity/htmlunit/commons-logging-1.1.1.jar +0 -0
  41. data/lib/celerity/htmlunit/cssparser-0.9.5.jar +0 -0
  42. data/lib/celerity/htmlunit/htmlunit-2.4-SNAPSHOT.jar +0 -0
  43. data/lib/celerity/htmlunit/htmlunit-core-js-2.4-SNAPSHOT.jar +0 -0
  44. data/lib/celerity/htmlunit/nekohtml-1.9.10-20081209.100757-4.jar +0 -0
  45. data/lib/celerity/htmlunit/sac-1.3.jar +0 -0
  46. data/lib/celerity/htmlunit/serializer-2.7.1.jar +0 -0
  47. data/lib/celerity/htmlunit/xalan-2.7.1.jar +0 -0
  48. data/lib/celerity/htmlunit/xercesImpl-2.8.1.jar +0 -0
  49. data/lib/celerity/htmlunit/xml-apis-1.3.04.jar +0 -0
  50. data/lib/celerity/identifier.rb +11 -0
  51. data/lib/celerity/input_element.rb +25 -0
  52. data/lib/celerity/listener.rb +106 -0
  53. data/lib/celerity/resources/no_viewer.png +0 -0
  54. data/lib/celerity/util.rb +79 -0
  55. data/lib/celerity/version.rb +9 -0
  56. data/lib/celerity/watir_compatibility.rb +85 -0
  57. data/tasks/benchmark.rake +4 -0
  58. data/tasks/deployment.rake +43 -0
  59. data/tasks/environment.rake +7 -0
  60. data/tasks/fix.rake +25 -0
  61. data/tasks/jar.rake +57 -0
  62. data/tasks/rdoc.rake +4 -0
  63. data/tasks/rspec.rake +30 -0
  64. data/tasks/simple_ci.rake +94 -0
  65. data/tasks/snapshot.rake +26 -0
  66. data/tasks/specserver.rake +21 -0
  67. data/tasks/website.rake +17 -0
  68. data/tasks/yard.rake +5 -0
  69. metadata +129 -0
@@ -0,0 +1,117 @@
1
+ module Celerity
2
+
3
+ class Table < Element
4
+ include Enumerable
5
+ include Container
6
+
7
+ TAGS = [ Identifier.new('table') ]
8
+ ATTRIBUTES = BASE_ATTRIBUTES | [:summary, :width, :border, :frame, :rules,
9
+ :cellspacing, :cellpadding, :align, :bgcolor]
10
+ DEFAULT_HOW = :id
11
+
12
+ def locate
13
+ super
14
+ if @object # cant call assert_exists here, as an exists? method call will fail
15
+ @rows = @object.getRows
16
+ @cells = []
17
+ @rows.each do |row|
18
+ row.getCells.each do |c|
19
+ @cells << c
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ # @return [Celerity::TableRows]
26
+ def rows
27
+ assert_exists
28
+ TableRows.new(self, :object, @rows)
29
+ end
30
+
31
+ # @return [Celerity::TableCells]
32
+ def cells
33
+ assert_exists
34
+ TableCells.new(self, :object, @cells)
35
+ end
36
+
37
+ # Iterates through each row in the table.
38
+ # @yieldparam [Celerity::TableRow] row A row.
39
+ def each
40
+ assert_exists
41
+ @rows.each { |row| yield TableRow.new(self, :object, row) }
42
+ end
43
+
44
+ # Returns the TableRow at the given index (1-indexed).
45
+ #
46
+ # browser.table(:foo, 'bar')[1] # => #<TableRow...>
47
+ # browser.table(:foo, 'bar').child_row[1] # => #<TableRow...>
48
+ #
49
+ # @param [Fixnum] index The index of the wanted row, 1-indexed.
50
+ # @raise [Celerity::Exception::UnknownRowException]
51
+ # @return [Celerity::TableRow]
52
+ def child_row(index)
53
+ assert_exists
54
+
55
+ if (index - INDEX_OFFSET) >= @rows.length
56
+ raise UnknownRowException, "Unable to locate a row at index #{index}"
57
+ end
58
+
59
+ TableRow.new(self, :object, @rows[index - INDEX_OFFSET])
60
+ end
61
+ alias_method :[], :child_row
62
+
63
+ # Returns the TableCell at the given index (1-indexed).
64
+ #
65
+ # In a 10-column row, table.child_cell[11] will return the first cell on the second row.
66
+ #
67
+ # @param [Fixnum] index The index of the wanted cell, 1-indexed.
68
+ # @raise [Celerity::Exception::UnknownCellException]
69
+ # @return [Celerity::TableCell]
70
+ def child_cell(index)
71
+ assert_exists
72
+
73
+ if (index - INDEX_OFFSET) >= @cells.length
74
+ raise UnknownCellException, "Unable to locate a cell at index #{index}"
75
+ end
76
+ TableCell.new(self, :object, @cells[index - INDEX_OFFSET])
77
+ end
78
+
79
+ # The number of rows in the table
80
+ # @return [Fixnum]
81
+ def row_count
82
+ assert_exists
83
+ @object.getRowCount
84
+ end
85
+
86
+ # Returns the number of columns on the row at the given index. (1-indexed)
87
+ # Default is the number of columns on the first row
88
+ # @param [Fixnum] index An index, 1-indexed (optional).
89
+ # @return [Fixnum]
90
+ def column_count(index = INDEX_OFFSET)
91
+ assert_exists
92
+ @object.getRow(index - INDEX_OFFSET).getCells.length
93
+ end
94
+
95
+ # Returns the text of each cell in the the table as a two-dimensional array.
96
+ # @return [Array<Array<String>>]
97
+ def to_a
98
+ assert_exists
99
+ # @object.getRows.map do |table_row|
100
+ # table_row.getCells.map { |td| td.asText.strip }
101
+ # end
102
+ rows.map do |table_row|
103
+ table_row.map { |td| td.text }
104
+ end
105
+ end
106
+
107
+ def column_values(column_number)
108
+ (1..row_count).map { |index| self[index][column_number].text }
109
+ end
110
+
111
+ def row_values(row_number)
112
+ (1..column_count(row_number)).map { |index| self[row_number][index].text }
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,28 @@
1
+ module Celerity
2
+
3
+ class TableCell < Element
4
+ include Celerity::Exception
5
+ include Container
6
+
7
+ TAGS = [ Identifier.new('td') ]
8
+ ATTRIBUTES = BASE_ATTRIBUTES | CELLHALIGN_ATTRIBUTES |
9
+ CELLVALIGN_ATTRIBUTES | [:abbr, :axis, :headers, :scope, :rowspan, :colspan]
10
+
11
+ DEFAULT_HOW = :id
12
+
13
+ alias_method :to_s, :text # why?
14
+
15
+ def colspan
16
+ assert_exists
17
+ attribute_value = @object.getAttributeValue('colspan').to_i
18
+ attribute_value > 0 ? attribute_value : 1
19
+ end
20
+
21
+ end
22
+
23
+ # needs code review regarding attributes/correctness of this
24
+ class Th < TableCell
25
+ TAGS = [ Identifier.new('th')]
26
+ end
27
+
28
+ end
@@ -0,0 +1,41 @@
1
+ module Celerity
2
+ class TableElement < Element
3
+ include Enumerable
4
+
5
+ ATTRIBUTES = BASE_ATTRIBUTES | CELLHALIGN_ATTRIBUTES | CELLVALIGN_ATTRIBUTES
6
+ DEFAULT_HOW = :id
7
+
8
+ def locate
9
+ super
10
+ @rows = @object.getRows if @object
11
+ end
12
+
13
+ def [](index)
14
+ assert_exists
15
+ TableRow.new(self, :object, @rows[index - INDEX_OFFSET])
16
+ end
17
+
18
+ def length
19
+ assert_exists
20
+ @object.getRows.length
21
+ end
22
+
23
+ def each
24
+ assert_exists
25
+ @rows.each { |row| yield TableRow.new(self, :object, row) }
26
+ end
27
+ end
28
+
29
+ class TableBody < TableElement
30
+ TAGS = [ Identifier.new('tbody') ]
31
+ end
32
+
33
+ class TableFooter < TableElement
34
+ TAGS = [ Identifier.new('tfoot') ]
35
+ end
36
+
37
+ class TableHeader < TableElement
38
+ TAGS = [ Identifier.new('thead') ]
39
+ end
40
+
41
+ end
@@ -0,0 +1,36 @@
1
+ module Celerity
2
+ class TableRow < Element
3
+ include Enumerable
4
+
5
+ TAGS = [ Identifier.new('tr') ]
6
+ DEFAULT_HOW = :id
7
+
8
+ def locate
9
+ super
10
+ @cells = @object.getCells if @object
11
+ end
12
+
13
+ # Yields each TableCell in this row cell to the given block.
14
+ def each
15
+ assert_exists
16
+ @cells.each { |cell| yield TableCell.new(self, :object, cell) }
17
+ end
18
+
19
+ def child_cell(index)
20
+ assert_exists
21
+
22
+ if (index - INDEX_OFFSET) >= @cells.length
23
+ raise UnknownCellException, "Unable to locate a cell at index #{index}"
24
+ end
25
+
26
+ TableCell.new(self, :object, @cells[index - INDEX_OFFSET])
27
+ end
28
+ alias_method :[], :child_cell
29
+
30
+ def column_count
31
+ assert_exists
32
+ @cells.length
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,127 @@
1
+ module Celerity
2
+ #
3
+ # Class representing text field elements
4
+ #
5
+ # This class is the main class for Text Fields
6
+ # Normally a user would not need to create this object as it is returned by the Watir::Container#text_field method
7
+ class TextField < InputElement
8
+ NON_TEXT_TYPES = %w[file radio checkbox submit reset image button hidden]
9
+ TAGS = [ Identifier.new('textarea'),
10
+ Identifier.new('input', :type => ["text", "password", /^(?!(#{ Regexp.union(*NON_TEXT_TYPES) })$)/]) ]
11
+ DEFAULT_HOW = :name
12
+
13
+ # Clear the text field.
14
+ def clear
15
+ assert_exists
16
+ insert_string ''
17
+ end
18
+
19
+ # Set the text field to the given value.
20
+ # This ensures execution of JavaScript events (onkeypress etc.), but is slower than +value=+
21
+ def set(value)
22
+ assert_enabled
23
+ assert_not_readonly
24
+ clear
25
+ type_string(value.to_s)
26
+ end
27
+
28
+ # This directly sets the text field to the given value, skipping exectuion of JavaScript events.
29
+ # Use +set+ if you want to run events on text fields.
30
+ def value=(value)
31
+ assert_enabled
32
+ assert_not_readonly
33
+ clear
34
+
35
+ insert_string value.to_s
36
+
37
+ value
38
+ end
39
+
40
+ # Returns the text in the text field.
41
+ def value
42
+ assert_exists
43
+ case @object.getTagName
44
+ when 'textarea'
45
+ @object.getText
46
+ when 'input'
47
+ @object.getValueAttribute
48
+ end
49
+ end
50
+ alias_method :get_contents, :value
51
+
52
+ # Append the given value to the text in the text field.
53
+ def append(value)
54
+ assert_enabled
55
+ assert_not_readonly
56
+ type_string(value)
57
+ end
58
+
59
+ def type
60
+ assert_exists
61
+ type = @object.getAttributeValue('type')
62
+
63
+ if NON_TEXT_TYPES.include?(type)
64
+ type
65
+ else
66
+ 'text'
67
+ end
68
+ end
69
+
70
+ # This bascially just moves the text to the other text field using TextField#append
71
+ # Should check if the HtmlUnit API supports some kind of dragging.
72
+ def drag_contents_to(how, what)
73
+ assert_exists # assert_enabled?
74
+ val = self.value
75
+ self.value = ''
76
+ @container.text_field(how, what).append(val)
77
+ end
78
+
79
+ def contains_text(expected_text)
80
+ assert_exists
81
+
82
+ case expected_text
83
+ when Regexp
84
+ value() =~ expected_text
85
+ when String
86
+ value().index(expected_text)
87
+ else
88
+ raise TypeError, "expected String or Regexp, got #{expected_text.inspect}:#{expected_text.class}"
89
+ end
90
+ end
91
+
92
+ # A boolean version of TextField#contains_text
93
+ # @param [String, Regexp] expected_text The text to look for.
94
+ # @return [boolean]
95
+ def verify_contains(expected)
96
+ # assert_exists called by contains_text
97
+ !!contains_text(expected)
98
+ end
99
+
100
+ private
101
+
102
+ def type_string(value)
103
+ java.lang.String.new(value.to_java_bytes, @container.page.getPageEncoding).toCharArray.each do |char|
104
+ @container.update_page @object.type(char)
105
+ end
106
+ end
107
+
108
+ def insert_string(value)
109
+ case @object.getTagName
110
+ when 'textarea'
111
+ @object.setText(value)
112
+ when 'input'
113
+ @object.setValueAttribute(value)
114
+ else
115
+ raise "unknown tag name #{@object.getTagName.inspect} for #{self.class}"
116
+ end
117
+ end
118
+ end
119
+
120
+ # this class can be used to access hidden field objects
121
+ # Normally a user would not need to create this object as it is returned by the Celerity::Container#hidden method
122
+ class Hidden < TextField
123
+ TAGS = [ Identifier.new('input', :type => %w[hidden]) ]
124
+ DEFAULT_HOW = :name
125
+ end
126
+
127
+ end
@@ -0,0 +1,40 @@
1
+ module Celerity
2
+ module Exception
3
+
4
+ # Superclass for all Celerity exceptions.
5
+ class CelerityException < StandardError; end
6
+
7
+ # This exception is thrown if an attempt is made to access an object that doesn't exist
8
+ class UnknownObjectException < CelerityException; end
9
+
10
+ # This exception is thrown if an attempt is made to access an object that is in a disabled state
11
+ class ObjectDisabledException < CelerityException; end
12
+
13
+ # This exception is thrown if an attempt is made to access a frame that cannot be found
14
+ class UnknownFrameException < CelerityException; end
15
+
16
+ # This exception is thrown if an attempt is made to access a form that cannot be found
17
+ class UnknownFormException < CelerityException; end
18
+
19
+ # This exception is thrown if an attempt is made to access an object that is in a read-only state
20
+ class ObjectReadOnlyException < CelerityException; end
21
+
22
+ # This exception is thrown if an attempt is made to access an object when the specified value cannot be found
23
+ class NoValueFoundException < CelerityException; end
24
+
25
+ # This exception gets raised if the how argument is wrong.
26
+ class MissingWayOfFindingObjectException < CelerityException; end
27
+
28
+ # This exception is raised if an attempt is made to access a table row that doesn't exist
29
+ class UnknownRowException < CelerityException; end
30
+
31
+ # This exception is raised if an attempt is made to access a table cell that doesn't exist
32
+ class UnknownCellException < CelerityException; end
33
+
34
+ # This exception is thrown if an http error, such as a 404, 500 etc is encountered while navigating
35
+ class NavigationException < CelerityException; end
36
+
37
+ # This exception is thrown if an unexpected content type is returned by the server.
38
+ class UnexpectedPageException < CelerityException; end
39
+ end
40
+ end
@@ -0,0 +1,158 @@
1
+ require "rubygems"
2
+ require "uri"
3
+ require "active_support"
4
+
5
+ #--
6
+ # http://api.rubyonrails.com/classes/Inflector.html#M001621
7
+ #++
8
+ class String # :nodoc:
9
+ def underscore
10
+ gsub(/::/, '/').
11
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
12
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
13
+ tr("-", "_").
14
+ downcase
15
+ end
16
+ end
17
+
18
+ module Celerity
19
+ # Experimental - generate a method definition for accessing elements on the current page.
20
+ class MethodGenerator
21
+
22
+ ELEMENTS = %w[text_field select_list radio checkbox button].map { |e| e.to_sym }
23
+ BUGGY_ELEMENTS = %w[radio checkbox].map { |e| e.to_sym }
24
+
25
+ def initialize(ie, opts = {})
26
+ @ie = ie
27
+ @opts = opts
28
+ @browser = @opts[:browser] || '@ie'
29
+
30
+ @docs = " # Fills in the page at #{@ie.url}\n #\n"
31
+ @docs << " # Parameters:\n #\n"
32
+ @doc_elements = []
33
+
34
+ @method = " def #{@opts[:method_name] || 'generated_method'}(opts = {})\n\n"
35
+ end
36
+
37
+ def parse
38
+ ELEMENTS.each do |elem|
39
+ @method << " # buggy!\n" if BUGGY_ELEMENTS.include?(elem)
40
+ add_elements(elem)
41
+ end
42
+ add_elements(:link) if @opts[:include_links]
43
+ @method << " end\n\n"
44
+
45
+ # fix docs
46
+ max = @doc_elements.map { |symbol, _| symbol.to_s.size }.max
47
+ @doc_elements.each do |sym, desc|
48
+ @docs << " # #{sym.to_s.ljust(max)} => #{desc}\n"
49
+ end
50
+ @docs << " #\n"*2
51
+ @docs + @method
52
+ end
53
+
54
+ private
55
+
56
+ def add_elements(symbol)
57
+ symbol = symbol.to_sym
58
+ symbol_pluralized = symbol.to_s.pluralize.to_sym
59
+ @ie.send(symbol_pluralized).each_with_index do |elem, idx|
60
+ self.send("add_#{symbol}".to_sym, elem, idx)
61
+ end
62
+
63
+ @method << "\n"
64
+ end
65
+
66
+ def add_text_field(elem, idx)
67
+ how, what = find_identifier(elem) || [:index, (idx + 1).to_s]
68
+ @method << " #{@browser}.text_field(#{how.inspect}, #{what.inspect}).value = "
69
+ symbol = (how == :index) ? ":text_field_#{what.underscore}" : ":#{what.underscore}"
70
+ @method << "opts[#{symbol}]\n"
71
+ @doc_elements << [symbol, "value for text field #{what.inspect}"]
72
+ end
73
+
74
+ def add_select_list(elem, idx)
75
+ how, what = find_identifier(elem) || [:index, (idx + 1).to_s]
76
+ @method << " #{@browser}.select_list(#{how.inspect}, #{what.inspect}).select("
77
+ symbol = (how == :index) ? ":select_list_#{what.underscore}" : ":#{what.underscore}"
78
+ @method << "opts[#{symbol}])\n"
79
+ @doc_elements << [symbol, "option to select for select list #{what.inspect}"]
80
+ end
81
+
82
+ def add_radio(elem, idx)
83
+ how, what = find_identifier(elem) || [:index, (idx + 1).to_s]
84
+ @method << " #{@browser}.radio(#{how.inspect}, #{what.inspect}, "
85
+
86
+ if (value = elem.value).empty?
87
+ symbol = (how == :index) ? ":radio_#{what.underscore}" : ":#{what.underscore}"
88
+ else
89
+ symbol = ":#{what.underscore}_#{value.underscore}"
90
+ @method << "#{value.inspect}).set if opts[#{symbol}]\n"
91
+ end
92
+
93
+ @doc_elements << [symbol, "set the radio with id/value #{what.inspect}"]
94
+ end
95
+
96
+ def add_checkbox(elem, idx)
97
+ how, what = find_identifier(elem) || [:index, (idx + 1).to_s]
98
+ @method << " #{@browser}.checkbox(#{how.inspect}, #{what.inspect}, "
99
+ symbol = (how == :index) ? ":checkbox_#{what.underscore}" : ":#{what.underscore}"
100
+ @method << "#{elem.value.inspect}).set if opts[#{symbol}]\n"
101
+
102
+ @doc_elements << [symbol, "set the checkbox with id/value #{what.inspect}"]
103
+ end
104
+
105
+ def add_button(elem, idx)
106
+ how, what = find_identifier(elem) || [:index, (idx + 1).to_s]
107
+ @method << " #{@browser}.button(#{how.inspect}, #{what.inspect}).click\n"
108
+ end
109
+
110
+ def add_link(elem, idx)
111
+ if (href = elem.href) =~ /javascript/
112
+ how, what = :index, (idx + 1).to_s
113
+ else
114
+ how = :url
115
+
116
+ begin
117
+ uri = URI.parse(href)
118
+ what = Regexp.new(Regexp.escape(uri.to_s.sub(/.*#{uri.host}\//, '')))
119
+ rescue URI::InvalidURIError
120
+ what = href
121
+ end
122
+ end
123
+ @method << " #{@browser}.link(#{how.inspect}, #{what.inspect}).click\n"
124
+ end
125
+
126
+ def find_identifier(element)
127
+ # could use ATTRIBUTES if they were 'weighted' somehow?
128
+ attrs = element.class::ATTRIBUTES
129
+ [:id, :name].each do |attribute|
130
+ return [attribute, element.send(attribute)] if attrs.include?(attribute) && !element.send(attribute).empty?
131
+ end
132
+ nil
133
+ end
134
+
135
+ end # MethodGenerator
136
+
137
+ class Browser
138
+ # Experimental - generate a method definition for accessing elements on the current page
139
+ # Not loaded by default - need to require 'celerity/extra/method_generator'
140
+ def generate_method(opts = {})
141
+ MethodGenerator.new(self, opts).parse
142
+ end
143
+ end # Browser
144
+
145
+ end # Celerity
146
+
147
+
148
+
149
+ # if __FILE__ == $0
150
+ # require File.dirname(__FILE__) + "/../spec/spec_helper"
151
+ # $stdout.sync = true
152
+ # @ie = Browser.new
153
+ # @ie.goto(TEST_HOST + "/forms_with_input_elements.html")
154
+ #
155
+ # puts MethodGenerator.new(@ie).parse
156
+ # @ie.goto(TEST_HOST + "/forms3.html")
157
+ # puts MethodGenerator.new(@ie).parse
158
+ # end