calabash 1.9.9.pre3 → 2.0.0.prelegacy

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -33
  3. data/bin/calabash +45 -36
  4. data/lib/calabash.rb +137 -13
  5. data/lib/calabash/android.rb +6 -0
  6. data/lib/calabash/android/adb.rb +25 -1
  7. data/lib/calabash/android/application.rb +14 -3
  8. data/lib/calabash/android/build/builder.rb +16 -3
  9. data/lib/calabash/android/build/java_keystore.rb +10 -0
  10. data/lib/calabash/android/build/resigner.rb +23 -1
  11. data/lib/calabash/android/build/test_server.rb +2 -0
  12. data/lib/calabash/android/defaults.rb +1 -0
  13. data/lib/calabash/android/device.rb +55 -3
  14. data/lib/calabash/android/environment.rb +10 -0
  15. data/lib/calabash/android/interactions.rb +106 -3
  16. data/lib/calabash/android/legacy.rb +143 -0
  17. data/lib/calabash/android/lib/TestServer.apk +0 -0
  18. data/lib/calabash/android/life_cycle.rb +6 -4
  19. data/lib/calabash/android/physical_buttons.rb +12 -1
  20. data/lib/calabash/android/screenshot.rb +1 -0
  21. data/lib/calabash/android/scroll.rb +115 -0
  22. data/lib/calabash/android/server.rb +3 -1
  23. data/lib/calabash/android/text.rb +16 -25
  24. data/lib/calabash/application.rb +29 -0
  25. data/lib/calabash/cli/build.rb +15 -1
  26. data/lib/calabash/cli/console.rb +9 -5
  27. data/lib/calabash/cli/generate.rb +5 -0
  28. data/lib/calabash/cli/helpers.rb +7 -1
  29. data/lib/calabash/cli/resign.rb +1 -0
  30. data/lib/calabash/cli/run.rb +10 -6
  31. data/lib/calabash/cli/setup_keystore.rb +2 -0
  32. data/lib/calabash/color.rb +7 -0
  33. data/lib/calabash/console_helpers.rb +4 -2
  34. data/lib/calabash/defaults.rb +1 -0
  35. data/lib/calabash/device.rb +8 -9
  36. data/lib/calabash/environment.rb +5 -0
  37. data/lib/calabash/gestures.rb +159 -66
  38. data/lib/calabash/http/retriable_client.rb +3 -1
  39. data/lib/calabash/interactions.rb +68 -5
  40. data/lib/calabash/ios.rb +4 -0
  41. data/lib/calabash/ios/application.rb +8 -1
  42. data/lib/calabash/ios/conditions.rb +3 -1
  43. data/lib/calabash/ios/date_picker.rb +412 -0
  44. data/lib/calabash/ios/defaults.rb +1 -0
  45. data/lib/calabash/ios/device.rb +1 -0
  46. data/lib/calabash/ios/device/device_implementation.rb +33 -16
  47. data/lib/calabash/ios/device/gestures_mixin.rb +202 -48
  48. data/lib/calabash/ios/device/ipad_1x_2x_mixin.rb +253 -0
  49. data/lib/calabash/ios/device/keyboard_mixin.rb +2 -0
  50. data/lib/calabash/ios/device/rotation_mixin.rb +11 -8
  51. data/lib/calabash/ios/device/routes/condition_route_mixin.rb +1 -0
  52. data/lib/calabash/ios/device/routes/handle_route_mixin.rb +5 -1
  53. data/lib/calabash/ios/device/routes/map_route_mixin.rb +1 -0
  54. data/lib/calabash/ios/device/routes/response_parser.rb +1 -0
  55. data/lib/calabash/ios/device/routes/uia_route_mixin.rb +44 -6
  56. data/lib/calabash/ios/device/runtime_attributes.rb +4 -5
  57. data/lib/calabash/ios/device/text_mixin.rb +2 -0
  58. data/lib/calabash/ios/device/uia_keyboard_mixin.rb +9 -0
  59. data/lib/calabash/ios/device/uia_mixin.rb +1 -0
  60. data/lib/calabash/ios/gestures.rb +82 -8
  61. data/lib/calabash/ios/interactions.rb +30 -1
  62. data/lib/calabash/ios/orientation.rb +21 -21
  63. data/lib/calabash/ios/runtime.rb +154 -2
  64. data/lib/calabash/ios/slider.rb +70 -0
  65. data/lib/calabash/ios/text.rb +11 -47
  66. data/lib/calabash/ios/uia.rb +24 -2
  67. data/lib/calabash/legacy.rb +7 -0
  68. data/lib/calabash/lib/skeleton/config/cucumber.yml +1 -3
  69. data/lib/calabash/lib/skeleton/features/support/dry_run.rb +8 -0
  70. data/lib/calabash/lib/skeleton/features/support/env.rb +15 -1
  71. data/lib/calabash/life_cycle.rb +78 -32
  72. data/lib/calabash/location.rb +2 -1
  73. data/lib/calabash/orientation.rb +0 -1
  74. data/lib/calabash/page.rb +51 -5
  75. data/lib/calabash/patch.rb +1 -0
  76. data/lib/calabash/patch/array.rb +7 -7
  77. data/lib/calabash/query.rb +17 -2
  78. data/lib/calabash/query_result.rb +14 -0
  79. data/lib/calabash/screenshot.rb +28 -8
  80. data/lib/calabash/text.rb +105 -8
  81. data/lib/calabash/utility.rb +6 -6
  82. data/lib/calabash/version.rb +1 -1
  83. data/lib/calabash/wait.rb +37 -11
  84. metadata +14 -7
