calabash-cucumber 0.19.2 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/dylibs/libCalabashDyn.dylib +0 -0
  3. data/dylibs/libCalabashDynSim.dylib +0 -0
  4. data/lib/calabash-cucumber.rb +9 -2
  5. data/lib/calabash-cucumber/abstract.rb +23 -0
  6. data/lib/calabash-cucumber/automator/automator.rb +158 -0
  7. data/lib/calabash-cucumber/automator/coordinates.rb +401 -0
  8. data/lib/calabash-cucumber/automator/device_agent.rb +424 -0
  9. data/lib/calabash-cucumber/automator/instruments.rb +441 -0
  10. data/lib/calabash-cucumber/connection_helpers.rb +1 -0
  11. data/lib/calabash-cucumber/core.rb +632 -138
  12. data/lib/calabash-cucumber/device_agent.rb +346 -0
  13. data/lib/calabash-cucumber/dot_dir.rb +1 -0
  14. data/lib/calabash-cucumber/environment.rb +1 -0
  15. data/lib/calabash-cucumber/environment_helpers.rb +4 -3
  16. data/lib/calabash-cucumber/http/http.rb +6 -4
  17. data/lib/calabash-cucumber/keyboard_helpers.rb +97 -679
  18. data/lib/calabash-cucumber/launcher.rb +107 -31
  19. data/lib/calabash-cucumber/log_tailer.rb +46 -0
  20. data/lib/calabash-cucumber/map.rb +7 -1
  21. data/lib/calabash-cucumber/rotation_helpers.rb +47 -139
  22. data/lib/calabash-cucumber/status_bar_helpers.rb +51 -20
  23. data/lib/calabash-cucumber/store/preferences.rb +3 -0
  24. data/lib/calabash-cucumber/uia.rb +333 -2
  25. data/lib/calabash-cucumber/usage_tracker.rb +2 -0
  26. data/lib/calabash-cucumber/version.rb +2 -2
  27. data/lib/calabash-cucumber/wait_helpers.rb +2 -0
  28. data/lib/calabash/formatters/html.rb +6 -1
  29. data/lib/frank-calabash.rb +10 -4
  30. data/scripts/.irbrc +3 -0
  31. data/staticlib/calabash.framework.zip +0 -0
  32. data/staticlib/libFrankCalabash.a +0 -0
  33. metadata +11 -6
  34. data/lib/calabash-cucumber/actions/instruments_actions.rb +0 -155
@@ -39,7 +39,9 @@ module Calabash
39
39
  class Launcher
40
40
 
41
41
  require "calabash-cucumber/device"
42
- require "calabash-cucumber/actions/instruments_actions"
42
+ require "calabash-cucumber/automator/automator"
43
+ require "calabash-cucumber/automator/instruments"
44
+ require "calabash-cucumber/automator/device_agent"
43
45
  require "calabash-cucumber/usage_tracker"
44
46
  require "calabash-cucumber/dylibs"
45
47
  require "calabash-cucumber/environment"
@@ -58,10 +60,10 @@ module Calabash
58
60
  @@launcher = nil
59
61
 
60
62
  # @!visibility private
61
- attr_accessor :run_loop
63
+ attr_reader :run_loop
62
64
 
63
65
  # @!visibility private
64
- attr_accessor :actions
66
+ attr_reader :automator
65
67
 
66
68
  # @!visibility private
67
69
  attr_accessor :launch_args
@@ -76,15 +78,26 @@ module Calabash
76
78
 
77
79
  # @!visibility private
78
80
  def to_s
79
- msg = ["#{self.class}"]
80
- if self.run_loop
81
- msg << "Log file: #{self.run_loop[:log_file]}"
81
+ class_name = "Launcher"
82
+
83
+ if !automator
84
+ "#<#{class_name}: not attached to an automator>"
82
85
  else
