calabash 2.0.0.pre2 → 2.0.0.pre3

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -26
  3. data/lib/calabash/android/adb.rb +9 -0
  4. data/lib/calabash/android/build/builder.rb +2 -0
  5. data/lib/calabash/android/build/java_keystore.rb +10 -0
  6. data/lib/calabash/android/build/resigner.rb +4 -0
  7. data/lib/calabash/android/build/test_server.rb +2 -0
  8. data/lib/calabash/android/defaults.rb +1 -0
  9. data/lib/calabash/android/device.rb +18 -0
  10. data/lib/calabash/android/interactions.rb +2 -0
  11. data/lib/calabash/android/lib/TestServer.apk +0 -0
  12. data/lib/calabash/android/life_cycle.rb +1 -0
  13. data/lib/calabash/android/physical_buttons.rb +8 -0
  14. data/lib/calabash/android/screenshot.rb +1 -0
  15. data/lib/calabash/android/server.rb +1 -0
  16. data/lib/calabash/android/text.rb +1 -0
  17. data/lib/calabash/cli/build.rb +1 -0
  18. data/lib/calabash/cli/console.rb +2 -0
  19. data/lib/calabash/cli/generate.rb +3 -0
  20. data/lib/calabash/cli/helpers.rb +6 -0
  21. data/lib/calabash/cli/resign.rb +1 -0
  22. data/lib/calabash/cli/run.rb +2 -0
  23. data/lib/calabash/cli/setup_keystore.rb +2 -0
  24. data/lib/calabash/color.rb +7 -0
  25. data/lib/calabash/defaults.rb +1 -0
  26. data/lib/calabash/device.rb +2 -0
  27. data/lib/calabash/environment.rb +1 -0
  28. data/lib/calabash/http/retriable_client.rb +2 -0
  29. data/lib/calabash/interactions.rb +1 -0
  30. data/lib/calabash/ios/conditions.rb +3 -0
  31. data/lib/calabash/ios/date_picker.rb +412 -0
  32. data/lib/calabash/ios/defaults.rb +1 -0
  33. data/lib/calabash/ios/device/device_implementation.rb +5 -1
  34. data/lib/calabash/ios/device/ipad_1x_2x_mixin.rb +253 -0
  35. data/lib/calabash/ios/device/keyboard_mixin.rb +2 -0
  36. data/lib/calabash/ios/device/rotation_mixin.rb +1 -0
  37. data/lib/calabash/ios/device/routes/condition_route_mixin.rb +1 -0
  38. data/lib/calabash/ios/device/routes/map_route_mixin.rb +1 -0
  39. data/lib/calabash/ios/device/routes/response_parser.rb +1 -0
  40. data/lib/calabash/ios/device/routes/uia_route_mixin.rb +44 -6
  41. data/lib/calabash/ios/device/text_mixin.rb +2 -0
  42. data/lib/calabash/ios/device/uia_keyboard_mixin.rb +9 -0
  43. data/lib/calabash/ios/device/uia_mixin.rb +1 -0
  44. data/lib/calabash/ios/device.rb +1 -0
  45. data/lib/calabash/ios/interactions.rb +30 -1
  46. data/lib/calabash/ios/runtime.rb +8 -0
  47. data/lib/calabash/ios.rb +2 -0
  48. data/lib/calabash/life_cycle.rb +19 -2
  49. data/lib/calabash/location.rb +2 -0
  50. data/lib/calabash/page.rb +13 -0
  51. data/lib/calabash/patch.rb +1 -0
  52. data/lib/calabash/utility.rb +4 -4
  53. data/lib/calabash/version.rb +1 -1
  54. data/lib/calabash/wait.rb +4 -0
  55. data/lib/calabash.rb +6 -4
  56. metadata +113 -112
  57. data/lib/calabash/android/scroll.rb +0 -5
@@ -1,5 +1,8 @@
1
1
  module Calabash
