lebowski 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/History.md +3 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +84 -0
  4. data/README.md +146 -0
  5. data/Rakefile +42 -0
  6. data/bin/lebowski +26 -0
  7. data/bin/lebowski-spec +29 -0
  8. data/bin/lebowski-start-server +24 -0
  9. data/lib/lebowski.rb +15 -0
  10. data/lib/lebowski/core.rb +35 -0
  11. data/lib/lebowski/foundation.rb +52 -0
  12. data/lib/lebowski/foundation/application.rb +315 -0
  13. data/lib/lebowski/foundation/core.rb +61 -0
  14. data/lib/lebowski/foundation/core_query.rb +231 -0
  15. data/lib/lebowski/foundation/dom_element.rb +114 -0
  16. data/lib/lebowski/foundation/errors/argument_invalid_type.rb +31 -0
  17. data/lib/lebowski/foundation/errors/unexpected_type.rb +30 -0
  18. data/lib/lebowski/foundation/mixins/collection_item_view_support.rb +87 -0
  19. data/lib/lebowski/foundation/mixins/delegate_support.rb +22 -0
  20. data/lib/lebowski/foundation/mixins/inline_text_field_support.rb +29 -0
  21. data/lib/lebowski/foundation/mixins/key_check.rb +35 -0
  22. data/lib/lebowski/foundation/mixins/list_item_view_support.rb +36 -0
  23. data/lib/lebowski/foundation/mixins/positioned_element.rb +20 -0
  24. data/lib/lebowski/foundation/mixins/stall_support.rb +79 -0
  25. data/lib/lebowski/foundation/mixins/user_actions.rb +302 -0
  26. data/lib/lebowski/foundation/mixins/wait_actions.rb +44 -0
  27. data/lib/lebowski/foundation/object_array.rb +305 -0
  28. data/lib/lebowski/foundation/panes/alert.rb +117 -0
  29. data/lib/lebowski/foundation/panes/main.rb +20 -0
  30. data/lib/lebowski/foundation/panes/menu.rb +100 -0
  31. data/lib/lebowski/foundation/panes/modal.rb +21 -0
  32. data/lib/lebowski/foundation/panes/palette.rb +21 -0
  33. data/lib/lebowski/foundation/panes/pane.rb +24 -0
  34. data/lib/lebowski/foundation/panes/panel.rb +25 -0
  35. data/lib/lebowski/foundation/panes/picker.rb +43 -0
  36. data/lib/lebowski/foundation/panes/sheet.rb +21 -0
  37. data/lib/lebowski/foundation/proxy_factory.rb +87 -0
  38. data/lib/lebowski/foundation/proxy_object.rb +670 -0
  39. data/lib/lebowski/foundation/sc_object.rb +38 -0
  40. data/lib/lebowski/foundation/views/button.rb +20 -0
  41. data/lib/lebowski/foundation/views/checkbox.rb +63 -0
  42. data/lib/lebowski/foundation/views/collection.rb +304 -0
  43. data/lib/lebowski/foundation/views/container.rb +32 -0
  44. data/lib/lebowski/foundation/views/disclosure.rb +59 -0
  45. data/lib/lebowski/foundation/views/grid.rb +21 -0
  46. data/lib/lebowski/foundation/views/label.rb +30 -0
  47. data/lib/lebowski/foundation/views/list.rb +67 -0
  48. data/lib/lebowski/foundation/views/list_item.rb +280 -0
  49. data/lib/lebowski/foundation/views/menu_item.rb +27 -0
  50. data/lib/lebowski/foundation/views/radio.rb +32 -0
  51. data/lib/lebowski/foundation/views/segmented.rb +97 -0
  52. data/lib/lebowski/foundation/views/select_field.rb +139 -0
  53. data/lib/lebowski/foundation/views/support/simple_item_array.rb +249 -0
  54. data/lib/lebowski/foundation/views/text_field.rb +108 -0
  55. data/lib/lebowski/foundation/views/view.rb +108 -0
  56. data/lib/lebowski/runtime.rb +7 -0
  57. data/lib/lebowski/runtime/errors/remote_control_command_execution_error.rb +9 -0
  58. data/lib/lebowski/runtime/errors/remote_control_command_timeout_error.rb +9 -0
  59. data/lib/lebowski/runtime/errors/remote_control_error.rb +9 -0
  60. data/lib/lebowski/runtime/errors/selenium_server_error.rb +9 -0
  61. data/lib/lebowski/runtime/object_encoder.rb +123 -0
  62. data/lib/lebowski/runtime/sprout_core_driver.rb +14 -0
  63. data/lib/lebowski/runtime/sprout_core_extensions.rb +600 -0
  64. data/lib/lebowski/scui.rb +18 -0
  65. data/lib/lebowski/scui/mixins/node_item_view_support.rb +136 -0
  66. data/lib/lebowski/scui/mixins/terminal_view_support.rb +25 -0
  67. data/lib/lebowski/scui/views/combo_box.rb +119 -0
  68. data/lib/lebowski/scui/views/date_picker.rb +148 -0
  69. data/lib/lebowski/scui/views/linkit.rb +36 -0
  70. data/lib/lebowski/spec.rb +17 -0
  71. data/lib/lebowski/spec/core.rb +21 -0
  72. data/lib/lebowski/spec/matchers/be.rb +63 -0
  73. data/lib/lebowski/spec/matchers/has.rb +40 -0
  74. data/lib/lebowski/spec/matchers/match_supporters/has_object_function.rb +67 -0
  75. data/lib/lebowski/spec/matchers/match_supporters/has_predicate_with_no_prefix.rb +50 -0
  76. data/lib/lebowski/spec/matchers/match_supporters/has_predicate_with_prefix_has.rb +50 -0
  77. data/lib/lebowski/spec/matchers/match_supporters/match_supporter.rb +29 -0
  78. data/lib/lebowski/spec/matchers/method_missing.rb +24 -0
  79. data/lib/lebowski/spec/operators/operator.rb +20 -0
  80. data/lib/lebowski/spec/operators/that.rb +116 -0
  81. data/lib/lebowski/spec/util.rb +26 -0
  82. data/lib/lebowski/version.rb +17 -0
  83. data/resources/selenium-server.jar +0 -0
  84. data/resources/user-extensions.js +1421 -0
  85. metadata +198 -0