@@ -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
@@ -59,10 +60,10 @@ module Calabash
59
60
  end
60
61
 
61
62
  # @!visibility private
62
- # Caller must pass position one of these positions down, left, right, up
63
+ # Caller must pass position one of these positions :down, :left, :right, :up
63
64
  def rotate_home_button_to(position)
64
65
 
65
- valid_positions = ['down', 'left', 'right', 'up']
66
+ valid_positions = [:down, :left, :right, :up]
66
67
  unless valid_positions.include?(position)
67
68
  raise ArgumentError,
68
69
  "Expected '#{position}' to be on of #{valid_positions.join(', ')}"
@@ -76,7 +77,7 @@ module Calabash
76
77
  wait_for_server_to_start({:timeout => 1})
77
78
  end
78
79
 
79
- orientation = status_bar_orientation
80
+ orientation = status_bar_orientation.to_sym
80
81
 
81
82
  if orientation == position
82
83
  return orientation
@@ -94,16 +95,18 @@ module Calabash
94
95
  playback_route(recording_name, form_factor)
95
96
 
96
97
  # Wait for rotation animation.
97
- timeout = 1.0
98
- condition_route('NONE_ANIMATING', timeout, '*')
98
+ #
99
+ # Can't wait for animations because there might be animations other
100
+ # than rotation on the screen.
101
+ sleep(0.4)
99
102
 
100
- orientation = status_bar_orientation
103
+ orientation = status_bar_orientation.to_sym
101
104
  if orientation == position
102
- return orientation
105
+ return orientation.to_s
103
106
  end
104
107
  end
105
108
 
106
- orientation
109
+ orientation.to_s
107
110
  end
108
111
 
109
112
  private
@@ -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)
@@ -9,7 +9,11 @@ module Calabash
9
9
 
10
10
  def route_post_request(request)
11
11
  begin
12
- http_client.post(request)
12
+ if request.params[/\"method_name\":\"flash\"/, 0]
13
+ http_client.post(request, timeout: 30)
14
+ else
15
+ http_client.post(request)
16
+ end
13
17
  rescue => e
14
18
  raise Calabash::IOS::RouteError, e
15
19
  end
@@ -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")
@@ -8,6 +8,10 @@ module Calabash
8
8
 
9
9
  require 'run_loop'
10
10
 
11
+ # @!visibility private
12
+ # The hash passed to initialize.
13
+ attr_reader :runtime_info
14
+
11
15
  # @!visibility private
12
16
  # Creates a new instance of DeviceRuntimeInfo.
13
17
  # @param [Hash] runtime_info The result of calling the version route on
@@ -173,11 +177,6 @@ module Calabash
173
177
  runtime_info['system']
174
178
  end.call
175
179
  end
176
-
177
- # @!visibility private
178
- # The hash passed to initialize.
179
- attr_reader :runtime_info
180
-
181
180
  end
182
181
  end
183
182
  end