calabash 2.0.0.pre1 → 2.0.0.prelegacy2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -38
  3. data/lib/calabash.rb +18 -5
  4. data/lib/calabash/android.rb +3 -0
  5. data/lib/calabash/android/adb.rb +25 -1
  6. data/lib/calabash/android/application.rb +14 -3
  7. data/lib/calabash/android/build/builder.rb +15 -2
  8. data/lib/calabash/android/build/java_keystore.rb +10 -0
  9. data/lib/calabash/android/build/resigner.rb +4 -0
  10. data/lib/calabash/android/build/test_server.rb +2 -0
  11. data/lib/calabash/android/defaults.rb +1 -0
  12. data/lib/calabash/android/device.rb +42 -1
  13. data/lib/calabash/android/environment.rb +10 -0
  14. data/lib/calabash/android/interactions.rb +2 -0
  15. data/lib/calabash/android/legacy.rb +149 -0
  16. data/lib/calabash/android/lib/TestServer.apk +0 -0
  17. data/lib/calabash/android/life_cycle.rb +1 -0
  18. data/lib/calabash/android/physical_buttons.rb +8 -0
  19. data/lib/calabash/android/screenshot.rb +1 -0
  20. data/lib/calabash/android/scroll.rb +110 -0
  21. data/lib/calabash/android/server.rb +3 -1
  22. data/lib/calabash/android/text.rb +6 -0
  23. data/lib/calabash/application.rb +29 -0
  24. data/lib/calabash/cli/build.rb +15 -1
  25. data/lib/calabash/cli/console.rb +9 -5
  26. data/lib/calabash/cli/generate.rb +3 -0
  27. data/lib/calabash/cli/helpers.rb +7 -1
  28. data/lib/calabash/cli/resign.rb +1 -0
  29. data/lib/calabash/cli/run.rb +10 -6
  30. data/lib/calabash/cli/setup_keystore.rb +2 -0
  31. data/lib/calabash/color.rb +7 -0
  32. data/lib/calabash/defaults.rb +1 -0
  33. data/lib/calabash/device.rb +7 -0
  34. data/lib/calabash/environment.rb +1 -0
  35. data/lib/calabash/http/retriable_client.rb +2 -0
  36. data/lib/calabash/interactions.rb +1 -0
  37. data/lib/calabash/ios.rb +2 -0
  38. data/lib/calabash/ios/application.rb +8 -1
  39. data/lib/calabash/ios/conditions.rb +3 -0
  40. data/lib/calabash/ios/date_picker.rb +412 -0
  41. data/lib/calabash/ios/defaults.rb +1 -0
  42. data/lib/calabash/ios/device.rb +1 -0
  43. data/lib/calabash/ios/device/device_implementation.rb +16 -11
  44. data/lib/calabash/ios/device/ipad_1x_2x_mixin.rb +253 -0
  45. data/lib/calabash/ios/device/keyboard_mixin.rb +2 -0
  46. data/lib/calabash/ios/device/rotation_mixin.rb +1 -0
  47. data/lib/calabash/ios/device/routes/condition_route_mixin.rb +1 -0
  48. data/lib/calabash/ios/device/routes/map_route_mixin.rb +1 -0
  49. data/lib/calabash/ios/device/routes/response_parser.rb +1 -0
  50. data/lib/calabash/ios/device/routes/uia_route_mixin.rb +44 -6
  51. data/lib/calabash/ios/device/text_mixin.rb +2 -0
  52. data/lib/calabash/ios/device/uia_keyboard_mixin.rb +9 -0
  53. data/lib/calabash/ios/device/uia_mixin.rb +1 -0
  54. data/lib/calabash/ios/interactions.rb +30 -1
  55. data/lib/calabash/ios/runtime.rb +8 -0
  56. data/lib/calabash/ios/text.rb +5 -45
  57. data/lib/calabash/legacy.rb +7 -0
  58. data/lib/calabash/lib/skeleton/config/cucumber.yml +1 -3
  59. data/lib/calabash/lib/skeleton/features/support/env.rb +15 -1
  60. data/lib/calabash/life_cycle.rb +19 -2
  61. data/lib/calabash/location.rb +2 -0
  62. data/lib/calabash/page.rb +13 -0
  63. data/lib/calabash/patch.rb +1 -0
  64. data/lib/calabash/query_result.rb +4 -0
  65. data/lib/calabash/text.rb +53 -0
  66. data/lib/calabash/utility.rb +4 -4
  67. data/lib/calabash/version.rb +1 -1
  68. data/lib/calabash/wait.rb +4 -0
  69. metadata +119 -115