@@ -0,0 +1,108 @@
1
+ # ==========================================================================
2
+ # Project: Lebowski Framework - The SproutCore Test Automation Framework
3
+ # License: Licensed under MIT license (see License.txt)
4
+ # ==========================================================================
5
+
6
+ module Lebowski
7
+ module Foundation
8
+ module Views
9
+
10
+ #
11
+ # Represents a proxy to a SproutCore text field view (SC.TextFieldView)
12
+ #
13
+ class TextFieldView < Lebowski::Foundation::Views::View
14
+
15
+ representing_sc_class 'SC.TextFieldView'
16
+
17
+ SELECTOR_INPUT_FIELD = "input"
18
+ SELECTOR_TEXT_AREA = "textarea"
19
+
20
+ #
21
+ # Used to check if this view is empty
22
+ #
23
+ def empty?()
24
+ val = self['value']
25
+ return (val.nil? or val.empty?)
26
+ end
27
+
28
+ def clear()
29
+ type ""
30
+ end
31
+
32
+ def key_down(char)
33
+ cq = core_query(text_field_selector)
34
+ input = cq[0]
35
+ input.key_down char
36
+ cq.done
37
+ end
38
+
39
+ def key_up(char)
40
+ cq = core_query(text_field_selector)
41
+ input = cq[0]
42
+ input.key_up char
43
+ cq.done
44
+ end
45
+
46
+ #
47
+ # Used to type text into this view. This will directly insert the text
48
+ # into the input field. There will be no simulated key up and key down
49
+ # events
50
+ #
51
+ def type(text)
52
+ cq = core_query(text_field_selector)
53
+ input = cq[0]
54
+ input.type text
55
+ cq.done
56
+ end
57
+
58
+ def type_append(text)
59
+ val = self['value']
60
+ type "#{val}#{text}"
61
+ end
62
+
63
+ #
64
+ # Used to simulate the typing of a single key. This will cause a simulated
65
+ # key up and key down event
66
+ #
67
+ def type_key(text)
68
+ clear
69
+ type_key_append text
70
+ end
71
+
72
+ def type_key_append(text)
73
+ cq = core_query(text_field_selector)
74
+ input = cq[0]
75
+ input.type_key text
76
+ cq.done
77
+ end
78
+
79
+ #
80
+ # Used to simulate the typing of some given text. Each character from the
81
+ # given text will "typed" meaning that a simulated key up and key down event
82
+ # will occur for each character. This is useful when you have something
83
+ # that reacts to each character being entered.
84
+ #
85
+ def type_keys(text)
86
+ clear
87
+ type_keys_append text
88
+ end
89
+
90
+ def type_keys_append(text)
91
+ cq = core_query(text_field_selector)
92
+ input = cq[0]
93
+ text.chars.each do |x|
94
+ input.type_key x
95
+ end
96
+ cq.done
97
+ end
98
+
99
+ private
100
+
101
+ def text_field_selector()
102
+ return (self['isTextArea'] == true) ? SELECTOR_TEXT_AREA : SELECTOR_INPUT_FIELD
103
+ end
104
+
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,108 @@
1
+ # ==========================================================================
2
+ # Project: Lebowski Framework - The SproutCore Test Automation Framework
3
+ # License: Licensed under MIT license (see License.txt)
4
+ # ==========================================================================
5
+
6
+ module Lebowski
7
+ module Foundation
8
+
9
+ #
10
+ # Represents a proxy to a generic SproutCore view (SC.View).
11
+ #
12
+ # All other views in this framework derive from this in order to acquire base
13
+ # functionality. If you intended to create your own view proxy object then you
14
+ # must inherit from this class as well.
15
+ #
16
+ module Views
17
+ class View < Lebowski::Foundation::SCObject
18
+ include Lebowski::Foundation
19
+ include Lebowski::Foundation::Views
20
+ include Lebowski::Foundation::Mixins::UserActions
21
+ include Lebowski::Foundation::Mixins::DelegateSupport
22
+
23
+ representing_sc_class 'SC.View'
24
+
25
+ def assigned_layer_id?()
26
+ return (not (rel_path =~ /^#/).nil?)
27
+ end
28
+
29
+ def abs_path()
30
+ return rel_path if assigned_layer_id?
31
+ return super
32
+ end
33
+
34
+ # @override Actions#action_locator_args
35
+ def action_locator_args()
36
+ return [abs_path]
37
+ end
38
+
39
+ # @override Actions#action_target
40
+ def action_target()
41
+ return :view
42
+ end
43
+
44
+ #
45
+ # Gets the remote layer ID for this view
46
+ #
47
+ # @see SC.View.layerId
48
+ #
49
+ def layer_id()
50
+ # We only need to fetch the layer ID once since it never changes for a given instance
51
+ @layer_id = @driver.get_sc_property_string_value(abs_path, "layerId") if @layer_id.nil?
52
+ return @layer_id
53
+ end
54
+
55
+ #
56
+ # Gets the frame for this view.
57
+ #
58
+ # @return {Lebowski::Rect}
59
+ #
60
+ def frame()
61
+ return @driver.get_sc_view_frame(abs_path)
62
+ end
63
+
64
+ # @override Lebowski::Foundation::Mixins::PositionedElement#position
65
+ def position()
66
+ return @driver.get_sc_element_window_position(action_target, *action_locator_args)
67
+ end
68
+
69
+ # @override Lebowski::Foundation::Mixins::PositionedElement#scroll_to_visible
70
+ def scroll_to_visible()
71
+ @driver.sc_view_scroll_to_visible(abs_path)
72
+ end
73
+
74
+ #
75
+ # Gets the string representing of the view's layer. The layer in SproutCore is
76
+ # the root DOM element of the view. This method will return an HTML string
77
+ # representation of the entire layer that is equivalent to the following:
78
+ #
79
+ # view.get('layer').outerHTML
80
+ #
81
+ def layer()
82
+ value = @driver.get_sc_view_layer(abs_path)
83
+ return value
84
+ end
85
+
86
+ #
87
+ # Obtain a core query object from the view.
88
+ #
89
+ # @param selector {String} a CSS selector that the core query object will use
90
+ # @return returns an instance of Lebowski::Foundation::CoreQuery
91
+ #
92
+ def core_query(selector=nil)
93
+ cq = Lebowski::Foundation::CoreQuery.new abs_path, selector, @driver
94
+ return cq
95
+ end
96
+
97
+ def child_views()
98
+ if @child_views.nil?
99
+ @child_views = ObjectArray.new self, 'childViews', 'length'
100
+ end
101
+ return @child_views
102
+ end
103
+
104
+ end
105
+ end
106
+ end
107
+ end
108
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/runtime/errors/remote_control_error')
2
+ require File.expand_path(File.dirname(__FILE__) + '/runtime/errors/remote_control_command_execution_error')
3
+ require File.expand_path(File.dirname(__FILE__) + '/runtime/errors/remote_control_command_timeout_error')
4
+ require File.expand_path(File.dirname(__FILE__) + '/runtime/errors/selenium_server_error')
5
+ require File.expand_path(File.dirname(__FILE__) + '/runtime/object_encoder')
6
+ require File.expand_path(File.dirname(__FILE__) + '/runtime/sprout_core_extensions')
7
+ require File.expand_path(File.dirname(__FILE__) + '/runtime/sprout_core_driver')
@@ -0,0 +1,9 @@
1
+ module Lebowski
2
+ module Runtime
3
+
4
+ class RemoteControlCommandExecutionError < Exception
5
+
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Lebowski
2
+ module Runtime
3
+
4
+ class RemoteControlCommandTimeoutError < Exception
5
+
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Lebowski
2
+ module Runtime
3
+
4
+ class RemoteControlError < Exception
5
+
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Lebowski
2
+ module Runtime
3
+
4
+ class SeleniumServerError < Exception
5
+
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,123 @@
1
+ module Lebowski
2
+ module Runtime
3
+
4
+ #
5
+ # Used to encode Ruby values into an encoded string that can be sent over
6
+ # to the browser to then be decoded and used.
7
+ #
8
+ # TODO: This needs to be completely changed. Instead of using a custom encoding,
9
+ # should instead use a standard encoding scheme, like JSON. Got too
10
+ # experimental in this case.
11
+ #
12
+ module ObjectEncoder
13
+
14
+ COMMA_CHAR = ','
15
+ COLON_CHAR = ':'
16
+ EQUAL_CHAR = '='
17
+ LEFT_SQUARE_BRACKET_CHAR = '['
18
+ RIGHT_SQUARE_BRACKET_CHAR = ']'
19
+
20
+ ENCODED_COMMA = "[cma]"
21
+ ENCODED_COLON = "[cln]"
22
+ ENCODED_EQUAL = "[eql]"
23
+ ENCODED_LEFT_SQUARE_BRACKET = "[lsb]"
24
+ ENCODED_RIGHT_SQUARE_BRACKET = "[rsb]"
25
+
26
+ def self.encode(value)
27
+ return encode_hash(value) if value.kind_of? Hash
28
+ return encode_array(value) if value.kind_of? Array
29
+ return nil
30
+ end
31
+
32
+ #
33
+ # Encodes a given hash into a string representation that can be sent over
34
+ # to the remote browser via Selenium. Examples of encoding:
35
+ #
36
+ # { 'foo' => "bar"} # => foo=bar
37
+ # { :foo => "bar"} # => foo=bar
38
+ # { 'foo' => "cat", bar => "dog"} # => foo=cat,bar=dog
39
+ # { 'foo' => /bar/ } # => foo=regexp:bar
40
+ # { 'foo' => /bar/i } # => foo=regexpi:bar
41
+ # { 'foo.bar' => "cat" } # => foo.bar=cat
42
+ # { 'foo' => "Acme, Inc."} # => foo=Acme[comma] Inc.
43
+ # { 'foo' => 100 } # => foo=int:100
44
+ # { 'foo' => true } # => foo=bool:true
45
+ # { 'foo' => false } # => foo=bool:false
46
+ #
47
+ def self.encode_hash(hash)
48
+ str = ""
49
+ counter = 1
50
+ hash.each do |key, value|
51
+ str << key.to_s << "=" << encode_value(value)
52
+ str << "," if counter < hash.length
53
+ counter = counter.next
54
+ end
55
+ return str
56
+ end
57
+
58
+ #
59
+ # Encodes a given array into a string representation that can be sent over
60
+ # to the remote browser via Selenium
61
+ #
62
+ def self.encode_array(array)
63
+ str = ""
64
+ counter = 1
65
+ array.each do |value|
66
+ str << encode_value(value)
67
+ str << "," if counter < array.length
68
+ counter = counter.next
69
+ end
70
+ return str
71
+ end
72
+
73
+ def self.encode_value(value)
74
+ str = ""
75
+ if value.kind_of? Regexp
76
+ str << "regexp"
77
+ str << "i" if (value.options & Regexp::IGNORECASE) == Regexp::IGNORECASE
78
+ str << ":" << encode_string(value.source)
79
+ elsif value.kind_of? Integer
80
+ str << "int:" << value.to_s
81
+ elsif value.kind_of? TrueClass or value.kind_of? FalseClass
82
+ str << "bool:" << value.to_s
83
+ elsif value.kind_of? Hash
84
+ str << "hash:" << encode_string(encode_hash(value))
85
+ elsif value.kind_of? Array
86
+ str << "array:" << encode_string(encode_array(value))
87
+ elsif value.nil?
88
+ str << "null:null"
89
+ elsif value == :undefined
90
+ str << "undefined:undefined"
91
+ else
92
+ str << encode_string(value.to_s)
93
+ end
94
+ return str
95
+ end
96
+
97
+ #
98
+ # Encodes special characters in a string. Characters encoded are:
99
+ #
100
+ # , : = [ ]
101
+ #
102
+ def self.encode_string(string)
103
+ new_string = string.gsub(/[,=:\[\]]/) do |c|
104
+ case c
105
+ when COMMA_CHAR
106
+ ENCODED_COMMA
107
+ when COLON_CHAR
108
+ ENCODED_COLON
109
+ when EQUAL_CHAR
110
+ ENCODED_EQUAL
111
+ when LEFT_SQUARE_BRACKET_CHAR
112
+ ENCODED_LEFT_SQUARE_BRACKET
113
+ when RIGHT_SQUARE_BRACKET_CHAR
114
+ ENCODED_RIGHT_SQUARE_BRACKET
115
+ end
116
+ end
117
+ return new_string
118
+ end
119
+
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,14 @@
1
+ module Lebowski
2
+ module Runtime
3
+ class SproutCoreDriver
4
+ include Selenium::Client::Base
5
+ include Lebowski::Runtime::SproutCoreExtensions
6
+
7
+ def session_id=(value)
8
+ @session_id = value
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,600 @@
1
+ module Lebowski
2
+ module Runtime
3
+
4
+ module SproutCoreExtensions
5
+ include Lebowski
6
+
7
+ HTTP_HEADERS = { 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8' }
8
+
9
+ REMOTE_CONTROL_COMMAND_TIMEOUT = /^timed out after/i
10
+
11
+ # SC Application Setup Selenium Calls
12
+
13
+ def set_application_name(name)
14
+ __remote_control_command("setScApplicationName", [name,])
15
+ end
16
+
17
+ def initialize_sc_selenium_extension(timeout)
18
+ __remote_control_command("initializeScSeleniumExtension", [timeout,])
19
+ end
20
+
21
+ def open_sc_application(app_root_path, timeout=nil)
22
+ __remote_control_command("openScApplication", [app_root_path, timeout])
23
+ end
24
+
25
+ # SC Object Foundation Selenium Calls
26
+
27
+ def get_sc_guid(scpath)
28
+ return __string_command("getScGuid", [scpath])
29
+ end
30
+
31
+ def get_sc_object_class_name(scpath)
32
+ return __string_command("getScObjectClassName", [scpath])
33
+ end
34
+
35
+ def is_sc_object_kind_of_class(scpath, klass)
36
+ return __boolean_command("isScObjectKindOfClass", [scpath, klass])
37
+ end
38
+
39
+ def get_sc_type_of(scpath)
40
+ return __string_command("getScTypeOf", [scpath])
41
+ end
42
+
43
+ def get_sc_type_of_array_content(scpath)
44
+ return __string_command("getScTypeOfArrayContent", [scpath])
45
+ end
46
+
47
+ def get_sc_object_class_names(scpath)
48
+ return __string_array_command("getScObjectClassNames", [scpath])
49
+ end
50
+
51
+ # SC Object Property Selenium Calls
52
+
53
+ def get_sc_path_string_value(scpath)
54
+ return __string_command("getScPropertyValue", [scpath])
55
+ end
56
+
57
+ def get_sc_path_number_value(scpath)
58
+ return __number_command("getScPropertyValue", [scpath])
59
+ end
60
+
61
+ def get_sc_path_boolean_value(scpath)
62
+ return __boolean_command("getScPropertyValue", [scpath])
63
+ end
64
+
65
+ def get_sc_path_string_array_value(scpath)
66
+ return __string_array_command("getScPropertyValue", [scpath])
67
+ end
68
+
69
+ def get_sc_path_number_array_value(scpath)
70
+ return __number_array_command("getScPropertyValue", [scpath])
71
+ end
72
+
73
+ def get_sc_path_boolean_array_value(scpath)
74
+ return __boolean_array_command("getScPropertyValue", [scpath])
75
+ end
76
+
77
+ def get_sc_property_value(scpath, property)
78
+ return get_sc_property_string_value(scpath, property)
79
+ end
80
+
81
+ def get_sc_property_string_value(scpath, property)
82
+ full_path = "#{scpath}.#{property}"
83
+ return __string_command("getScPropertyValue", [full_path,])
84
+ end
85
+
86
+ def get_sc_property_boolean_value(scpath, property)
87
+ full_path = "#{scpath}.#{property}"
88
+ return __boolean_command("getScPropertyValue", [full_path,])
89
+ end
90
+
91
+ def get_sc_property_number_value(scpath, property)
92
+ full_path = "#{scpath}.#{property}"
93
+ return __number_command("getScPropertyValue", [full_path,])
94
+ end
95
+
96
+ def get_sc_property_array_value(scpath, property)
97
+ return get_sc_property_string_array_value(scpath, property)
98
+ end
99
+
100
+ def get_sc_property_string_array_value(scpath, property)
101
+ full_path = "#{scpath}.#{property}"
102
+ return __string_array_command("getScPropertyValue", [full_path,])
103
+ end
104
+
105
+ def get_sc_property_number_array_value(scpath, property)
106
+ full_path = "#{scpath}.#{property}"
107
+ return __number_array_command("getScPropertyValue", [full_path,])
108
+ end
109
+
110
+ def get_sc_property_boolean_array_value(scpath, property)
111
+ full_path = "#{scpath}.#{property}"
112
+ return __boolean_array_command("getScPropertyValue", [full_path,])
113
+ end
114
+
115
+ # SC View Object Foundation Selenium Calls
116
+
117
+ def get_sc_view_layer(scpath)
118
+ return string_command("getScViewLayer", [scpath, ])
119
+ end
120
+
121
+ # @return {Lebowski::Rect}
122
+ def get_sc_view_frame(scpath)
123
+ value = __number_array_command("getScViewFrame", [scpath])
124
+ return nil if value.nil?
125
+ return Rect.new(value[0], value[1], value[2], value[3])
126
+ end
127
+
128
+ # @return {Lebowski::Coords}
129
+ def get_sc_element_window_position(type, *params)
130
+ value = __number_array_command("getScElementWindowPosition", [__locator(type, *params)])
131
+ return nil if value.nil?
132
+ return Coords.new(value[0], value[1])
133
+ end
134
+
135
+ def get_sc_view_child_view_count(scpath)
136
+ full_path = "#{scpath}.childViews.length"
137
+ return __number_command("getScPropertyValue", [full_path, ])
138
+ end
139
+
140
+ # SC View Object Event Selenium Calls
141
+
142
+ def sc_type(type, text, *params)
143
+ __remote_control_command("type", [__locator(type, *params), text, ])
144
+ end
145
+
146
+ def sc_select(type, optionLocator, *params)
147
+ __remote_control_command("select", [__locator(type, *params), optionLocator, ])
148
+ end
149
+
150
+ def sc_mouse_move(type, *params)
151
+ __remote_control_command("mouseMove", [__locator(type, *params),])
152
+ end
153
+
154
+ alias_method :sc_mouse_enter, :sc_mouse_move
155
+ alias_method :sc_mouse_exit, :sc_mouse_move
156
+
157
+ def sc_mouse_move_at(type, x, y, *params)
158
+ coords = "#{x},#{y}"
159
+ __remote_control_command("mouseMoveAt", [__locator(type, *params), coords])
160
+ end
161
+
162
+ def sc_mouse_down(type, *params)
163
+ __remote_control_command("scMouseDown", [__locator(type, *params),])
164
+ end
165
+
166
+ def sc_mouse_up(type, *params)
167
+ __remote_control_command("scMouseUp", [__locator(type, *params), ])
168
+ end
169
+
170
+ def sc_mouse_down_at(type, x, y, *params)
171
+ coords = "#{x},#{y}"
172
+ __remote_control_command("mouseDownAt", [__locator(type, *params), coords])
173
+ end
174
+
175
+ def sc_mouse_up_at(type, x, y, *params)
176
+ coords = "#{x},#{y}"
177
+ __remote_control_command("mouseUpAt", [__locator(type, *params), coords])
178
+ end
179
+
180
+ def sc_right_mouse_down(type, *params)
181
+ __remote_control_command("scMouseDownRight", [__locator(type, *params), ])
182
+ end
183
+
184
+ def sc_right_mouse_up(type, *params)
185
+ __remote_control_command("scMouseUpRight", [__locator(type, *params), ])
186
+ end
187
+
188
+ def sc_right_mouse_down_at(type, x, y, *params)
189
+ coords = "#{x},#{y}"
190
+ __remote_control_command("mouseDownRightAt", [__locator(type, *params), coords])
191
+ end
192
+
193
+ def sc_right_mouse_up_at(type, x, y, *params)
194
+ coords = "#{x},#{y}"
195
+ __remote_control_command("mouseUpRightAt", [__locator(type, *params), coords])
196
+ end
197
+
198
+ def sc_basic_click(type, *params)
199
+ __remote_control_command("click", [__locator(type, *params), ])
200
+ end
201
+
202
+ def sc_click(type, *params)
203
+ __remote_control_command("scClick", [__locator(type, *params), ])
204
+ end
205
+
206
+ def sc_right_click(type, *params)
207
+ __remote_control_command("scRightClick", [__locator(type, *params), ])
208
+ end
209
+
210
+ def sc_double_click(type, *params)
211
+ __remote_control_command("scDoubleClick", [__locator(type, *params), ])
212
+ end
213
+
214
+ def sc_focus(type, *params)
215
+ __remote_control_command("focus", [__locator(type, *params), ])
216
+ end
217
+
218
+ def sc_key_down(type, key, *params)
219
+ if key == :meta_key
220
+ meta_key_down
221
+ elsif key == :alt_key
222
+ alt_key_down
223
+ elsif key == :ctrl_key
224
+ control_key_down
225
+ elsif key == :shift_key
226
+ shift_key_down
227
+ elsif key.kind_of? Symbol
228
+ __remote_control_command("scFunctionKeyDown", [__locator(type, *params), key.to_s])
229
+ else
230
+ __remote_control_command("scKeyDown", [__locator(type, *params), key])
231
+ end
232
+ __register_key_as_down(key)
233
+ end
234
+
235
+ def sc_key_up(type, key, *params)
236
+ if key == :meta_key
237
+ meta_key_up
238
+ elsif key == :alt_key
239
+ alt_key_up
240
+ elsif key == :ctrl_key
241
+ control_key_up
242
+ elsif key == :shift_key
243
+ shift_key_up
244
+ elsif key.kind_of? Symbol
245
+ __remote_control_command("scFunctionKeyUp", [__locator(type, *params), key.to_s])
246
+ else
247
+ __remote_control_command("scKeyUp", [__locator(type, *params), key])
248
+ end
249
+ __register_key_as_up(key)
250
+ end
251
+
252
+ def key_down?(key)
253
+ return __key_pressed?(key)
254
+ end
255
+
256
+ def key_up?(key)
257
+ return (not __key_pressed?(key))
258
+ end
259
+
260
+ def sc_type_key(type, key, *params)
261
+ if key.kind_of? Symbol
262
+ __remote_control_command("scTypeFunctionKey", [__locator(type, *params), key.to_s, ])
263
+ else
264
+ __remote_control_command("scTypeKey", [__locator(type, *params), key, ])
265
+ end
266
+ end
267
+
268
+ def sc_view_scroll_to_visible(scpath)
269
+ __remote_control_command("scViewScrollToVisible", [scpath])
270
+ end
271
+
272
+ def sc_wait_until(root_scpath, join, conditions, timeout=nil)
273
+ params = {
274
+ :rootPath => root_scpath,
275
+ :join => join,
276
+ :conditions => conditions,
277
+ :timeout => timeout
278
+ }
279
+
280
+ encoded_params = ObjectEncoder.encode_hash(params)
281
+
282
+ __remote_control_command("scWaitUntil", [encoded_params])
283
+ end
284
+
285
+ # SC Core Query Selenium Calls
286
+
287
+ def sc_core_query_done(handle)
288
+ __remote_control_command("scCoreQueryDone", [handle,])
289
+ end
290
+
291
+ def get_sc_core_query(scpath, selector)
292
+ return __number_command("getScCoreQuery", [scpath, selector])
293
+ end
294
+
295
+ def get_sc_core_query_size(handle)
296
+ return __number_command("getScCoreQuerySize", [handle,])
297
+ end
298
+
299
+ def get_sc_core_query_element_classes(handle, elemIndex)
300
+ return __string_command("getScCoreQueryElementClasses", [handle, elemIndex,])
301
+ end
302
+
303
+ def get_sc_core_query_element_html(handle, elemIndex)
304
+ return __string_command("getScCoreQueryElementHTML", [handle, elemIndex,])
305
+ end
306
+
307
+ def get_sc_core_query_element_attribute(handle, elemIndex, attribute)
308
+ return __string_command("getScCoreQueryElementAttribute", [handle, "#{elemIndex}:#{attribute}",])
309
+ end
310
+
311
+ def get_sc_core_query_element_text(handle, elemIndex)
312
+ return __string_command("getScCoreQueryElementText", [handle, elemIndex,])
313
+ end
314
+
315
+ def get_sc_core_query_element_tag(handle, elemIndex)
316
+ return __string_command("getScCoreQueryElementTag", [handle, elemIndex,])
317
+ end
318
+
319
+ # SC Collection View Selenium Calls
320
+
321
+ def get_sc_collection_view_content_group_indexes(scpath)
322
+ return __number_array_command("getScCollectionViewContentGroupIndexes", [scpath,])
323
+ end
324
+
325
+ def get_sc_collection_view_content_selected_indexes(scpath)
326
+ return __number_array_command("getScCollectionViewContentSelectedIndexes", [scpath,])
327
+ end
328
+
329
+ def get_sc_collection_view_content_now_showing_indexes(scpath)
330
+ return __number_array_command("getScCollectionViewContentNowShowingIndexes", [scpath,])
331
+ end
332
+
333
+ def get_sc_collection_view_content_is_selected(scpath, index)
334
+ return __boolean_command("getScCollectionViewContentIsSelected", [scpath, index,])
335
+ end
336
+
337
+ def get_sc_collection_view_content_is_group(scpath, index)
338
+ return __boolean_command("getScCollectionViewContentIsGroup", [scpath, index,])
339
+ end
340
+
341
+ def get_sc_collection_view_content_disclosure_state(scpath, index)
342
+ return __number_command("getScCollectionViewContentDisclosureState", [scpath, index,])
343
+ end
344
+
345
+ def get_sc_collection_view_content_outline_level(scpath, index)
346
+ return __number_command("getScCollectionViewContentOutlineLevel", [scpath, index,])
347
+ end
348
+
349
+ # Selenium User Extension Utility Function Selenium Calls
350
+
351
+ def get_sc_object_array_index_lookup(scpath, lookup_params)
352
+ encoded_params = ObjectEncoder.encode_hash(lookup_params)
353
+ return __number_array_command("getScObjectArrayIndexLookup", [scpath, encoded_params])
354
+ end
355
+
356
+ def get_sc_selection_set_indexes(scpath)
357
+ return __number_array_command("getScSelectSetIndexes", [scpath, ])
358
+ end
359
+
360
+ def get_sc_localized_string(str)
361
+ if @localized_string_cache.nil?
362
+ @localized_string_cache = {}
363
+ end
364
+
365
+ if @localized_string_cache.has_key?(str)
366
+ return @localized_string_cache[str]
367
+ else
368
+ val = __string_command("getScLocalizedString", [str, ])
369
+ @localized_string_cache[str] = val
370
+ return val
371
+ end
372
+ end
373
+
374
+ def sc_window_move_to(x, y)
375
+ __remote_control_command("scWindowMoveTo", [x, y])
376
+ end
377
+
378
+ def sc_window_resize_to(width, height)
379
+ __remote_control_command("scWindowResizeTo", [width, height])
380
+ end
381
+
382
+ def sc_window_maximize()
383
+ __remote_control_command("scWindowMaximize")
384
+ end
385
+
386
+ # Selenium User Extensions Testing/Debugging Calls
387
+
388
+ def __sc_test_computing_property_path(key, path)
389
+ __remote_control_command("scTestComputePropertyPath", [key.to_s, path,])
390
+ end
391
+
392
+ def __sc_test_sending_encoded_hash(key, hash)
393
+ str = ObjectEncoder.encode_hash(hash)
394
+ __remote_control_command("scTestDecodingEncodedHash", [key.to_s, str,])
395
+ end
396
+
397
+ def __sc_test_sending_encoded_array(key, array)
398
+ str = ObjectEncoder.encode_array(array)
399
+ __remote_control_command("scTestDecodingEncodedArray", [key.to_s, str,])
400
+ end
401
+
402
+ def __sc_test_object_array_lookup(key, path, lookup)
403
+ params = ObjectEncoder.encode_hash({
404
+ :key => key.to_s,
405
+ :path => path,
406
+ :lookup => lookup,
407
+ })
408
+ __remote_control_command("scTestObjectArrayLookup", [params,])
409
+ end
410
+
411
+ protected
412
+
413
+ #
414
+ # @private
415
+ #
416
+ # Will convert the given type and parameters into a locator recognized by Selenium
417
+ #
418
+ def __locator(type, *params)
419
+ loc = ""
420
+ case type
421
+ when :view
422
+ loc = "scPath=#{params[0]}"
423
+ when :core_query_element
424
+ loc = "scCoreQuery=#{params[0]}:#{params[1]}"
425
+ else
426
+ loc = "scPath=#{params[0]}"
427
+ end
428
+ return loc
429
+ end
430
+
431
+ def __remote_control_command(verb, args=[])
432
+ if session_id.nil?
433
+ raise RemoteControlError.new "Unable to execute remote control command: A session ID is required"
434
+ end
435
+
436
+ timeout(default_timeout_in_seconds) do
437
+ data = http_request_for(verb, args)
438
+ status, response = __http_post(data)
439
+
440
+ if status != "OK"
441
+ if response =~ REMOTE_CONTROL_COMMAND_TIMEOUT
442
+ raise RemoteControlCommandTimeoutError, response
443
+ else
444
+ called_from = caller.detect{|line| line !~ /(selenium-client|vendor|usr\/lib\/ruby|\(eval\))/i}
445
+ err_message = "Received error from server while trying to execute command:\n"
446
+ err_message << "requested:\n"
447
+ err_message << "\t" + CGI::unescape(data.split('&').join("\n\t")) + "\n"
448
+ err_message << "received:\n"
449
+ err_message << "\t#{response}\n"
450
+ raise RemoteControlCommandExecutionError, err_message
451
+ end
452
+ end
453
+
454
+ return response[3..-1] # strip "OK," from response
455
+ end
456
+ end
457
+
458
+ def __http_post(data)
459
+ http = Net::HTTP.new(@host, @port)
460
+ http.open_timeout = default_timeout_in_seconds
461
+ http.read_timeout = default_timeout_in_seconds
462
+ begin
463
+ response = http.post('/selenium-server/driver/', data, HTTP_HEADERS)
464
+ rescue Exception => ex
465
+ err_message = "Error communicating with selenium server: #{ex.message}\n"
466
+ err_message << "Confirm that selenium server is running"
467
+ raise SeleniumServerError.new err_message
468
+ end
469
+ [ response.body[0..1], response.body ]
470
+ end
471
+
472
+ def __string_command(verb, args=[])
473
+ __remote_control_command(verb, args)
474
+ end
475
+
476
+ #
477
+ # @private
478
+ #
479
+ # Use this method instead of the number_command method. The number_command method
480
+ # in the selenium-client does not actually convert a string into an int where this
481
+ # method will
482
+ #
483
+ def __number_command(verb, args)
484
+ return __string_command(verb, args).to_i
485
+ end
486
+
487
+ #
488
+ # @private
489
+ #
490
+ # User this method instead of the boolean_command method. This method will handle
491
+ # cases when the returned value from the server is neither "true" not "false". In
492
+ # such a case the raw value is returned. The reflects cases when a returned value
493
+ # is typically expected to be a true or false boolean value, but in certain
494
+ # situations can be a none boolean value.
495
+ #
496
+ def __boolean_command(verb, args)
497
+ val = __string_command(verb, args)
498
+ return true if (val == "true")
499
+ return false if (val == "false")
500
+ return val
501
+ end
502
+
503
+ #
504
+ # @private
505
+ #
506
+ # Use this method instead of the string_array_command method. The
507
+ # string_array_command method does not handle the case when the
508
+ # returned value from the server is null where as this method will
509
+ #
510
+ def __string_array_command(verb, args)
511
+ csv = string_command(verb, args)
512
+ return [] if csv.nil? or csv.empty?
513
+ token = ""
514
+ tokens = []
515
+ escape = false
516
+ csv.split(//).each do |letter|
517
+ if escape
518
+ token += letter
519
+ escape = false
520
+ next
521
+ end
522
+ case letter
523
+ when '\\'
524
+ escape = true
525
+ when ','
526
+ tokens << token
527
+ token = ""
528
+ else
529
+ token += letter
530
+ end
531
+ end
532
+ tokens << token
533
+ return tokens
534
+ end
535
+
536
+ #
537
+ # @private
538
+ #
539
+ # Use this method instead of the number_array_command method
540
+ #
541
+ def __number_array_command(verb, args)
542
+ val = __string_array_command(verb, args)
543
+ val.collect! { |x| x.to_i }
544
+ return val
545
+ end
546
+
547
+ #
548
+ # @private
549
+ #
550
+ # Use this method instead of the boolean_array_command method. This
551
+ # method will handle cases when the value is neither true nor false. In
552
+ # such a case the raw value is returned. In done in cases when a return
553
+ # value is typicallys a true or false boolean value, but in particular
554
+ # situations can be a none boolean value.
555
+ #
556
+ def __boolean_array_command(verb, args)
557
+ val = __string_array_command(verb, args)
558
+ val.collect! do |x|
559
+ if ("true" == x)
560
+ true
561
+ elsif ("false" == x)
562
+ false
563
+ else
564
+ x
565
+ end
566
+ end
567
+ return val
568
+ end
569
+
570
+ #
571
+ # @private
572
+ #
573
+ def __pressed_keys
574
+ @pressed_keys = {} if @pressed_keys.nil?
575
+ return @pressed_keys
576
+ end
577
+
578
+ #
579
+ # @private
580
+ #
581
+ def __register_key_as_down(key)
582
+ keys = __pressed_keys
583
+ keys[key] = key
584
+ end
585
+
586
+ #
587
+ # @private
588
+ #
589
+ def __register_key_as_up(key)
590
+ keys = __pressed_keys
591
+ keys.delete key
592
+ end
593
+
594
+ def __key_pressed?(key)
595
+ return _pressed_keys.has_key?(key)
596
+ end
597
+
598
+ end
599
+ end
600
+ end