83
- msg << "Not attached to instruments."
84
- msg << "Start your app with `start_test_server_in_background`"
85
- msg << "If your app is already running, try `console_attach`"
86
+ if automator.respond_to?(:name)
87
+ case automator.name
88
+ when :instruments
89
+ log_file = automator.run_loop[:log_file]
90
+ "#<#{class_name}: UIAutomation/instruments - #{log_file}>"
91
+ when :device_agent
92
+ launcher_name = automator.client.cbx_launcher.name
93
+ "#<#{class_name}: DeviceAgent/#{launcher_name}>"
94
+ else
95
+ "#<#{class_name}: attached to #{automator.name}>"
96
+ end
97
+ else
98
+ "#<#{class_name}: attached to #{automator}>"
99
+ end
86
100
  end
87
- msg.join("\n")
88
101
  end
89
102
 
90
103
  # @!visibility private
@@ -138,17 +151,11 @@ module Calabash
138
151
  @usage_tracker ||= Calabash::Cucumber::UsageTracker.new
139
152
  end
140
153
 
141
- # @!visibility private
142
- def actions
143
- attach if @actions.nil?
144
- @actions
145
- end
146
-
147
154
  # @!visibility private
148
155
  # @see Calabash::Cucumber::Core#console_attach
149
156
  def self.attach
150
157
  l = launcher
151
- return l if l && l.active?
158
+ return l if l && l.attached_to_automator?
152
159
  l.attach
153
160
  end
154
161
 
@@ -163,7 +170,19 @@ module Calabash
163
170
  :http_connection_timeout => 10}
164
171
  merged_options = default_options.merge(options)
165
172
 
166
- self.run_loop = RunLoop::HostCache.default.read
173
+ @run_loop = RunLoop::HostCache.default.read
174
+
175
+ if @run_loop[:automator] == :device_agent
176
+ # TODO Attach to DeviceAgent - run-loop supports this!
177
+ # TODO Rewrite UIA methods to raise in the context of UIA
178
+ raise RuntimeError, %Q[
179
+
180
+ Cannot attach to DeviceAgent automator.
181
+
182
+ This behavior is not implemented yet.
183
+
184
+ ]
185
+ end
167
186
 
168
187
  begin
169
188
  Calabash::Cucumber::HTTP.ensure_connectivity(merged_options)
@@ -188,19 +207,18 @@ Try `start_test_server_in_background`
188
207
  return false
189
208
  end
190
209
 
191
- if self.run_loop[:pid]
192
- self.actions = Calabash::Cucumber::InstrumentsActions.new
210
+ if run_loop[:pid]
211
+ @automator = Calabash::Cucumber::Automator::Instruments.new(run_loop)
193
212
  else
194
213
  RunLoop.log_warn(
195
214
  %Q[
196
215
 
197
216
  Connected to an app that was not launched by Calabash using instruments.
198
217
 
199
- Queries will work, but gestures will not.
218
+ Queries will work, but gestures and other automator actions will not.
200
219
 
201
220
  ])
202
221
  end
203
-
204
222
  self
205
223
  end
206
224
 
@@ -208,19 +226,30 @@ Queries will work, but gestures will not.
208
226
  #
209
227
  # @return {Boolean} true if we're using instruments to launch
210
228
  def self.instruments?
211
- l = launcher_if_used
212
- return false unless l
213
- l.instruments?
229
+ launcher = Launcher::launcher_if_used
230
+ if !launcher
231
+ false
232
+ else
233
+ launcher.instruments?
234
+ end
214
235
  end
215
236
 
216
237
  # @!visibility private
217
238
  def instruments?
218
- !!(active? && run_loop[:pid])
239
+ attached_to_automator? &&
240
+ automator.name == :instruments
219
241
  end
220
242
 
243
+ # @!visibility private
244
+ def attached_to_automator?
245
+ automator != nil
246
+ end
247
+
248
+ # TODO remove in 0.21.0
221
249
  # @!visibility private
222
250
  def active?
223
- not run_loop.nil?
251
+ RunLoop.deprecated("0.20.0", "replaced with attached_to_automator?")
252
+ attached_to_automator?
224
253
  end
225
254
 
226
255
  # A reference to the current launcher (instantiates a new one if needed).
@@ -336,10 +365,29 @@ Resetting physical devices is not supported.
336
365
  options[:xcode] = xcode || Calabash::Cucumber::Environment.xcode
337
366
  options[:inject_dylib] = detect_inject_dylib_option(launch_options)
338
367
 
