lebowski 0.1.0

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