@@ -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
@@ -18,6 +18,7 @@ module Calabash
18
18
  require 'calabash/ios/device/uia_keyboard_mixin'
19
19
  require 'calabash/ios/device/text_mixin'
20
20
  require 'calabash/ios/device/uia_mixin'
21
+ require 'calabash/ios/device/ipad_1x_2x_mixin'
21
22
  require 'calabash/ios/device/device_implementation'
22
23
 
23
24
  end
@@ -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
@@ -78,7 +78,7 @@ module Calabash
78
78
  identifier = Environment::DEVICE_IDENTIFIER
79
79
 
80
80
  if identifier.nil?
81
- connected_devices = RunLoop::XCTools.new.instruments(:devices)
81
+ connected_devices = RunLoop::Instruments.new.physical_devices
82
82
  if connected_devices.empty?
83
83
  raise 'There are no physical devices connected.'
84
84
  elsif connected_devices.count > 1
@@ -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,
@@ -367,7 +371,7 @@ module Calabash
367
371
 
368
372
  expect_app_installed_on_simulator(bridge)
369
373
 
370
- installed_app = Calabash::IOS::Application.new(bridge.fetch_app_dir)
374
+ installed_app = Calabash::IOS::Application.new(bridge.send(:installed_app_bundle_dir))
371
375
  expect_matching_sha1s(installed_app, application)
372
376
  end
373
377
 
@@ -468,7 +472,7 @@ module Calabash
468
472
  bridge = run_loop_bridge(@run_loop_device, application)
469
473
 
470
474
  if bridge.app_is_installed?
471
- installed_app = Calabash::IOS::Application.new(bridge.fetch_app_dir)
475
+ installed_app = Calabash::IOS::Application.new(bridge.send(:installed_app_bundle_dir))
472
476
 
473
477
  if installed_app.same_sha1_as?(application)
474
478
  true
@@ -571,9 +575,9 @@ module Calabash
571
575
  # @!visibility private
572
576
  def uninstall_app_on_simulator(application, run_loop_device, bridge)
573
577
  begin
574
- bridge.uninstall
578
+ bridge.uninstall_app_and_sandbox
575
579
  true
576
- rescue e
580
+ rescue => e
577
581
  raise "Could not uninstall #{application.identifier} on #{run_loop_device}: #{e}"
578
582
  end
579
583
  end
@@ -599,10 +603,11 @@ module Calabash
599
603
  end
600
604
 
601
605
  # @!visibility private
602
- # Do not memoize this. The Bridge initializer does a bunch of work to
606
+ # Do not memoize this. The CoreSimulator initializer does a bunch of work to
603
607
  # prepare the environment for simctl actions.
604
608
  def run_loop_bridge(run_loop_simulator_device, application)
605
- RunLoop::Simctl::Bridge.new(run_loop_simulator_device, application.path)
609
+ run_loop_app = RunLoop::App.new(application.path)
610
+ RunLoop::CoreSimulator.new(run_loop_simulator_device, run_loop_app)
606
611
  end
607
612
 
608
613
  # @!visibility private
@@ -615,7 +620,7 @@ module Calabash
615
620
  bridge = run_loop_bridge
616
621
  end
617
622
 
618
- bridge.uninstall
623
+ bridge.uninstall_app_and_sandbox
619
624
  bridge.install
620
625
  rescue StandardError => e
621
626
  raise "Could not install #{application} on #{run_loop_device}: #{e}"
@@ -635,8 +640,8 @@ module Calabash
635
640
  # @!visibility private
636
641
  # Very expensive!
637
642
  def Device.fetch_matching_physical_device(udid_or_name)
638
- xctools = RunLoop::XCTools.new
639
- xctools.instruments(:devices).detect do |device|
643
+ instruments = RunLoop::Instruments.new
644
+ instruments.physical_devices.detect do |device|
640
645
  device.name == udid_or_name ||
641
646
  device.udid == udid_or_name
642
647
  end
@@ -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
@@ -110,6 +110,7 @@ module Calabash
110
110
  end.call(self)