339
- self.launch_args = options
368
+ @launch_args = options
369
+
370
+ @run_loop = new_run_loop(options)
371
+ if @run_loop.is_a?(Hash)
372
+ @automator = Calabash::Cucumber::Automator::Instruments.new(@run_loop)
373
+ elsif @run_loop.is_a?(RunLoop::DeviceAgent::Client)
374
+ @automator = Calabash::Cucumber::Automator::DeviceAgent.new(@run_loop)
375
+ else
376
+ raise ArgumentError, %Q[
377
+
378
+ Could not determine which automator to use based on the launch arguments:
379
+
380
+ #{@launch_args.join("$-0")}
381
+
382
+ RunLoop.run returned:
340
383
 
341
- self.run_loop = new_run_loop(options)
342
- self.actions= Calabash::Cucumber::InstrumentsActions.new
384
+ #{@run_loop}
385
+
386
+ ]
387
+ end
388
+
389
+ Calabash::Cucumber::UIA.redefine_instance_methods_if_necessary(options[:xcode],
390
+ automator)
343
391
 
344
392
  if !options[:calabash_lite]
345
393
  Calabash::Cucumber::HTTP.ensure_connectivity
@@ -374,7 +422,23 @@ Resetting physical devices is not supported.
374
422
  # @!visibility private
375
423
  # TODO Should call calabash exit route to shutdown the server.
376
424
  def stop
377
- RunLoop.stop(run_loop) if run_loop && run_loop[:pid]
425
+ return :no_automator if !automator
426
+
427
+ if !automator.respond_to?(:name)
428
+ RunLoop.log_warn("Unknown automator: #{automator}")
429
+ RunLoop.log_warn("Calabash does not know how to stop this automator")
430
+ return :unknown_automator
431
+ end
432
+
433
+ case automator.name
434
+ when :instruments, :device_agent
435
+ automator.stop
436
+ :stopped
437
+ else
438
+ RunLoop.log_warn("Unknown automator: #{automator}")
439
+ RunLoop.log_warn("Calabash does not know how to stop this automator")
440
+ :unknown_automator
441
+ end
378
442
  end
379
443
 
380
444
  # Should Calabash quit the app under test after a Scenario?
@@ -418,6 +482,7 @@ Resetting physical devices is not supported.
418
482
  end
419
483
 
420
484
  # @deprecated 0.19.0 - replaced with #quit_app_after_scenario?
485
+ # TODO remove in 0.20.0
421
486
  # @!visibility private
422
487
  def calabash_no_stop?
423
488
  # Not yet. Save for 0.20.0.
@@ -427,6 +492,7 @@ Resetting physical devices is not supported.
427
492
 
428
493
  # @!visibility private
429
494
  # @deprecated 0.19.0 - no replacement
495
+ # TODO remove in 0.20.0
430
496
  def calabash_no_launch?