2
2
  module IOS
3
+
4
+ # Methods for waiting for things to happen on iOS, like animations to
5
+ # complete.
3
6
  module Conditions
4
7
 
5
8
  # Waits for all elements to stop animating.
@@ -0,0 +1,412 @@
1
+ require 'date'
2
+
3
+ module Calabash
4
+ module IOS
5
+
6
+ # A collection of methods for interacting with UIDatePicker.
7
+ module DatePicker
8
+
9
+ # The API has been tested in various time zones and tested
10
+ # once while crossing the international date line (on a boat).
11
+ # With that said, the API makes some assumptions about locales
12
+ # and time zones. It is possible to customize the ruby date
13
+ # format and Objective-C date format to get the behavior you
14
+ # need. You will need to monkey patch the following methods:
15
+ #
16
+ # * date_picker_ruby_date_format
17
+ # * date_picker_objc_date_format
18
+ #
19
+ # Before going down this path, we recommend that you ask for
20
+ # advice on the Calabash support channels.
21
+
22
+ # @!visibility private
23
+ # Provided for monkey patching, but not part of the public API.
24
+ def date_picker_ruby_date_format
25
+ RUBY_DATE_AND_TIME_FMT
26
+ end
27
+
28
+ # @!visibility private
29
+ # Provided for monkey patching, but not part of the public API.
30
+ def date_picker_objc_date_format
31
+ OBJC_DATE_AND_TIME_FMT
32
+ end
33
+
34
+ # @!visibility private
35
+ # Returns the picker mode of the first UIDatePicker match by `query`.
36
+ #
37
+ # @see #time_mode?
38
+ # @see #date_mode?
39
+ # @see #date_and_time_mode?
40
+ # @see #countdown_mode?
41
+ #
42
+ # @param [String, Hash, Calabash::Query] query A query that can be used
43
+ # to find UIDatePickers.
44
+ # @return [String] Returns the picker mode which will be one of
45
+ # `{'0', '1', '2', '3'}`
46
+ # @raise [RuntimeError] If no picker can be found.
47
+ # @raise [RuntimeError] If an unknown mode is returned.
48
+ # @raise [RuntimeError] If first view matched by query does not responde
49
+ # to 'datePickerMode'.
50
+ def date_picker_mode(query)
51
+ Query.ensure_valid_query(query)
52
+
53
+ message = "Timed out waiting for picker with #{query}"
54
+ mode = nil
55
+
56
+ wait_for(message) do
57
+ result = query(query, :datePickerMode)
58
+ if result.empty?
59
+ false
60
+ else
61
+ mode = result.first
62
+ if [0, 1, 2, 3].include?(mode)
63
+ mode
64
+ else
65
+ if mode == '*****'
66
+ raise RuntimeError,
67
+ "Query #{query} matched a view that does not respond 'datePickerMode'"
68
+ else
69
+ raise RuntimeError,
70
+ "Query #{query} returned an unknown mode '#{mode}' for 'datePickerMode'"
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ # Is the date picker in time mode?
78
+ #
79
+ # @see #time_mode?
80
+ # @see #date_mode?
81
+ # @see #date_and_time_mode?
82
+ # @see #countdown_mode?
83
+ #
84
+ # @param [String, Hash, Calabash::Query] query A query that can be used
85
+ # to find UIDatePickers.
86
+ #
87
+ # @return [Boolean] True if the picker is in time mode.
88
+ #
89
+ # @raise [RuntimeError] If no picker can be found.
90
+ # @raise [RuntimeError] If an unknown mode is returned.
91
+ # @raise [RuntimeError] If first view matched by query does not responde
92
+ # to 'datePickerMode'.
93
+ def time_mode?(query)
94
+ date_picker_mode(query) == UI_DATE_PICKER_MODE_TIME
95
+ end
96
+
97
+ # Is the date picker in date mode?
98
+ #
99
+ # @see #time_mode?
100
+ # @see #date_mode?
101
+ # @see #date_and_time_mode?
102
+ # @see #countdown_mode?
103
+ #
104
+ # @param [String, Hash, Calabash::Query] query A query that can be used
105
+ # to find UIDatePickers.
106
+ #
107
+ # @return [Boolean] True if the picker is in date mode.
108
+ #
109
+ # @raise [RuntimeError] If no picker can be found.
110
+ # @raise [RuntimeError] If an unknown mode is returned.
111
+ # @raise [RuntimeError] If first view matched by query does not responde
112
+ # to 'datePickerMode'.
113
+ def date_mode?(query)
114
+ date_picker_mode(query) == UI_DATE_PICKER_MODE_DATE
115
+ end
116
+
117
+ # Is the date picker in date and time mode?
118
+ #
119
+ # @see #time_mode?
120
+ # @see #date_mode?
121
+ # @see #date_and_time_mode?
122
+ # @see #countdown_mode?
123
+ #
124
+ # @param [String, Hash, Calabash::Query] query A query that can be used
125
+ # to find UIDatePickers.
126
+ #
127
+ # @return [Boolean] True if the picker is in date and time mode.
128
+ #
129
+ # @raise [RuntimeError] If no picker can be found.
130
+ # @raise [RuntimeError] If an unknown mode is returned.
131
+ # @raise [RuntimeError] If first view matched by query does not responde
132
+ # to 'datePickerMode'.
133
+ def date_and_time_mode?(query)
134
+ date_picker_mode(query) == UI_DATE_PICKER_MODE_DATE_AND_TIME
135
+ end
136
+
137
+ # Is the date picker in countdown mode?
138
+ #
139
+ # @see #time_mode?
140
+ # @see #date_mode?
141
+ # @see #date_and_time_mode?
142
+ # @see #countdown_mode?
143
+ #
144
+ # @param [String, Hash, Calabash::Query] query A query that can be used
145
+ # to find UIDatePickers.
146
+ #
147
+ # @return [Boolean] True if the picker is in countdown mode.
148
+ #
149
+ # @raise [RuntimeError] If no picker can be found.
150
+ # @raise [RuntimeError] If an unknown mode is returned.
151
+ # @raise [RuntimeError] If first view matched by query does not responde
152
+ # to 'datePickerMode'.
153
+ def countdown_mode?(query)
154
+ date_picker_mode(query) == UI_DATE_PICKER_MODE_COUNT_DOWN_TIMER
155
+ end
156
+
157
+ # The maximum date for a picker. If there is no maximum date, this
158
+ # method returns nil.
159
+ #
160
+ # @note
161
+ # From the Apple docs:
162
+ # `The property is an NSDate object or nil (the default)`.
163
+ #
164
+ # @param [String, Hash, Calabash::Query] query A query that can be used
165
+ # to find UIDatePickers.
166
+ #
167
+ # @return [DateTime] The maximum date on the picker or nil if no maximum
168
+ # exists
169
+ #
170
+ # @raise [RuntimeError] If the picker is in countdown mode.
171
+ # @raise [RuntimeError] If no picker can be found.
172
+ # @raise [RuntimeError] If the date returned by the server cannot be
173
+ # converted to a DateTime object.
174
+ def maximum_date_time_from_picker(query)
175
+ Query.ensure_valid_query(query)
176
+
177
+ wait_for_view(query)
178
+
179
+ if countdown_mode?(query)
180
+ fail('Countdown pickers do not have a maximum date.')
181
+ end
182
+
183
+ result = query(query, :maximumDate)
184
+
185
+ if result.empty?
186
+ fail("Expected '#{query}' to return a visible UIDatePicker")
187
+ else
188
+ if result.first.nil?
189
+ nil
190
+ else
191
+ date_str = result.first
192
+ begin
193
+ date_time = DateTime.parse(date_str)
194
+ rescue TypeError, ArgumentError => _
195
+ raise RuntimeError,
196
+ "Could not convert string '#{date_str}' into a valid DateTime object"
197
+ end
198
+ date_time
199
+ end
200
+ end
201
+ end
202
+
203
+ # The minimum date for a picker. If there is no minimum date, this
204
+ # method returns nil.
205
+ #
206
+ # @note
207
+ # From the Apple docs:
208
+ # `The property is an NSDate object or nil (the default)`.
209
+ #
210
+ # @param [String, Hash, Calabash::Query] query A query that can be used
211
+ # to find UIDatePickers.
212
+ #
213
+ # @return [DateTime] The minimum date on the picker or nil if no minimum
214
+ # exists
215
+ #
216
+ # @raise [RuntimeError] If the picker is in countdown mode.
217
+ # @raise [RuntimeError] If no picker can be found.
218
+ # @raise [RuntimeError] If the date returned by the server cannot be
219
+ # converted to a DateTime object.
220
+ def minimum_date_time_from_picker(query)
221
+ Query.ensure_valid_query(query)
222
+
223
+ wait_for_view(query)
224
+
225
+ if countdown_mode?(query)
226
+ fail('Countdown pickers do not have a minimum date.')
227
+ end
228
+
229
+ result = query(query, :minimumDate)
230
+
231
+ if result.empty?
232
+ fail("Expected '#{query}' to return a visible UIDatePicker")
233
+ else
234
+ if result.first.nil?
235
+ nil
236
+ else
237
+ date_str = result.first
238
+ begin
239
+ date_time = DateTime.parse(date_str)
240
+ rescue TypeError, ArgumentError => _
241
+ raise RuntimeError,
242
+ "Could not convert string '#{date_str}' into a valid DateTime object"
243
+ end
244
+ date_time
245
+ end
246
+ end
247
+ end
248
+
249
+ # Returns the date and time from the picker as DateTime object.
250
+ #
251
+ # @param [String, Hash, Calabash::Query] query A query that can be used
252
+ # to find UIDatePickers.
253
+ #
254
+ # @return [DateTime] The date on the picker
255
+ #
256
+ # @raise [RuntimeError] If the picker is in countdown mode.
257
+ # @raise [RuntimeError] If no picker can be found.
258
+ # @raise [RuntimeError] If the date returned by the server cannot be
259
+ # converted to a DateTime object.
260
+ def date_time_from_picker(query)
261
+ Query.ensure_valid_query(query)
262
+
263
+ wait_for_view(query)
264
+
265
+ if countdown_mode?(query)
266
+ fail('This method is available for pickers in countdown mode.')
267
+ end
268
+
269
+ result = query(query, :date)
270
+
271
+ if result.empty?
272
+ fail("Expected '#{query}' to return a visible UIDatePicker")
273
+ else
274
+ if result.first.nil?
275
+ nil
276
+ else
277
+ date_str = result.first
278
+ date_time = DateTime.parse(date_str)
279
+ if date_time.nil?
280
+ raise RuntimeError,
281
+ "Could not convert string '#{date_str}' into a valid DateTime object"
282
+ end
283
+ date_time
284
+ end
285
+ end
286
+ end
287
+
288
+ # Sets the date and time on the _first_ UIDatePicker matched by
289
+ # `query`.
290
+ #
291
+ # This method is not valid for UIDatePickers in _countdown_ mode.
292
+ #
293
+ # @param [DateTime] date_time The date and time you want to change to.
294
+ #
295
+ # @raise [RuntimeError] If `query` does match exactly one picker.
296
+ # @raise [RuntimeError] If `query` matches a picker in countdown mode.
297
+ # @raise [RuntimeError] If the target date is greater than the picker's
298
+ # maximum date.
299
+ # @raise [RuntimeError] If the target date is less than the picker's
300
+ # minimum date
301
+ # @raise [ArgumentError] If the target date is not a DateTime instance.
302
+ def picker_set_date_time(date_time)
303
+ picker_set_date_time_in("UIDatePicker index:0", date_time)
304
+ end
305
+
306
+ # Sets the date and time on the _first_ UIDatePicker matched by
307
+ # `query`.
308
+ #
309
+ # This method is not valid for UIDatePickers in _countdown_ mode.
310
+ #
311
+ # An error will be raised if more than on view is matched by `query`.
312
+ #
313
+ # To avoid matching more than one UIPickerView or subclass:
314
+ # * Make the query more specific: "UIPickerView marked:'alarm'"
315
+ # * Use the index language feature: "UIPickerView index:0"
316
+ # * Query by custom class: "view:'MyPickerView'"
317
+ #
318
+ # @param [String, Hash, Calabash::Query] query A query that can be used
319
+ # to find UIDatePickers.
320
+ # @param [DateTime] date_time The date and time you want to change to.
321
+ #
322
+ # @raise [RuntimeError] If `query` does match exactly one picker.
323
+ # @raise [RuntimeError] If `query` matches a picker in countdown mode.
324
+ # @raise [RuntimeError] If the target date is greater than the picker's
325
+ # maximum date.
326
+ # @raise [RuntimeError] If the target date is less than the picker's
327
+ # minimum date
328
+ # @raise [ArgumentError] If the target date is not a DateTime instance.
329
+ def picker_set_date_time_in(query, date_time)
330
+ unless date_time.is_a?(DateTime)
331
+ raise ArgumentError,
332
+ "Date time argument '#{date_time}' must be a DateTime but found '#{date_time.class}'"
333
+ end
334
+
335
+ Query.ensure_valid_query(query)
336
+
337
+ message = "Timed out waiting for UIDatePicker with '#{query}'"
338
+
339
+ wait_for(message) do
340
+ result = query(query)
341
+ if result.length > 1
342
+ fail("Query '#{query}' matched more than on UIDatePicker")
343
+ else
344
+ !result.empty?
345
+ end
346
+ end
347
+
348
+ if countdown_mode?(query)
349
+ message =
350
+ [
351
+ "Query '#{query}' matched a picker in countdown mode.",
352
+ 'Setting the date or time on a countdown picker is not supported'
353
+ ].join("\n")
354
+ fail(message)
355
+ end
356
+
357
+ minimum_date = minimum_date_time_from_picker(query)
358
+ if !minimum_date.nil? && minimum_date > date_time
359
+ message = [
360
+ "Cannot set the date on the picker matched by '#{query}'",
361
+ "Picker minimum date: '#{minimum_date}'",
362
+ " Date to change to: '#{date_time}'",
363
+ "Target date comes before the minimum date."].join("\n")
364
+ fail(message)
365
+ end
366
+
367
+ maximum_date = maximum_date_time_from_picker(query)
368
+ if !maximum_date.nil? && maximum_date < date_time
369
+ message = [
370
+ "Cannot set the date on the picker matched by '#{query}'",
371
+ "Picker maximum date: '#{maximum_date}'",
372
+ " Date to change to: '#{date_time}'",
373
+ "Target date comes after the maximum date."].join("\n")
374
+ fail(message)
375
+ end
376
+
377
+ ruby_format = date_picker_ruby_date_format
378
+ objc_format = date_picker_objc_date_format
379
+ target_date_string = date_time.strftime(ruby_format).squeeze(' ').strip
380
+
381
+ Device.default.map_route(query,
382
+ :changeDatePickerDate,
383
+ target_date_string,
384
+ objc_format,
385
+ # notify targets
386
+ true,
387
+ # animate
388
+ true)
389
+ end
390
+
391
+ private
392
+
393
+ # @!visibility private
394
+ OBJC_DATE_AND_TIME_FMT = 'yyyy_MM_dd_HH_mm'
395
+
396
+ # @!visibility private
397
+ RUBY_DATE_AND_TIME_FMT = '%Y_%m_%d_%H_%M'
398
+
399
+ # UIDatePicker modes
400
+
401
+ # @!visibility private
402
+ UI_DATE_PICKER_MODE_TIME = 0
403
+ # @!visibility private
404
+ UI_DATE_PICKER_MODE_DATE = 1
405
+ # @!visibility private
406
+ UI_DATE_PICKER_MODE_DATE_AND_TIME = 2
407
+ # @!visibility private
408
+ UI_DATE_PICKER_MODE_COUNT_DOWN_TIMER = 3
409
+ end
410
+ end
411
+ end
412
+
@@ -1,5 +1,6 @@
1
1
  module Calabash
