rubium-ios 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bcbe9463c012f64333f88e922ae5389bac71686e
4
+ data.tar.gz: a70251807d1d63921d943adc60633c402c2cf96b
5
+ SHA512:
6
+ metadata.gz: e342b39c417d613e69cb7569ef112d221093f8002f6bc150e0ad71a3d435834ef9fa1873089fd52d92772669ea0c4a4ed3a3acdf54b18382a8f45646eb75402b
7
+ data.tar.gz: 5a9783145a2ae2cdbfcbc8513b39917229d25519b5d81d4101192a7d7ffbb1471dd253722897ebb40589facb6be8417b500f1ae0bd539bff0c3eee4a7fa122a8
@@ -0,0 +1,21 @@
1
+ module Rubium
2
+ class << self
3
+ def default_host
4
+ 'localhost'
5
+ end
6
+
7
+ def default_port
8
+ 4723
9
+ end
10
+
11
+ def root_path
12
+ "/wd/hub"
13
+ end
14
+ end
15
+ end
16
+
17
+ require 'ui_automation'
18
+ require 'ui_automation/element_proxy_methods'
19
+ require 'rubium'
20
+ require 'rubium/driver'
21
+ require 'rubium/capabilities'
@@ -0,0 +1,92 @@
1
+ module Rubium
2
+ module Capabilities
3
+ class Base
4
+ LATEST_SDK_VERSION = '7.1'
5
+
6
+ attr_accessor :platform_name
7
+ attr_accessor :platform_version
8
+ attr_accessor :device_name
9
+ attr_accessor :new_command_timeout
10
+ attr_accessor :app
11
+ attr_accessor :bundle_id
12
+ attr_accessor :launch_timeout
13
+ attr_accessor :auto_accept_alerts
14
+ attr_accessor :process_arguments
15
+
16
+ def initialize(values = {}, &block)
17
+ values.merge(default_values).each do |k, v|
18
+ __send__("#{k}=", v) if respond_to?(k)
19
+ end
20
+
21
+ yield self if block_given?
22
+ end
23
+
24
+ def to_hash(additional_keys = {})
25
+ without_nil_values additional_keys.merge({
26
+ 'platformName' => platform_name,
27
+ 'platformVersion' => platform_version,
28
+ 'deviceName' => device_name,
29
+ 'newCommandTimeout' => new_command_timeout,
30
+ 'app' => app,
31
+ 'bundleId' => bundle_id,
32
+ 'launchTimeout' => launch_timeout,
33
+ 'autoAcceptAlerts' => auto_accept_alerts,
34
+ 'processArguments' => process_arguments,
35
+ 'appium-version' => "1.0"
36
+ })
37
+ end
38
+
39
+ private
40
+
41
+ def without_nil_values(hash)
42
+ hash.delete_if { |k, v| v.nil? }
43
+ end
44
+
45
+ def default_values
46
+ {
47
+ platform_name: 'iOS',
48
+ platform_version: LATEST_SDK_VERSION,
49
+ new_command_timeout: 30
50
+ }
51
+ end
52
+ end
53
+
54
+ class Simulator < Base
55
+ attr_accessor :language
56
+ attr_accessor :orientation
57
+ attr_accessor :calendar_format
58
+ attr_accessor :location_services_enabled
59
+ attr_accessor :location_services_authorized
60
+
61
+ def to_hash(additional_keys = {})
62
+ super({
63
+ 'language' => language,
64
+ 'orientation' => orientation,
65
+ 'calendarFormat' => calendar_format,
66
+ 'locationServicesEnabled' => location_services_authorized,
67
+ 'locationServicesAuthorized' => location_services_authorized
68
+ })
69
+ end
70
+
71
+ private
72
+
73
+ def default_values
74
+ super.merge({
75
+ locale: 'en',
76
+ orientation: 'PORTRAIT',
77
+ device_name: 'iPhone Simulator'
78
+ })
79
+ end
80
+ end
81
+
82
+ class Device < Base
83
+ attr_accessor :udid
84
+
85
+ def to_hash(additional_keys = {})
86
+ super({
87
+ 'udid' => udid
88
+ })
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,233 @@
1
+ require 'rubium/session'
2
+
3
+ module Rubium
4
+ class Driver
5
+ attr_accessor :implicit_timeout
6
+
7
+ # The default session timeout, in seconds
8
+ DEFAULT_SESSION_TIMEOUT = 30
9
+
10
+ def initialize(capabilities, host = Rubium.default_host, port = Rubium.default_port)
11
+ @capabilities = capabilities
12
+ @host = host
13
+ @port = port
14
+ @implicit_timeout = 1
15
+ end
16
+
17
+ ### @!group Session Management
18
+
19
+ # Launches a new Appium session.
20
+ #
21
+ # Launching a new Appium session will cause Appium to launch Instruments, which
22
+ # in turn will launch your application in the simulator or on your device.
23
+ #
24
+ # @param [Numeric] session_timeout the underlying HTTP session timeout, in seconds
25
+ # @raise [Rubium::Session::ConnectionError] if could not connect to the server.
26
+ #
27
+ def launch(session_timeout = DEFAULT_SESSION_TIMEOUT)
28
+ @session ||= Rubium::Session.new(@host, @port, @capabilities, session_timeout)
29
+ update_implicit_timeout
30
+ end
31
+
32
+ # Quits the current session, if there is one.
33
+ #
34
+ # When you quit a session, Appium will terminate the Instruments process which will
35
+ # in turn kill the iOS simulator or remove the app from your device.
36
+ #
37
+ def quit
38
+ @session.terminate if @session
39
+ @session = nil
40
+ end
41
+
42
+ # Launches a new session, calls the given block, then quits.
43
+ #
44
+ # This method lets you treat a session as a transaction, with the given block being
45
+ # executed after launching then quitting the session when the block returns.
46
+ #
47
+ # Using this method ensures you do not have to explicitly quit the session when you
48
+ # are finished.
49
+ #
50
+ # This method will quit the session after the block has finished executing, even if
51
+ # the block raises an exception.
52
+ #
53
+ # @param [Numeric] session_timeout the underlying HTTP session timeout, in seconds
54
+ # @raise [RuntimeError] if a session is already running
55
+ #
56
+ def with_session(session_timeout = DEFAULT_SESSION_TIMEOUT, &block)
57
+ raise "Session already running!" if @session
58
+ launch(session_timeout)
59
+ begin
60
+ yield @session if block_given?
61
+ ensure
62
+ quit
63
+ end
64
+ end
65
+
66
+ # Quits any existing session before launching a new one.
67
+ #
68
+ def relaunch
69
+ quit
70
+ launch
71
+ end
72
+
73
+ ### @!endgroup
74
+
75
+ ### @!group Managing timeouts
76
+
77
+ # Sets the implicit timeout used by the underlying Selenium::WebDriver instance.
78
+ #
79
+ # Implicit timeouts are used when trying to find elements on the screen using the
80
+ # low-level #find and #find_all methods. They do not affect any remotely executed
81
+ # Javascript and therefore have no affect on code that uses the native Javascript
82
+ # proxy APIs.
83
+ #
84
+ # @param [Numeric] value The new timeout value, in seconds
85
+ #
86
+ def implicit_timeout=(value)
87
+ @implicit_timeout = value
88
+ update_implicit_timeout
89
+ end
90
+
91
+ # Temporarily sets the implicit timeout to the given value and invokes the block.
92
+ #
93
+ # After the block has been invoked, the original timeout will be restored.
94
+ #
95
+ # @param [Numeric] timeout The temporary timeout, in seconds
96
+ #
97
+ def with_implicit_timeout(timeout, &block)
98
+ update_implicit_timeout(timeout)
99
+ yield
100
+ update_implicit_timeout
101
+ end
102
+
103
+ # Returns the native Javascript API implicit timeout
104
+ # @see UIATarget.timeout()
105
+ #
106
+ def native_timeout
107
+ target.timeout
108
+ end
109
+
110
+ # Sets the native Javascript API implicit timeout
111
+ # @param [Numeric] new_timeout The new timeout, in seconds
112
+ # @see UIATarget.setTimeout()
113
+ #
114
+ def native_timeout=(new_timeout)
115
+ target.set_timeout(new_timeout)
116
+ end
117
+
118
+ # Temporarily sets the native Javascript API implicit timeout by pushing a new
119
+ # timeout on to the timeout stack, calling the given block and then popping the
120
+ # timeout off the stack.
121
+ #
122
+ # @param [Numeric] value The temporary timeout, in seconds
123
+ # @see UIATarget.pushTimeout(), UIATarget.popTimeout()
124
+ #
125
+ def with_native_timeout(value, &block)
126
+ target.push_timeout(value)
127
+ yield if block_given?
128
+ target.pop_timeout
129
+ end
130
+
131
+ class TimeoutError < RuntimeError; end
132
+
133
+ # Performs an explicit wait until the given block returns true.
134
+ #
135
+ # You can use this method to wait for an explicit condition to occur
136
+ # before continuing.
137
+ #
138
+ # @example
139
+ # driver.wait_until { something_happened }
140
+ #
141
+ # @param [Numeric] timeout The explicit wait timeout, in seconds
142
+ # @param [Numeric] interval The interval to wait between retries.
143
+ # @yieldreturn [Boolean] The block will be repeatedly called up to the timeout until it returns true.
144
+ # @raise [Rubium::Driver::TimeoutError] if the wait times out
145
+ #
146
+ def wait_until(timeout: 1, interval: 0.2, &block)
147
+ Selenium::WebDriver::Wait.new(timeout: timeout, interval: interval).until(&block)
148
+ rescue Selenium::WebDriver::Error::TimeOutError => e
149
+ raise TimeoutError.new(e.message)
150
+ end
151
+
152
+ ### @!endgroup
153
+
154
+ def find(xpath)
155
+ element_proxy_for driver.find_element(:xpath, xpath)
156
+ rescue Selenium::WebDriver::Error::NoSuchElementError => e
157
+ UIAutomation::NoSuchElement.new
158
+ end
159
+
160
+ def find_all(xpath)
161
+ driver.find_elements(:xpath, xpath)
162
+ end
163
+
164
+ def capture_screenshot(output_file, format = :png)
165
+ File.open(output_file, 'wb') { |io| io.write driver.screenshot_as(format) }
166
+ end
167
+
168
+ ### @!group Javascript Proxy Methods
169
+
170
+ # Returns a proxy to the local target (UIATarget).
171
+ #
172
+ # This method is the main entry point into the UIAutomation Javascript proxy API.
173
+ # The local target is the root object in the UIAutomation object graph.
174
+ #
175
+ # @return [UIAutomation::Target] A proxy to the local target (UIATarget.localTarget())
176
+ #
177
+ def target
178
+ @target ||= UIAutomation::Target.local_target(self)
179
+ end
180
+
181
+ # Returns a proxy to the native UIAutomation logger.
182
+ #
183
+ # @return [UIAutomation::Logger]
184
+ def logger
185
+ @logger ||= UIAutomation::Logger.logger(self)
186
+ end
187
+
188
+ # Executes a string of Javascript within the Instruments process.
189
+ #
190
+ # @param [String] script the Javascript to be executed.
191
+ # @raise [Selenium::WebDriver::Error::JavascriptError] if the evaluated Javascript errors.
192
+ # @note This method will always return immediately in the case of an error, regardless of# any implicit or native timeout set. If you need to execute some Javascript until it is successful, you should consider using an explicit wait.
193
+ #
194
+ def execute_script(script)
195
+ driver.execute_script(script)
196
+ end
197
+ alias :execute :execute_script
198
+
199
+ ### @!endgroup
200
+
201
+ private
202
+
203
+ def update_implicit_timeout(value = implicit_timeout)
204
+ driver.manage.timeouts.implicit_wait = value if @session
205
+ end
206
+
207
+ def driver
208
+ raise "You must call #launch to start a session first!" unless @session
209
+ @session.driver
210
+ end
211
+
212
+ ELEMENT_PROXY_MAPPING = {
213
+ 'UIAKeyboard' => UIAutomation::Keyboard,
214
+ 'UIATabBar' => UIAutomation::TabBar,
215
+ 'UIATableView' => UIAutomation::TableView,
216
+ 'UIATextField' => UIAutomation::TextField,
217
+ 'UIASecureTextField' => UIAutomation::TextField,
218
+ 'UIASearchBar' => UIAutomation::TextField,
219
+ 'UIAWindow' => UIAutomation::Window,
220
+ 'UIANavigationBar' => UIAutomation::NavigationBar,
221
+ 'UIAActionSheet' => UIAutomation::ActionSheet,
222
+ 'UIAActivityView' => UIAutomation::ActivityView,
223
+ 'UIAPicker' => UIAutomation::Picker,
224
+ 'UIAPopover' => UIAutomation::Popover,
225
+ 'UIATextView' => UIAutomation::TextView
226
+ }
227
+
228
+ def element_proxy_for(element)
229
+ proxy_klass = ELEMENT_PROXY_MAPPING[element.tag_name] || UIAutomation::Element
230
+ proxy_klass.from_element_id(driver, element.ref, nil, nil)
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,26 @@
1
+ require 'selenium-webdriver'
2
+
3
+ module Rubium
4
+ class Session
5
+ attr_reader :driver
6
+
7
+ def initialize(host, port, capabilities, timeout = 30)
8
+ client = Selenium::WebDriver::Remote::Http::Default.new
9
+ client.timeout = timeout
10
+
11
+ @driver = Selenium::WebDriver.for(:remote,
12
+ desired_capabilities: capabilities.to_hash,
13
+ url: "http://#{host}:#{port}#{Rubium.root_path}",
14
+ http_client: client
15
+ )
16
+ rescue Errno::ECONNREFUSED
17
+ raise ConnectionError
18
+ end
19
+
20
+ def terminate
21
+ @driver.quit rescue nil
22
+ end
23
+
24
+ class ConnectionError < RuntimeError; end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module Rubium
2
+ module Version
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ TINY = 0
6
+
7
+ def self.to_s
8
+ version = [MAJOR, MINOR, TINY].join(".")
9
+ prerelease ? "#{version}.pre" : version
10
+ end
11
+
12
+ def self.prerelease
13
+ true
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,325 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+ require 'json'
3
+
4
+ module UIAutomation
5
+ # RemoteProxy acts as a proxy or facade to Appium's ability to remotely execute
6
+ # Javascript within the Instruments runtime environment.
7
+ #
8
+ # Rather than having to manual build strings of Javascript using the Apple UIAutomation
9
+ # API and executing them using Rubium::Driver#execute, you can use a RemoteProxy as
10
+ # if it was an instance of a Javascript object within the UIAutomation API.
11
+ #
12
+ # You can fetch values of properties, perform methods and obtain new proxies to objects
13
+ # returned by a Javascript method.
14
+ #
15
+ # As well as providing explicit APIs for fetching properties and performing methods,
16
+ # you can also use square-bracket notation for accessing properties and for methods,
17
+ # you can just call them on the proxy as if they were Ruby methods. You can perform
18
+ # any method that is defined in the UIAutomation API and you can also use underscore_case
19
+ # - it will automatically be converted to `lowerCamelCase`.
20
+ #
21
+ # Generally, you wouldn't use this directly but would instead use one of the sub-classes
22
+ # within the library: the Ruby mirror of the UIAutomation Javascript API is built on top
23
+ # of this class.
24
+ #
25
+ # @example Fetch the 'model' from the local target
26
+ # executor = Rubium::Driver.new(capabilities)
27
+ # target = UIAutomation::RemoteProxy.new(executor, "UIATarget.localTarget()")
28
+ # puts target.model # => 'iOS Simulator'
29
+ #
30
+ # @example Set the simulator location on the local target
31
+ # target = UIAutomation::RemoteProxy.new(executor, "UIATarget.localTarget()")
32
+ # target.set_location(lat: 90.0, lng: -10.0)
33
+ #
34
+ # @example Print the rect of the main window
35
+ # target = UIAutomation::RemoteProxy.new(executor, "UIATarget.localTarget()")
36
+ # application = target.proxy_for(:frontMostApp)
37
+ # main_window = application.proxy_for(:mainWindow)
38
+ # puts main_window.rect
39
+ #
40
+ class RemoteProxy
41
+ class << self
42
+ attr_accessor :debug_on_exception
43
+ end
44
+
45
+ # Creates a new RemoteProxy instance.
46
+ #
47
+ # Generally, the executor param will be an instance of `Rubium::Driver` but it
48
+ # can be any object that responds to `#execute(string)` and is able to execute the
49
+ # Javascript remotely using the Selenium web-driver protocol.
50
+ #
51
+ # A string of Javascript can be passed as the second parameter and it will automatically be
52
+ # converted into an instance of `RemoteJavascriptObject`.
53
+ #
54
+ # @note Use the factory methods `from_javascript` or `from_element_id` instead
55
+ # @api private
56
+ # @param [<Object#execute>] executor An object that can execute remote Javascript
57
+ # @param [String, RemoteJavascriptObject] remote_object_or_string A Javascript representation of the object to be proxied.
58
+ #
59
+ def initialize(executor, remote_object_or_string)
60
+ @executor = executor
61
+ @remote_object = remote_object_from(remote_object_or_string)
62
+ end
63
+
64
+ ### @!group Factory methods
65
+
66
+ # Returns a new RemoteProxy instance from a string of Javascript.
67
+ #
68
+ # @see #initialize
69
+ #
70
+ def self.from_javascript(executor, javascript, *args)
71
+ new(executor, RemoteJavascriptObject.new(javascript), *args)
72
+ end
73
+
74
+ # Returns a new RemoteProxy instance from an Appium element ID.
75
+ #
76
+ # @note This method uses Appium's own internal element IDs returned from an XPath
77
+ # query and should not be used directly.
78
+ #
79
+ # @see #initialize
80
+ # @see Rubium::Driver#find
81
+ #
82
+ def self.from_element_id(executor, element_id, *args)
83
+ new(executor, RemoteObjectByElementID.new(element_id), *args)
84
+ end
85
+
86
+ ### @!endgroup
87
+
88
+ ### @!group Proxy Methods
89
+
90
+ # Returns a new proxy to the Javascript object returned from the method called on the object
91
+ # represented by self.
92
+ #
93
+ # For instance, if the current proxy represents the object `UIATarget.localTarget()` and
94
+ # you call this method with the method name :frontMostApp, it will return a proxy to the object
95
+ # represented in the Javascript API by `UIATarget.localTarget().frontMostApp()`.
96
+ #
97
+ # @param [Symbol] function_name The Javascript method name that returns the object to be proxied
98
+ # @param [Array] function_args Any arguments that should be passed to the Javascript method
99
+ # @param [Class] proxy_klass The type of RemoteProxy class to use (must be a sub-class of RemoteProxy)
100
+ # @param [Array] proxy_args Any additional arguments required to initialize a specific RemoteProxy sub-class.
101
+ # @raise `TypeError` if proxy_klass is not a valid sub-class of RemoteProxy
102
+ # @return [RemoteProxy] default return type
103
+ # @return [proxy_klass] if specified
104
+ #
105
+ def proxy_for(function_name, function_args: [], proxy_klass: RemoteProxy, proxy_args: [])
106
+ raise TypeError.new("proxy_klass must be a RemoteProxy or sub-class") unless proxy_klass <= RemoteProxy
107
+ build_proxy(proxy_klass, remote_object.object_for_function(function_name, *function_args), proxy_args)
108
+ end
109
+
110
+ # Performs a function on the current Javascript object and returns the raw value.
111
+ #
112
+ # This is useful for any methods that return values like strings, hashes and numbers.
113
+ #
114
+ # If you use this to call a method that would normally return another Javascript object
115
+ # this will simply return an empty hash. Use `#proxy_for` to return a new proxy to
116
+ # another Javascript object instead.
117
+ #
118
+ # @param [Symbol] function The name of the Javascript method to call on the object represented by self
119
+ # @param [args] args A list of arguments to be passed to the Javascript method
120
+ # @see #method_missing
121
+ #
122
+ def perform(function, *args)
123
+ @executor.execute_script(remote_object.object_for_function(function, *args).javascript)
124
+ rescue StandardError => e
125
+ binding.pry if self.class.debug_on_exception
126
+ raise "Error performing javascript: #{javascript} (server error: #{e})"
127
+ end
128
+
129
+ # Fetches the value of the named property on the current Javascript object.
130
+ #
131
+ # @param [Symbol] property The name of the property to return
132
+ # @return [Object] The Ruby equivalent of whatever the Javascript method returns.
133
+ #
134
+ def fetch(property)
135
+ @executor.execute_script(remote_object.object_for_property(property).javascript)
136
+ rescue StandardError => e
137
+ binding.pry if self.class.debug_on_exception
138
+ raise "Error performing javascript: #{javascript} (server error: #{e})"
139
+ end
140
+
141
+ # Can be used as an alternative to calling #fetch
142
+ # @see #fetch
143
+ #
144
+ def [](property)
145
+ fetch(property)
146
+ end
147
+
148
+ # @api private
149
+ def execute_self
150
+ @executor.execute_script(remote_object.javascript)
151
+ end
152
+
153
+ ### @!endgroup
154
+
155
+ ### @!group Debugging
156
+
157
+ # Returns the Javascript representation
158
+ # @return [String]
159
+ # @see #to_javascript
160
+ #
161
+ def to_s
162
+ to_javascript
163
+ end
164
+
165
+
166
+ def inspect
167
+ "<RemoteProxy(#{self.class.name}): #{to_javascript}>"
168
+ end
169
+
170
+ # @return [String] the Javascript representation of the proxied object
171
+ #
172
+ def to_javascript
173
+ @remote_object.javascript
174
+ end
175
+ alias :javascript :to_javascript
176
+
177
+ ### @!endgroup
178
+
179
+ # Represents a remote javascript object using raw javascript, e.g.
180
+ # to represent the main application, you would initialize this with
181
+ # the string 'UIATarget.currentTarget().frontMostApp()'
182
+ #
183
+ # @api private
184
+ #
185
+ class RemoteJavascriptObject
186
+ def initialize(javascript)
187
+ @javascript = javascript
188
+ end
189
+
190
+ def javascript
191
+ @javascript
192
+ end
193
+
194
+ def to_s
195
+ javascript
196
+ end
197
+
198
+ def object_for_function(function_name, *args)
199
+ RemoteJavascriptObject.new("#{javascript}.#{function_name}(#{format_args(args)})")
200
+ end
201
+
202
+ def object_for_subscript(subscript)
203
+ RemoteJavascriptObject.new("#{javascript}[#{format_arg(subscript)}]")
204
+ end
205
+
206
+ def object_for_property(property_name)
207
+ RemoteJavascriptObject.new("#{javascript}.#{property_name}")
208
+ end
209
+
210
+ def format_arg(arg)
211
+ case arg
212
+ when String, Symbol
213
+ "'#{arg}'"
214
+ when Hash, Array
215
+ arg.to_json
216
+ else
217
+ arg
218
+ end
219
+ end
220
+
221
+ def format_args(args)
222
+ args.map { |arg| format_arg(arg) }.join(", ")
223
+ end
224
+ end
225
+
226
+ # Represents a remote javascript object by element ID, where element ID
227
+ # is the ID of a Selenium::WebDriver::Element returned by one of the built-in
228
+ # Selenium finder methods.
229
+ #
230
+ # This allows us to construct remote proxies to javascript objects that are found
231
+ # using e.g. an xpath without having to know the actual index path to the object
232
+ # in the UIAutomation javascript object tree.
233
+ #
234
+ # @api private
235
+ #
236
+ class RemoteObjectByElementID < RemoteJavascriptObject
237
+ def initialize(object_id)
238
+ @object_id = object_id
239
+ end
240
+
241
+ def javascript
242
+ # this uses internal APIs provided by appium-auto
243
+ "au.getElement(#{@object_id})"
244
+ end
245
+ end
246
+
247
+ private
248
+
249
+ def remote_object_from(remote_object_or_string)
250
+ if remote_object_or_string.is_a?(String)
251
+ RemoteJavascriptObject.new(remote_object_or_string)
252
+ elsif remote_object_or_string.is_a?(RemoteJavascriptObject)
253
+ remote_object_or_string
254
+ else
255
+ raise TypeError.new("Remote object must be a RemoteJavascriptObject or String, but was #{remote_object_or_string.class}")
256
+ end
257
+ end
258
+
259
+ # RemoteProxy lets you call methods that correspond to
260
+ # methods in the Javascript API without having to explicitly call perform().
261
+ #
262
+ # As with perform(), this should only be used for methods that return values rather
263
+ # than other objects.
264
+ #
265
+ # You can call methods in the Javascript API using snake_case or lowerCamelCase - all
266
+ # snake case methods will automatically be transformed into the Javascript lowerCamelCase
267
+ # equivalent (e.g. text_fields -> textFields).
268
+ #
269
+ # @param [Symbol] method will be converted to lowerCamelCase and used as the first argument to #perform
270
+ # @param [Array] args any arguments will be passed as arguments to the Javascript function
271
+ # @see #perform
272
+ #
273
+ def method_missing(method, *args, &block)
274
+ perform(method.to_s.camelize(:lower), *args)
275
+ end
276
+
277
+ def window
278
+ nil
279
+ end
280
+
281
+ def remote_object
282
+ @remote_object
283
+ end
284
+
285
+ def build_proxy(proxy_klass, remote_object, proxy_args)
286
+ proxy_klass.new(@executor, remote_object, *proxy_args)
287
+ end
288
+ end
289
+
290
+ class NoSuchElement
291
+ def method_missing(method, *args, &block)
292
+ if UIAutomation::Element.method_defined?(method)
293
+ return nil
294
+ else
295
+ warn "Tried to call #{method} on NoSuchElement"
296
+ super
297
+ end
298
+ end
299
+ end
300
+
301
+ require 'ui_automation/element_definitions'
302
+
303
+ autoload :Element, 'ui_automation/element'
304
+ autoload :ElementArray, 'ui_automation/element_array'
305
+ autoload :Application, 'ui_automation/application'
306
+ autoload :Window, 'ui_automation/window'
307
+ autoload :TableView, 'ui_automation/table_view'
308
+ autoload :TabBar, 'ui_automation/tab_bar'
309
+ autoload :NavigationBar, 'ui_automation/navigation_bar'
310
+ autoload :TextField, 'ui_automation/text_field'
311
+ autoload :Keyboard, 'ui_automation/keyboard'
312
+ autoload :Target, 'ui_automation/target'
313
+ autoload :Logger, 'ui_automation/logger'
314
+ autoload :Picker, 'ui_automation/picker'
315
+ autoload :Popover, 'ui_automation/popover'
316
+ autoload :TextView, 'ui_automation/text_view'
317
+ autoload :ActivityView, 'ui_automation/activity_view'
318
+ autoload :ActionSheet, 'ui_automation/action_sheet'
319
+ autoload :Alert, 'ui_automation/alert'
320
+
321
+ module Traits
322
+ autoload :Cancellable, 'ui_automation/traits/cancellable'
323
+ autoload :TextInput, 'ui_automation/traits/text_input'
324
+ end
325
+ end