431
497
  RunLoop.log_warn(%Q[
432
498
  Calabash::Cucumber::Launcher #calabash_no_launch? and support for the NO_LAUNCH
@@ -438,6 +504,7 @@ true. Please remove this method call from your hooks.
438
504
 
439
505
  # @!visibility private
440
506
  # @deprecated 0.19.0 - no replacement.
507
+ # TODO remove in 0.20.0
441
508
  def default_uia_strategy(launch_args, sim_control, instruments)
442
509
  RunLoop::deprecated("0.19.0", "This method has been removed.")
443
510
  :host
@@ -445,6 +512,7 @@ true. Please remove this method call from your hooks.
445
512
 
446
513
  # @!visibility private
447
514
  # @deprecated 0.19.0 - no replacement
515
+ # TODO remove in 0.20.0
448
516
  def detect_connected_device?
449
517
  RunLoop.deprecated("0.19.0", "No replacement")
450
518
  false
@@ -452,6 +520,7 @@ true. Please remove this method call from your hooks.
452
520
 
453
521
  # @!visibility private
454
522
  # @deprecated 0.19.0 - no replacement
523
+ # TODO remove in 0.20.0
455
524
  def default_launch_args
456
525
  RunLoop.deprecated("0.19.0", "No replacement")
457
526
  {}
@@ -459,6 +528,7 @@ true. Please remove this method call from your hooks.
459
528
 
460
529
  # @!visibility private
461
530
  # @deprecated 0.19.0 - no replacement
531
+ # TODO remove in 0.20.0
462
532
  def discover_device_target(launch_args)
463
533
  RunLoop.deprecated("0.19.0", "No replacement")
464
534
  nil
@@ -466,6 +536,7 @@ true. Please remove this method call from your hooks.
466
536
 
467
537
  # @!visibility private
468
538
  # @deprecated 0.19.0 - no replacement
539
+ # TODO remove in 0.20.0
469
540
  def app_path
470
541
  RunLoop.deprecated("0.19.0", "No replacement")
471
542
  nil
@@ -473,6 +544,7 @@ true. Please remove this method call from your hooks.
473
544
 
474
545
  # @!visibility private
475
546
  # @deprecated 0.19.0 - no replacement
547
+ # TODO remove in 0.20.0
476
548
  def xcode
477
549
  RunLoop.deprecated("0.19.0", "Use Calabash::Cucumber::Environment.xcode")
478
550
  Calabash::Cucumber::Environment.xcode
@@ -480,6 +552,7 @@ true. Please remove this method call from your hooks.
480
552
 
481
553
  # @!visibility private
482
554
  # @deprecated 0.19.0 - no replacement
555
+ # TODO remove in 0.20.0
483
556
  def ensure_connectivity
484
557
  RunLoop.deprecated("0.19.0", "No replacement")
485
558
  Calabash::Cucumber::HTTP.ensure_connectivity
@@ -491,12 +564,14 @@ true. Please remove this method call from your hooks.
491
564
  # #relaunch will now send ":on_launch" to the Cucumber World if:
492
565
  # * the Launcher is part of the World (it is not by default).
493
566
  # * Cucumber responds to :on_launch.
567
+ # TODO remove in 0.20.0
494
568
  def calabash_notify(_)
495
569
  false
496
570
  end
497
571
 
498
572
  # @!visibility private
499
573
  # @deprecated 0.19.0 - no replacement.
574
+ # TODO remove in 0.20.0
500
575
  def server_version_from_server
501
576
  RunLoop.deprecated("0.19.0", "No replacement")
502
577
  server_version
@@ -504,6 +579,7 @@ true. Please remove this method call from your hooks.
504
579
 
505
580
  # @!visibility private
506
581
  # @deprecated 0.19.0 - no replacement
582
+ # TODO remove in 0.20.0
507
583
  def server_version_from_bundle(app_bundle_path)
508
584
  RunLoop.deprecated("0.19.0", "No replacement")
509
585
  options = {:app => app_bundle_path }
@@ -0,0 +1,46 @@
1
+
2
+ module Calabash
3
+ module Cucumber
4
+ # @!visibility private
5
+ class LogTailer
6
+
7
+ # @!visibility private
8
+ def self.tail_in_terminal(path)
9
+ if !File.exist?(path)
10
+ raise RuntimeError, %Q[
11
+ Cannot tail a file that does not exist:
12
+
13
+ #{path}
14
+
15
+ ]
16
+ end
17
+
18
+ term_part = %Q[xcrun osascript -e 'tell application "Terminal" to do script]
19
+ tail_part = %Q["tail -n 10000 -F #{path} | grep -v \\"Default: \\\\*\\""']
20
+ cmd = "#{term_part} #{tail_part}"
21
+
22
+ if !LogTailer.run_command(cmd)
23
+ raise RuntimeError, %Q[
24
+ Could not tail file:
25
+
26
+ #{path}
27
+
28
+ with command:
29
+
30
+ #{cmd}
31
+
32
+ ]
33
+ end
34
+
35
+ true
36
+ end
37
+
38
+ private
39
+
40
+ # @!visibility private
41
+ def self.run_command(cmd)
42
+ system(cmd)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -27,7 +27,7 @@ module Calabash
27
27
  # If `method_name` maps to no LPOperation, then it is treated a selector
28
28
  # and is performed on any view that matches `query`.
29
29
  #
30
- # @examples
30
+ # @example
31
31
  #
32
32
  # # Calls 'text' on any visible UITextField, because :text is not a defined operation.
33
33
  # > map("textField", :text)
@@ -55,6 +55,12 @@ module Calabash
55
55
  #
56
56
  # @todo Calabash LPOperations should return 'views touched' in JSON format
57
57
  def self.map(query, method_name, *method_args)
58
+ require "calabash-cucumber/launcher"
59
+ launcher = Calabash::Cucumber::Launcher.launcher_if_used
60
+ if launcher && launcher.automator && launcher.automator.name == :device_agent
61
+ launcher.automator.client.send(:_dismiss_springboard_alerts)
62
+ end
63
+
58
64
  self.raw_map(query, method_name, *method_args)['results']
59
65
  end
60
66
 
@@ -1,108 +1,11 @@
1
1
  module Calabash
2
2
  module Cucumber
3
3
 
4
- # Provides methods for rotating a device in a direction or to a particular
5
- # orientation.
4
+ # @!visibility private
6
5
  module RotationHelpers
7
6
 
8
- require "run_loop"
9
-
10
- # Rotates the home button to a position relative to the status bar.
11
- #
12
- # @example portrait
13
- # rotate_home_button_to :down
14
- #
15
- # @example upside down
16
- # rotate_home_button_to :up
17
- #
18
- # @example landscape with left home button AKA: _right_ landscape
19
- # rotate_home_button_to :left
20
- #
21
- # @example landscape with right home button AKA: _left_ landscape
22
- # rotate_home_button_to :right
23
- #
24
- # @note Refer to Apple's documentation for clarification about left vs.
25
- # right landscape orientations.
26
- #
27
- # @note For legacy support the `dir` argument can be a String or Symbol.
28
- # Please update your code to pass a Symbol.
29
- #
30
- # @note For legacy support `:top` and `top` are synonyms for `:up`.
31
- # Please update your code to pass `:up`.
32
- #
33
- # @note For legacy support `:bottom` and `bottom` are synonyms for `:down`.
34
- # Please update your code to pass `:down`.
35
- #
36
- # @note This method generates verbose messages when full console logging
37
- # is enabled. See {Calabash::Cucumber::Logging#full_console_logging?}.
38
- #
39
- # @param [Symbol] direction The position of the home button after the rotation.
40
- # Can be one of `{:down | :left | :right | :up }`.
41
- #
42
- # @note A rotation will only occur if your view controller and application
43
- # support the target orientation.
44
- #
45
- # @return [Symbol] The position of the home button relative to the status
46
- # bar when all rotations have been completed.
47
- def rotate_home_button_to(direction)
48
-
49
- begin
50
- as_symbol = ensure_valid_rotate_home_to_arg(direction)
51
- rescue ArgumentError => e
52
- raise ArgumentError, e.message
53
- end
54
-
55
- current_orientation = status_bar_orientation.to_sym
56
-
57
- return current_orientation if current_orientation == as_symbol
58
-
59
- rotate_to_uia_orientation(as_symbol)
60
- recalibrate_after_rotation
61
- status_bar_orientation.to_sym
62
- end
63
-
64
- # Rotates the device in the direction indicated by `direction`.
65
- #
66
- # @example rotate left
67
- # rotate :left
68
- #
69
- # @example rotate right
70
- # rotate :right
71
- #
72
- # @param [Symbol] direction The direction to rotate. Can be :left or :right.
73
- #
74
- # @return [Symbol] The position of the home button relative to the status
75
- # bar after the rotation. Will be one of `{:down | :left | :right | :up }`.
76
- # @raise [ArgumentError] If direction is not :left or :right.
77
- def rotate(direction)
78
-
79
- as_symbol = direction.to_sym
80
-
81
- if as_symbol != :left && as_symbol != :right
82
- raise ArgumentError,
83
- "Expected '#{direction}' to be :left or :right"
84
- end
85
-
86
- current_orientation = status_bar_orientation.to_sym
87
-
88
- result = rotate_with_uia(as_symbol, current_orientation)
89
-
90
- recalibrate_after_rotation
91
-
92
- ap result if RunLoop::Environment.debug?
93
-
94
- status_bar_orientation
95
- end
96
-
97
- private
98
-
99
- # @! visibility private
100
- def recalibrate_after_rotation
101
- uia_query :window
102
- end
103
-
104
7
  # @! visibility private
105
- def ensure_valid_rotate_home_to_arg(arg)
8
+ def expect_valid_rotate_home_to_arg(arg)
106
9
  coerced = arg.to_sym
107
10
 
108
11
  if coerced == :top
@@ -119,62 +22,44 @@ module Calabash
119
22
  coerced
120
23
  end
121
24
 
122
- # @! visibility private
123
- UIA_DEVICE_ORIENTATION = {
124
- :portrait => 1,
125
- :upside_down => 2,
126
- :landscape_left => 3,
127
- :landscape_right => 4
128
- }.freeze
129
-
130
- # @! visibility private
131
- def rotate_to_uia_orientation(orientation)
132
- case orientation
133
- when :down then key = :portrait
134
- when :up then key = :upside_down
135
- when :left then key = :landscape_right
136
- when :right then key = :landscape_left
137
- else
138
- raise ArgumentError,
139
- "Expected '#{orientation}' to be :left, :right, :up, or :down"
140
- end
141
- value = UIA_DEVICE_ORIENTATION[key]
142
- cmd = "UIATarget.localTarget().setDeviceOrientation(#{value})"
143
- uia(cmd)
144
- end
145
-
146
- # @! visibility private
147
- def rotate_with_uia(direction, current_orientation)
148
- key = uia_orientation_key(direction, current_orientation)
149
- value = UIA_DEVICE_ORIENTATION[key]
150
- cmd = "UIATarget.localTarget().setDeviceOrientation(#{value})"
151
- uia(cmd)
25
+ # @!visibility private
26
+ def orientation_for_key(key)
27
+ DEVICE_ORIENTATION[key]
152
28
  end
153
29
 
154
30
  # @! visibility private
155
- def uia_orientation_key(direction, current_orientation)
156
-
31
+ #
32
+ # It is important to remember that the current orientation is the
33
+ # position of the home button:
34
+ #
35
+ # :up => home button on the top => upside_down
36
+ # :bottom => home button on the bottom => portrait
37
+ # :left => home button on the left => landscape_right
38
+ # :right => home button on the right => landscape_left
39
+ #
40
+ # Notice how :left and :right are mapped.
41
+ def orientation_key(direction, current_orientation)
157
42
  key = nil
158
43
  case direction
159
44
  when :left then
160
45
  if current_orientation == :down
161
- key = :landscape_right
46
+ key = :landscape_left
162
47
  elsif current_orientation == :right
163
- key = :portrait
164
- elsif current_orientation == :left
165
48
  key = :upside_down
49
+ elsif current_orientation == :left
50
+ key = :portrait
166
51
  elsif current_orientation == :up
167
- key = :landscape_left
52
+ key = :landscape_right
168
53
  end
169
54
  when :right then
170
55
  if current_orientation == :down
171
- key = :landscape_left
56
+ key = :landscape_right
172
57
  elsif current_orientation == :right
173
- key = :upside_down
174
- elsif current_orientation == :left
175
58
  key = :portrait
59
+ elsif current_orientation == :left
60
+ key = :upside_down
176
61
  elsif current_orientation == :up
177
- key = :landscape_right
62
+ key = :landscape_left
178
63
  end
179
64
  else
180
65
  raise ArgumentError,
@@ -182,6 +67,29 @@ module Calabash
182
67
  end
183
68
  key
184
69
  end
70
+
71
+ private
72
+
73
+ # @! visibility private
74
+ #
75
+ # It is important to remember that the current orientation is the
76
+ # position of the home button:
77
+ #
78
+ # :up => home button on the top => upside_down
79
+ # :bottom => home button on the bottom => portrait
80
+ # :left => home button on the left => landscape_right
81
+ # :right => home button on the right => landscape_left
82
+ #
83
+ # Notice how :left and :right are mapped to their logical opposite.
84
+ # @!visibility private
85
+ # @! visibility private
86
+ DEVICE_ORIENTATION = {
87
+ :portrait => 1,
88
+ :upside_down => 2,
89
+ :landscape_left => 3, # Home button on the right
90
+ :landscape_right => 4 # Home button on the left
91
+ }.freeze
92
+
185
93
  end
186
94
  end
187
95
  end