calabash-cucumber 0.19.2 → 0.20.0

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 (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