2
2
  module IOS
3
+ # Runtime defaults for iOS.
3
4
  module Defaults
4
5
  # Sets up the default application based on the
5
6
  # environment, and the default device based on the default application
@@ -19,7 +19,7 @@ module Calabash
19
19
  include Calabash::IOS::UIAKeyboardMixin
20
20
  include Calabash::IOS::TextMixin
21
21
  include Calabash::IOS::UIAMixin
22
-
22
+ include Calabash::IOS::IPadMixin
23
23
  include Calabash::IOS::GesturesMixin
24
24
 
25
25
  attr_reader :run_loop
@@ -339,6 +339,10 @@ module Calabash
339
339
  else
340
340
  raise "Invalid application #{application} for iOS platform."
341
341
  end
342
+
343
+ # @todo Get the language code from the server!
344
+ ensure_ipad_emulation_1x
345
+
342
346
  {
343
347
  :device => self,
344
348
  :application => application,
@@ -0,0 +1,253 @@
1
+ module Calabash
2
+ module IOS
3
+ # @!visibility private
4
+ # Contains methods for interacting with the iPad.
5
+ module IPadMixin
6
+
7
+ # @!visibility private
8
+ # Provides methods to interact with the 1x and 2x buttons that appear
9
+ # when an iPhone-only app is emulated on an iPad. Calabash cannot
10
+ # interact with these apps in 2x mode because the touch coordinates
11
+ # cannot be reliably translated from normal iPhone dimensions to the
12
+ # emulated dimensions.
13
+ #
14
+ # On iOS < 7, an app _remembered_ its last 1x/2x scale so when it
15
+ # reopened the previous scale would be the same as when it closed. This
16
+ # meant you could manually set the scale once to 1x and never have to
17
+ # interact with the scale button again.
18
+ #
19
+ # On iOS > 7, the default behavior is that all emulated apps open at 2x
20
+ # regardless of their previous scale.
21
+ #
22
+ # @note In order to use this class, you must allow Calabash to launch
23
+ # your app with instruments.
24
+ class Emulation
25
+
26
+ # @!visibility private
27
+ #
28
+ # Maintainers: when adding a localization, please notice that
29
+ # the keys and values are semantically reversed.
30
+ #
31
+ # you should read the hash as:
32
+ #
33
+ # ```
34
+ # :emulated_1x <= what button is showing when the app is emulated at 2X?
35
+ # :emulated_2x <= what button is showing when the app is emulated at 1X?
36
+ # ```
37
+ #
38
+ # @todo Once we have localizations wired up, we can use these keys
39
+ #
40
+ # These pull requests describe how to find a key/value pairs from the
41
+ # on-disk accessibility bundles for _keyboards_ but we can use the same
42
+ # strategy to look up the Zoom button localizations.
43
+ #
44
+ # * https://github.com/calabash/run_loop/pull/197
45
+ # * https://github.com/calabash/calabash-ios-server/pull/221
46
+ #
47
+ #
48
+ # fullscreen.zoom => 2x => 'Switch to full screen mode'
49
+ # normal.zoom => 1x => 'Switch to normal mode'
50
+ #
51
+ # # We will still need query windows.
52
+ # uia("UIATarget.localTarget().frontMostApp().windows()[2].elements()[0].label()")
53
+ IPAD_1X_2X_BUTTON_LABELS = {
54
+ :en => {:emulated_1x => '2X',
55
+ :emulated_2x => '1X'}
56
+ }
57
+
58
+ # @!visibility private
59
+ # @!attribute [r] scale
60
+ # The current 1X or 2X scale represented as a Symbol.
61
+ #
62
+ # @return [Symbol] Returns this emulation's scale. Will be one of
63
+ # `{:emulated_1x | :emulated_2x}`.
64
+ attr_reader :scale
65
+
66
+ # @!visibility private
67
+ # @!attribute [r] lang_code
68
+ # The Apple compatible language code for determining the accessibility
69
+ # label of the 1X and 2X buttons.
70
+ #
71
+ # @return [Symbol] Returns the language code of this emulation.
72
+ attr_reader :lang_code
73
+
74
+ # @!visibility private
75
+ # @!attribute [r] device
76
+ # A handle on the default device.
77
+ attr_reader :device
78
+
79
+ # @!visibility private
80
+ # A private instance variable for storing this emulation's 1X/2X button
81
+ # names. The value will be set at runtime based on the language code
82
+ # that is passed the initializer.
83
+ @button_names_hash = nil
84
+
85
+ # @!visibility private
86
+ # Creates a new Emulation.
87
+ # @param [Symbol] lang_code an Apple compatible language code
88
+ # @return [Emulation] Returns an emulation that is ready for action!
89
+ def initialize (device, lang_code=:en)
90
+ @button_names_hash = IPAD_1X_2X_BUTTON_LABELS[lang_code]
91
+ if @button_names_hash.nil?
92
+ raise "could not find 1X/2X buttons for language code '#{lang_code}'"
93
+ end
94
+
95
+ @device = device
96
+ @lang_code = lang_code
97
+ @scale = _internal_ipad_emulation_scale
98
+ end
99
+
100
+ # @!visibility private
101
+ def tap_ipad_scale_button
102
+ key = @scale
103
+ name = @button_names_hash[key]
104
+
105
+ query_args =
106
+ [
107
+ [
108
+ :view, {:marked => "#{name}"}
109
+ ]
110
+ ]
111
+
112
+ device.uia_query_then_make_javascript_calls(:queryElWindows, query_args, :tap)
113
+ end
114
+
115
+ private
116
+
117
+ # @!visibility private
118
+ def _internal_ipad_emulation_scale
119
+ hash = @button_names_hash
120
+ val = nil
121
+ hash.values.each do |button_name|
122
+ query_args =
123
+ [
124
+ :view, {:marked => button_name}
125
+ ]
126
+
127
+ button_exists = device.uia_serialize_and_call(:queryElWindows,
128
+ query_args)
129
+ if button_exists
130
+ result = device.uia_query_then_make_javascript_calls(:queryElWindows,
131
+ [query_args],
132
+ :name)
133
+ if result == button_name
134
+ val = button_name
135
+ break
136
+ end
137
+ end
138
+ end
139
+
140
+ if val.nil?
141
+ raise "Could not find iPad scale button with '#{hash.values}'"
142
+ end
143
+
144
+ if val == hash[:emulated_1x]
145
+ :emulated_1x
146
+ elsif val == hash[:emulated_2x]
147
+ :emulated_2x
148
+ else
149
+ raise "Unrecognized emulation scale '#{val}'"
150
+ end
151
+ end
152
+ end
153
+
154
+ # @!visibility private
155
+ # Ensures that iPhone apps emulated on an iPad are displayed at scale.
156
+ #
157
+ # @note It is recommended that clients call this `ensure_ipad_emulation_1x`
158
+ # instead of this method.
159
+ #
160
+ # @note If this is not an iPhone app emulated on an iPad, then calling
161
+ # this method has no effect.
162
+ #
163
+ # @note In order to use this method, you must allow Calabash to launch
164
+ # your app with instruments.
165
+ #
166
+ # Starting in iOS 7, iPhone apps emulated on the iPad always launch at 2x.
167
+ # calabash cannot currently interact with such apps in 2x mode (trust us,
168
+ # we've tried).
169
+ #
170
+ # @see #ensure_ipad_emulation_1x
171
+ #
172
+ # @param [Symbol] scale the desired scale - must be `:emulated_1x` or
173
+ # `:emulated_2x`
174
+ #
175
+ # @param [Hash] opts optional arguments to control the interaction with
176
+ # the 1X/2X buttons
177
+ #
178
+ # @option opts [Symbol] :lang_code (:en) an Apple compatible
179
+ # language code
180
+ # @option opts [Symbol] :wait_after_touch (0.4) how long to
181
+ # wait _after_ the scale button is touched
182
+ #
183
+ # @return [void]
184
+ #
185
+ # @raise [RuntimeError] If the app was not launched with instruments.
186
+ # @raise [RuntimeError] If an invalid `scale` is passed.
187
+ # @raise [RuntimeError] If an unknown language code is passed.
188
+ # @raise [RuntimeError] If the scale button cannot be touched.
189
+ def ensure_ipad_emulation_scale(scale, opts={})
190
+ return unless iphone_app_emulated_on_ipad?
191
+
192
+ allowed = [:emulated_1x, :emulated_2x]
193
+ unless allowed.include?(scale)
194
+ raise "Scale '#{scale}' is not one of '#{allowed}' allowed args"
195
+ end
196
+
197
+ default_opts = {:lang_code => :en,
198
+ :wait_after_touch => 0.4}
199
+ merged_opts = default_opts.merge(opts)
200
+
201
+ obj = Emulation.new(self, merged_opts[:lang_code])
202
+
203
+ actual_scale = obj.scale
204
+
205
+ if actual_scale != scale
206
+ obj.tap_ipad_scale_button
207
+ end
208
+
209
+ sleep(merged_opts[:wait_after_touch])
210
+ end
211
+
212
+ # @!visibility private
213
+ # Ensures that iPhone apps emulated on an iPad are displayed at `1X`.
214
+ #
215
+ # @note If this is not an iPhone app emulated on an iPad, then calling
216
+ # this method has no effect.
217
+ #
218
+ # @note In order to use this method, you must allow Calabash to launch
219
+ # your app with instruments.
220
+ #
221
+ # Starting in iOS 7, iPhone apps emulated on the iPad always launch at 2x.
222
+ # calabash cannot currently interact with such apps in 2x mode (trust us,
223
+ # we've tried).
224
+ #
225
+ # @param [Hash] opts optional arguments to control the interaction with
226
+ # the 1X/2X buttons
227
+ #
228
+ # @option opts [Symbol] :lang_code (:en) an Apple compatible
229
+ # language code
230
+ # @option opts [Symbol] :wait_after_touch (0.4) how long to
231
+ # wait _after_ the scale button is touched
232
+ #
233
+ # @return [void]
234
+ #
235
+ # @raise [RuntimeError] If the app was not launched with instruments.
236
+ # @raise [RuntimeError] If an unknown language code is passed.
237
+ # @raise [RuntimeError] If the scale button cannot be touched.
238
+ def ensure_ipad_emulation_1x(opts={})
239
+ ensure_ipad_emulation_scale(:emulated_1x, opts)
240
+ end
241
+
242
+ private
243
+ # @!visibility private
244
+ # Ensures iPhone apps running on an iPad are emulated at 2X
245
+ #
246
+ # You should never need to call this function - Calabash cannot interact
247
+ # with iPhone apps emulated on the iPad in 2x mode.
248
+ def _ensure_ipad_emulation_2x(opts={})
249
+ ensure_ipad_emulation_scale(:emulated_2x, opts)
250
+ end
251
+ end
252
+ end
253
+ end