111
111
  end
112
112
 
113
+ # @!visibility private
113
114
  def query_for_keyboard
114
115
  keyboard_waiter.query(KEYBOARD_QUERY)
115
116
  end
@@ -120,6 +121,7 @@ module Calabash
120
121
  keyboard_waiter.query(KEYBOARD_KEY_QUERY)
121
122
  end
122
123
 
124
+ # @!visibility private
123
125
  def query_for_text_of_first_responder(query)
124
126
  result = keyboard_waiter.query("#{query} isFirstResponder:1", :text)
125
127
  if result.empty?
@@ -4,6 +4,7 @@ module Calabash
4
4
  # @!visibility private
5
5
  module RotationMixin
6
6
 
7
+ # @!visibility private
7
8
  def rotate(direction)
8
9
  # If we are in the console, we want to be able to rotate without
9
10
  # calling start_app. However, if the Device in the console has not
@@ -5,6 +5,7 @@ module Calabash
5
5
  # @!visibility private
6
6
  module ConditionRouteMixin
7
7
 
8
+ # @!visibility private
8
9
  def condition_route(condition, timeout, query)
9
10
  request = make_condition_request(condition, timeout, query)
10
11
  response = route_post_request(request)
@@ -5,6 +5,7 @@ module Calabash
5
5
  # @!visibility private
6
6
  module MapRouteMixin
7
7
 
8
+ # @!visibility private
8
9
  def map_route(query, method_name, *method_args)
9
10
  request = make_map_request(query, method_name, *method_args)
10
11
  response = route_post_request(request)
@@ -6,6 +6,7 @@ module Calabash
6
6
  # @!visibility private
7
7
  module ResponseParser
8
8
 
9
+ # @!visibility private
9
10
  def parse_response_body(response)
10
11
  body = response.body
11
12
  begin
@@ -9,6 +9,7 @@ module Calabash
9
9
  require 'run_loop'
10
10
  require 'edn'
11
11
 
12
+ # @!visibility private
12
13
  def uia_route(command)
13
14
  unless run_loop
14
15
  if defined?(IRB)
@@ -49,6 +50,49 @@ module Calabash
49
50
  end
50
51
  end
51
52
 
53
+ # @!visibility private
54
+ def uia_serialize_and_call(uia_command, *query_args)
55
+ command = uia_serialize_command(uia_command, *query_args)
56
+ result = uia_route(command)
57
+ result.first
58
+ end
59
+
60
+ # @!visibility private
61
+ # @todo Extract argument consing and unit test
62
+ def uia_query_then_make_javascript_calls(uia_command, query_parts, *javascript_parts)
63
+ if javascript_parts.empty?
64
+ uia_serialize_and_call(uia_command, *query_parts)
65
+ else
66
+ javascript_command = uia_serialize_command(uia_command, *query_parts)
67
+
68
+ javascript_args = []
69
+ javascript_parts.each do |invocation|
70
+ javascript_args << case invocation
71
+ when Symbol
72
+ "#{invocation}()"
73
+ when Hash
74
+ method = invocation.keys.first
75
+ method_args = invocation[method]
76
+
77
+ if method_args.is_a?(Array)
78
+ serialized_args = (method_args.map &:to_json).join(',')
79
+ else
80
+ serialized_args = method_args.to_json
81
+ end
82
+
83
+ "#{method}(#{serialized_args})"
84
+ else
85
+ raise Calabash::IOS::RouteError,
86
+ "Invalid invocation spec #{invocation}"
87
+ end
88
+ end
89
+ command = "#{javascript_command}.#{javascript_args.join('.')}"
90
+
91
+ result = uia_route(command)
92
+ result.first
93
+ end
94
+ end
95
+
52
96
  private
53
97
 
54
98
  UIA_STRATEGIES = [:preferences, :host, :shared_element]
@@ -159,12 +203,6 @@ module Calabash
159
203
  end
160
204
  end
161
205
 
162
- def uia_serialize_and_call(uia_command, *query_args)
163
- command = uia_serialize_command(uia_command, *query_args)
164
- result = uia_route(command)
165
- result.first
166
- end
167
-
168
206
  # @todo Verify this is the correct way to escape '\n in string
169
207
  def uia_escape_string(string)
170
208
  Calabash::Text.escape_single_quotes(string).gsub("\n", "\\\\n")