calabash 2.0.0.pre2 → 2.0.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
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