calabash-cucumber 0.9.169.pre2 → 0.9.169.pre5

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +14 -1
  3. data/bin/calabash-ios-setup.rb +2 -4
  4. data/bin/calabash-ios-sim.rb +10 -40
  5. data/calabash-cucumber.gemspec +25 -22
  6. data/features-skeleton/support/01_launch.rb +1 -1
  7. data/lib/calabash-cucumber.rb +13 -1
  8. data/lib/calabash-cucumber/actions/instruments_actions.rb +0 -4
  9. data/lib/calabash-cucumber/actions/playback_actions.rb +0 -4
  10. data/lib/calabash-cucumber/core.rb +9 -16
  11. data/lib/calabash-cucumber/device.rb +11 -2
  12. data/lib/calabash-cucumber/environment_helpers.rb +4 -56
  13. data/lib/calabash-cucumber/ios7_operations.rb +4 -2
  14. data/lib/calabash-cucumber/keyboard_helpers.rb +6 -3
  15. data/lib/calabash-cucumber/launch/simulator_helper.rb +40 -386
  16. data/lib/calabash-cucumber/launch/simulator_launcher.rb +534 -0
  17. data/lib/calabash-cucumber/launcher.rb +172 -36
  18. data/lib/calabash-cucumber/operations.rb +3 -4
  19. data/lib/calabash-cucumber/playback_helpers.rb +15 -29
  20. data/lib/calabash-cucumber/rotation_helpers.rb +14 -10
  21. data/lib/calabash-cucumber/status_bar_helpers.rb +5 -1
  22. data/lib/calabash-cucumber/uia.rb +6 -12
  23. data/lib/calabash-cucumber/utils/logging.rb +97 -0
  24. data/lib/calabash-cucumber/utils/plist_buddy.rb +178 -0
  25. data/lib/calabash-cucumber/utils/simulator_accessibility.rb +250 -0
  26. data/lib/calabash-cucumber/utils/xctools.rb +95 -0
  27. data/lib/calabash-cucumber/version.rb +197 -2
  28. data/lib/calabash-cucumber/wait_helpers.rb +16 -20
  29. data/scripts/.irbrc +11 -6
  30. data/scripts/com.example.plist +0 -0
  31. data/scripts/launch.rb +1 -1
  32. data/spec/bin/calabash_ios_sim_spec.rb +24 -0
  33. data/spec/launcher_spec.rb +76 -0
  34. data/spec/logging_spec.rb +38 -0
  35. data/spec/plist_buddy_spec.rb +99 -0
  36. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/Default-568h@2x.png +0 -0
  37. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/Info.plist +0 -0
  38. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/LPSimpleExample-cal +0 -0
  39. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/PkgInfo +1 -0
  40. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/en.lproj/InfoPlist.strings +0 -0
  41. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/en.lproj/LPFirstViewController.nib +0 -0
  42. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/en.lproj/LPFirstViewController~ipad.nib +0 -0
  43. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/en.lproj/LPFourthViewController.nib +0 -0
  44. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/en.lproj/LPFourthViewController~ipad.nib +0 -0
  45. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/en.lproj/LPSecondViewController.nib +0 -0
  46. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/en.lproj/LPSecondViewController~ipad.nib +0 -0
  47. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/en.lproj/LPThirdViewController.nib +0 -0
  48. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/en.lproj/LPThirdViewController~ipad.nib +0 -0
  49. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/first.png +0 -0
  50. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/first@2x.png +0 -0
  51. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/second.png +0 -0
  52. data/spec/resources/enable-accessibility/LPSimpleExample-cal.app/second@2x.png +0 -0
  53. data/spec/resources/plist_buddy/com.example.plist +0 -0
  54. data/spec/resources/plist_buddy/com.testing.plist +18 -0
  55. data/spec/simulator_accessibility_spec.rb +144 -0
  56. data/spec/spec_helper.rb +31 -0
  57. data/spec/xctools_spec.rb +58 -0
  58. metadata +120 -34
@@ -1,7 +1,11 @@
1
+ require 'calabash-cucumber/utils/logging'
2
+
1
3
  module Calabash
2
4
  module Cucumber
3
5
  module RotationHelpers #=> Connection, StatusBarHelpers
4
6
 
7
+ include Calabash::Cucumber::Logging
8
+
5
9
  def rotation_candidates
6
10
  %w(rotate_left_home_down rotate_left_home_left rotate_left_home_right rotate_left_home_up
7
11
  rotate_right_home_down rotate_right_home_left rotate_right_home_right rotate_right_home_up)
@@ -18,15 +22,15 @@ module Calabash
18
22
  def rotate_home_button_to(dir)
19
23
  dir_sym = dir.to_sym
20
24
  if dir_sym.eql?(:top)
21
- if ENV['CALABASH_FULL_CONSOLE_OUTPUT'] == '1'
22
- warn "converting '#{dir}' to ':up' - please adjust your code"
25
+ if full_console_logging?
26
+ calabash_warn "converting '#{dir}' to ':up' - please adjust your code"
23
27
  end
24
28
  dir_sym = :up
25
29
  end
26
30
 
27
31
  if dir_sym.eql?(:bottom)
28
- if ENV['CALABASH_FULL_CONSOLE_OUTPUT'] == '1'
29
- warn "converting '#{dir}' to ':down' - please adjust your code"
32
+ if full_console_logging?
33
+ calabash_warn "converting '#{dir}' to ':down' - please adjust your code"
30
34
  end
31
35
  dir_sym = :down
32
36
  end
@@ -46,7 +50,7 @@ module Calabash
46
50
  return res if res.eql? dir_sym
47
51
 
48
52
  rotation_candidates.each { |candidate|
49
- if ENV['CALABASH_FULL_CONSOLE_OUTPUT'] == '1'
53
+ if full_console_logging?
50
54
  puts "try to rotate to '#{dir_sym}' using '#{candidate}'"
51
55
  end
52
56
  playback(candidate)
@@ -63,10 +67,10 @@ module Calabash
63
67
  return if res.eql? dir_sym
64
68
  }
65
69
 
66
- if ENV['CALABASH_FULL_CONSOLE_OUTPUT'] == '1'
67
- warn "Could not rotate home button to '#{dir}'."
68
- warn 'Is rotation enabled for this controller?'
69
- warn "Will return 'down'"
70
+ if full_console_logging?
71
+ calabash_warn "Could not rotate home button to '#{dir}'."
72
+ calabash_warn 'Is rotation enabled for this controller?'
73
+ calabash_warn "Will return 'down'"
70
74
  end
71
75
  :down
72
76
  end
@@ -99,7 +103,7 @@ module Calabash
99
103
  end
100
104
 
101
105
  if rotate_cmd.nil?
102
- if ENV['CALABASH_FULL_CONSOLE_OUTPUT'] == '1'
106
+ if full_console_logging?
103
107
  puts "Could not rotate device in direction '#{dir}' with orientation '#{current_orientation} - will do nothing"
104
108
  end
105
109
  else
@@ -1,12 +1,16 @@
1
+ require 'calabash-cucumber/utils/logging'
2
+
1
3
  module Calabash
2
4
  module Cucumber
3
5
  module StatusBarHelpers #=> Map
4
6
 
7
+ include Calabash::Cucumber::Logging
8
+
5
9
  def device_orientation(force_down=false)
6
10
  res = map(nil, :orientation, :device).first
7
11
 
8
12
  if ['face up', 'face down'].include?(res)
9
- if ENV['CALABASH_FULL_CONSOLE_OUTPUT'] == '1'
13
+ if full_console_logging?
10
14
  if force_down
11
15
  puts "WARN found orientation '#{res}' - will rotate to force orientation to 'down'"
12
16
  end
@@ -2,11 +2,14 @@ require 'edn'
2
2
  require 'json'
3
3
  # required for ruby 1.8
4
4
  require 'enumerator'
5
+ require 'calabash-cucumber/utils/logging'
5
6
 
6
7
  module Calabash
7
8
  module Cucumber
8
9
  module UIA
9
10
 
11
+ include Calabash::Cucumber::Logging
12
+
10
13
  def uia(command,options={})
11
14
  res = http({:method => :post, :path => 'uia'}, {:command => command}.merge(options))
12
15
  res = JSON.parse(res)
@@ -16,15 +19,6 @@ module Calabash
16
19
  res['results'].first
17
20
  end
18
21
 
19
- def uia_tap_server(query, options={})
20
- res = http({:method => :post, :path => 'uia-tap'}, {:query => query}.merge(options))
21
- res = JSON.parse(res)
22
- if res['outcome'] != 'SUCCESS'
23
- raise "uia-tap action failed because: #{res['reason']}\n#{res['details']}"
24
- end
25
- res['results'].first
26
- end
27
-
28
22
  def uia_query(*queryparts)
29
23
  #TODO escape '\n etc in query
30
24
  uia_handle_command(:query, queryparts)
@@ -201,7 +195,7 @@ module Calabash
201
195
  end
202
196
  end
203
197
  command = "#{js_cmd}.#{js_args.join('.')}"
204
- if ENV['DEBUG'] == '1'
198
+ if debug_logging?
205
199
  puts 'Sending UIA command'
206
200
  puts command
207
201
  end
@@ -212,7 +206,7 @@ module Calabash
212
206
 
213
207
  def uia_handle_command(cmd, *query_args)
214
208
  command = uia_serialize_command(cmd, *query_args)
215
- if ENV['DEBUG'] == '1'
209
+ if debug_logging?
216
210
  puts 'Sending UIA command'
217
211
  puts command
218
212
  end
@@ -270,7 +264,7 @@ module Calabash
270
264
  end
271
265
 
272
266
  def uia_result(s)
273
- if ENV['DEBUG'] == '1'
267
+ if debug_logging?
274
268
  puts 'Result'
275
269
  p s
276
270
  end
@@ -0,0 +1,97 @@
1
+
2
+ module Calabash
3
+ module Cucumber
4
+
5
+ # internal logging methods for calabash-ios gem
6
+ module Logging
7
+
8
+ # controls the the kind of information calabash logs
9
+ #
10
+ # this is considered one level above debug logging - maybe we should call
11
+ # the info log level.
12
+ #
13
+ # @return [Boolean] +true+ if the <tt>CALABASH_FULL_CONSOLE_OUTPUT</tt> is set to
14
+ # '1'
15
+ def full_console_logging?
16
+ ENV['CALABASH_FULL_CONSOLE_OUTPUT'] == '1'
17
+ end
18
+
19
+ # controls whether or not calabash logs debug information
20
+ #
21
+ # @return [Boolean] +true+ if the <tt>DEBUG</tt> is set to '1'
22
+ def debug_logging?
23
+ ENV['DEBUG'] == '1'
24
+ end
25
+
26
+ # prints a blue/cyan warning message
27
+ # @param [String] msg the message to print
28
+ def calabash_warn(msg)
29
+ begin
30
+ warn "\033[34m\nWARN: #{msg}\033[0m"
31
+ rescue
32
+ warn "\nWARN: #{msg}"
33
+ end
34
+ end
35
+
36
+ # prints a green info message
37
+ # @param [String] msg the message to print
38
+ def calabash_info(msg)
39
+ begin
40
+ puts "\033[32m\nINFO: #{msg}\033[0m"
41
+ rescue
42
+ puts "\nINFO: #{msg}"
43
+ end
44
+ end
45
+
46
+ # controls printing of deprecation warnings
47
+ #
48
+ # to inhibit deprecation message set this to '1'
49
+ #
50
+ # inhibiting deprecation messages is not recommend
51
+ CALABASH_NO_DEPRECATION = ENV['CALABASH_NO_DEPRECATION'] || '0'
52
+
53
+ # returns +true+ if the <tt>CALABASH_NO_DEPRECATION</tt> variable is set
54
+ # to +1+
55
+ def no_deprecation_warnings?
56
+ ENV['CALABASH_NO_DEPRECATION'] == '1'
57
+ end
58
+
59
+ # prints a deprecated message that includes the line number
60
+ #
61
+ # if ENV['CALABASH_NO_DEPRECATION'] == '1' then this method is a nop
62
+ #
63
+ # @param [String] version indicates when the feature was deprecated
64
+ # @param [String] msg deprecation message (possibly suggesting alternatives)
65
+ # @param [Symbol] type { :warn | :pending } - :pending will raise a
66
+ # cucumber pending exception
67
+ def _deprecated(version, msg, type)
68
+ allowed = [:pending, :warn]
69
+ unless allowed.include?(type)
70
+ raise "type '#{type}' must be on of '#{allowed}'"
71
+ end
72
+
73
+ unless no_deprecation_warnings?
74
+
75
+ if RUBY_VERSION < '2.0'
76
+ stack = Kernel.caller()[1..6].join("\n")
77
+ else
78
+ stack = Kernel.caller(0, 6)[1..-1].join("\n")
79
+ end
80
+
81
+ msg = "deprecated '#{version}' - '#{msg}'\n#{stack}"
82
+
83
+ if type.eql?(:pending)
84
+ pending(msg)
85
+ else
86
+ begin
87
+ warn "\033[34m\nWARN: #{msg}\033[0m"
88
+ rescue
89
+ warn "\nWARN: #{msg}"
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,178 @@
1
+ require 'calabash-cucumber/utils/logging'
2
+
3
+ module Calabash
4
+ module Cucumber
5
+
6
+ include Calabash::Cucumber::Logging
7
+
8
+ # module for reading and writing property list values
9
+ module PlistBuddy
10
+
11
+ # reads +key+ from +file+ and returns the result
12
+ # @param [String] key the key to inspect (may not be nil or empty)
13
+ # @param [String] file the plist to read
14
+ # @param [Hash] opts options for controlling execution
15
+ # @option opts [Boolean] :verbose (false) controls log level
16
+ # @return [nil] if +key+ does not exist
17
+ # @return [String] if the +key+ exists then the value of +key+ (error)
18
+ # @raise [ArgumentError] if nil or empty +key+
19
+ def plist_read(key, file, opts={})
20
+ if key.nil? or key.length == 0
21
+ raise(ArgumentError, "key '#{key}' must not be nil or empty")
22
+ end
23
+ cmd = build_plist_cmd(:print, {:key => key}, file)
24
+ res = execute_plist_cmd(cmd, opts)
25
+ if res == "Print: Entry, \":#{key}\", Does Not Exist"
26
+ nil
27
+ else
28
+ res
29
+ end
30
+ end
31
+
32
+ # checks if the key exists in plist
33
+ # @param [String] key the key to inspect (may not be nil or empty)
34
+ # @param [String] file the plist to read
35
+ # @param [Hash] opts options for controlling execution
36
+ # @option opts [Boolean] :verbose (false) controls log level
37
+ # @return [Boolean] true iff the +key+ exists in plist +file+
38
+ def plist_key_exists?(key, file, opts={})
39
+ plist_read(key, file, opts) != nil
40
+
41
+ end
42
+
43
+ # replaces or creates the +value+ of +key+ in the +file+
44
+ #
45
+ # @param [String] key the key to set (may not be nil or empty)
46
+ # @param [String] type the plist type (used only when adding a value)
47
+ # @param [String] value the new value
48
+ # @param [String] file the plist to read
49
+ # @param [Hash] opts options for controlling execution
50
+ # @option opts [Boolean] :verbose (false) controls log level
51
+ # @return [Boolean] true iff the operation was successful
52
+ # @raise [ArgumentError] if nil or empty +key+
53
+ def plist_set(key, type, value, file, opts={})
54
+ default_opts = {:verbose => false}
55
+ merged = default_opts.merge(opts)
56
+
57
+ if key.nil? or key.length == 0
58
+ raise(ArgumentError, "key '#{key}' must not be nil or empty")
59
+ end
60
+
61
+ cmd_args = {:key => key,
62
+ :type => type,
63
+ :value => value}
64
+
65
+ if plist_key_exists?(key, file, merged)
66
+ cmd = build_plist_cmd(:set, cmd_args, file)
67
+ else
68
+ cmd = build_plist_cmd(:add, cmd_args, file)
69
+ end
70
+
71
+ res = execute_plist_cmd(cmd, merged)
72
+ res == ''
73
+ end
74
+
75
+ @private
76
+
77
+ # returns the path to the PlistBuddy executable
78
+ # @return [String] path to PlistBuddy
79
+ def plist_buddy
80
+ '/usr/libexec/PlistBuddy'
81
+ end
82
+
83
+ # executes +cmd+ as a shell command and returns the result
84
+ #
85
+ # @param [String] cmd shell command to execute
86
+ # @param [Hash] opts options for controlling execution
87
+ # @option opts [Boolean] :verbose (false) controls log level
88
+ # @return [Boolean] if command was successful
89
+ # @return [String] if :print'ing result, the value of the key
90
+ # @return [String] if there is an error, the output from stderr
91
+ def execute_plist_cmd(cmd, opts={})
92
+ default_opts = {:verbose => false}
93
+ merged = default_opts.merge(opts)
94
+
95
+ calabash_info(cmd) if merged[:verbose]
96
+
97
+ res = nil
98
+ # noinspection RubyUnusedLocalVariable
99
+ Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
100
+ err = stderr.read
101
+ std = stdout.read
102
+ if not err.nil? and err != ''
103
+ res = err.chomp
104
+ else
105
+ res = std.chomp
106
+ end
107
+ end
108
+ res
109
+ end
110
+
111
+ # composes a PlistBuddy command that can be executed as a shell command
112
+ #
113
+ # @param [Symbol] type should be one of [:print, :set, :add]
114
+ #
115
+ # @param [Hash] args_hash arguments used to construct plist command
116
+ # @option args_hash [String] :key (required) the plist key
117
+ # @option args_hash [String] :value (required for :set and :add) the new value
118
+ # @option args_hash [String] :type (required for :add) the new type of the value
119
+ #
120
+ # @param [String] file the plist file to interact with (must exist)
121
+ #
122
+ # @raise [RuntimeError] if +file+ does not exist
123
+ # @raise [ArgumentError] when invalid +type+ is passed
124
+ # @raise [ArgumentError] when +args_hash+ does not include required key/value pairs
125
+ #
126
+ # @return [String] a shell-ready PlistBuddy command
127
+ def build_plist_cmd(type, args_hash, file)
128
+
129
+ unless File.exist?(File.expand_path(file))
130
+ raise(RuntimeError, "plist '#{file}' does not exist - could not read")
131
+ end
132
+
133
+ case type
134
+ when :add
135
+ value_type = args_hash[:type]
136
+ unless value_type
137
+ raise(ArgumentError, ':value_type is a required key for :add command')
138
+ end
139
+ allowed_value_types = ['string', 'bool', 'real', 'integer']
140
+ unless allowed_value_types.include?(value_type)
141
+ raise(ArgumentError, "expected '#{value_type}' to be one of '#{allowed_value_types}'")
142
+ end
143
+ value = args_hash[:value]
144
+ unless value
145
+ raise(ArgumentError, ':value is a required key for :add command')
146
+ end
147
+ key = args_hash[:key]
148
+ unless key
149
+ raise(ArgumentError, ':key is a required key for :add command')
150
+ end
151
+ cmd_part = "\"Add :#{key} #{value_type} #{value}\""
152
+ when :print
153
+ key = args_hash[:key]
154
+ unless key
155
+ raise(ArgumentError, ':key is a required key for :print command')
156
+ end
157
+ cmd_part = "\"Print :#{key}\""
158
+ when :set
159
+ value = args_hash[:value]
160
+ unless value
161
+ raise(ArgumentError, ':value is a required key for :set command')
162
+ end
163
+ key = args_hash[:key]
164
+ unless key
165
+ raise(ArgumentError, ':key is a required key for :set command')
166
+ end
167
+ cmd_part = "\"Set :#{key} #{value}\""
168
+ else
169
+ cmds = [:add, :print, :set]
170
+ raise(ArgumentError, "expected '#{type}' to be one of '#{cmds}'")
171
+ end
172
+
173
+ "#{plist_buddy} -c #{cmd_part} \"#{file}\""
174
+ end
175
+
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,250 @@
1
+ require 'calabash-cucumber/utils/xctools'
2
+ require 'calabash-cucumber/utils/plist_buddy'
3
+ require 'sim_launcher'
4
+
5
+ module Calabash
6
+ module Cucumber
7
+
8
+ # methods for checking and setting simulator accessibility
9
+ module SimulatorAccessibility
10
+
11
+ include Calabash::Cucumber::XcodeTools
12
+ include Calabash::Cucumber::PlistBuddy
13
+
14
+ # quits the iOS Simulator
15
+ #
16
+ # ATM there can only be only simulator open at a time, so simply doing
17
+ # what the sim_launcher gem does:
18
+ #
19
+ # def quit_simulator
20
+ # `echo 'application "iPhone Simulator" quit' | osascript`
21
+ # end
22
+ #
23
+ # works. I am not sure if we will ever be able to launch more than one
24
+ # simulator, but in case we can, this method will quit the simulator
25
+ # that is indicated by +xcode-select+ or +DEVELOPER_DIR+.
26
+ def quit_simulator
27
+ dev_dir = xcode_developer_dir
28
+ system "/usr/bin/osascript -e 'tell application \"#{dev_dir}/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app\" to quit'"
29
+ end
30
+
31
+ # launches the iOS Simulator indicated by +xcode-select+ or +DEVELOPER_DIR+
32
+ def launch_simulator
33
+ dev_dir = xcode_developer_dir
34
+ system "open -a \"#{dev_dir}/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app\""
35
+ end
36
+
37
+ # resets the simulator content and settings. it is analogous to touching
38
+ # the menu item.
39
+ #
40
+ # it works by deleting the following directories:
41
+ #
42
+ # * ~/Library/Application Support/iPhone Simulator/Library
43
+ # * ~/Library/Application Support/iPhone Simulator/Library/<sdk>[-64]
44
+ #
45
+ # and relaunching the iOS Simulator which will recreate the Library
46
+ # directory and the latest SDK directory.
47
+ def reset_simulator_content_and_settings
48
+ quit_simulator
49
+ sim_lib_path = File.join(simulator_app_support_dir(), 'Library')
50
+ FileUtils.rm_rf(sim_lib_path)
51
+ simulator_support_sdk_dirs.each do |dir|
52
+ FileUtils.rm_rf(dir)
53
+ end
54
+
55
+ launch_simulator
56
+
57
+ # this is tricky because we need to wait for the simulator to recreate
58
+ # the directories. specifically, we need the Accessibility plist to be
59
+ # exist so subsequent calabash launches will be able to enable
60
+ # accessibility.
61
+ #
62
+ # the directories take ~3.0 - ~5.0 to create.
63
+ counter = 0
64
+ loop do
65
+ break if counter == 80
66
+ dirs = simulator_support_sdk_dirs
67
+ if dirs.count == 0
68
+ sleep(0.2)
69
+ else
70
+ break if dirs.all? { |dir|
71
+ plist = File.expand_path("#{dir}/Library/Preferences/com.apple.Accessibility.plist")
72
+ File.exists?(plist)
73
+ }
74
+ sleep(0.2)
75
+ end
76
+ counter = counter + 1
77
+ end
78
+ end
79
+
80
+ # enables accessibility on any existing iOS Simulator by adjusting the
81
+ # simulator's Library/Preferences/com.apple.Accessibility.plist contents.
82
+ #
83
+ # a simulator 'exists' if has an Application Support directory. for
84
+ # example, the 6.1, 7.0.3-64, and 7.1 simulators exist if the following
85
+ # directories are present:
86
+ #
87
+ # ~/Library/Application Support/iPhone Simulator/Library/6.1
88
+ # ~/Library/Application Support/iPhone Simulator/Library/7.0.3-64
89
+ # ~/Library/Application Support/iPhone Simulator/Library/7.1
90
+ #
91
+ # this method also hides the AXInspector.
92
+ # @param [Hash] opts controls the behavior of the method
93
+ # @option opts [Boolean] :verbose controls logging output
94
+ # @return [Boolean] true iff enabling accessibility worked on all sdk
95
+ # directories
96
+ def enable_accessibility_on_simulators(opts={})
97
+ results = simulator_support_sdk_dirs.map do |dir|
98
+ enable_accessibility_in_sdk_dir(dir, opts)
99
+ end
100
+ results.all? { |elm| elm }
101
+ end
102
+
103
+ @private
104
+
105
+ # enables accessibility on the simulator indicated by
106
+ # +sim_app_support_sdk_dir+.
107
+ #
108
+ # WARNING: this will quit the simulator
109
+ #
110
+ # path = '~/Library/Application Support/iPhone Simulator/Library/6.1'
111
+ # enable_accessibility_in_sdk_dir(path)
112
+ #
113
+ # this method also hides the AXInspector.
114
+ #
115
+ # if the Library/Preferences/com.apple.Accessibility.plist does not exist,
116
+ # this method returns false.
117
+ #
118
+ # @see enable_accessibility_on_simulators for the public API.
119
+ #
120
+ # @param [String] sim_app_support_sdk_dir the directory where the
121
+ # Library/Preferences/com.apple.Accessibility.plist can be found.
122
+ #
123
+ # @param [Hash] opts controls the behavior of the method
124
+ # @option opts [Boolean] :verbose controls logging output
125
+ # @return [Boolean] iff the plist exists and the plist was successfully
126
+ # updated.
127
+ def enable_accessibility_in_sdk_dir(sim_app_support_sdk_dir, opts={})
128
+ default_opts = {:verbose => false}
129
+ merged = default_opts.merge(opts)
130
+
131
+ plist_path = File.expand_path("#{sim_app_support_sdk_dir}/Library/Preferences/com.apple.Accessibility.plist")
132
+
133
+ verbose = merged[:verbose]
134
+
135
+ sdk = File.basename(sim_app_support_sdk_dir)
136
+ msgs = ["cannot enable accessibility for #{sdk} SDK"]
137
+ unless File.exists?(plist_path)
138
+ if verbose
139
+ msgs << "expected plist to exist at #{plist_path}"
140
+ calabash_warn(msgs.join("\n"))
141
+ end
142
+ return false
143
+ end
144
+
145
+ quit_simulator
146
+
147
+ hash = accessibility_properties_hash()
148
+
149
+ unless plist_set(hash[:access_enabled], 'bool', 'true', plist_path)
150
+ if verbose
151
+ msgs << "could not set '#{hash[:access_enabled]}' to YES"
152
+ calabash_warn(msgs.join("\n"))
153
+ end
154
+ return false
155
+ end
156
+
157
+ unless plist_set(hash[:app_access_enabled], 'bool', 'true', plist_path)
158
+ if verbose
159
+ msgs << "could not set '#{hash[:app_access_enabled]}' to YES"
160
+ calabash_warn(msgs.join("\n"))
161
+ end
162
+ return false
163
+ end
164
+
165
+ unless plist_set(hash[:automation_enabled], 'bool', 'true', plist_path)
166
+ if verbose
167
+ msgs << "could not set '#{hash[:automation_enabled]}' to YES"
168
+ calabash_warn(msgs.join("\n"))
169
+ end
170
+ return false
171
+ end
172
+
173
+ unless plist_set(hash[:inspector_showing], 'bool', 'false', plist_path)
174
+ if verbose
175
+ msgs << "could not set '#{hash[:inspector_showing]}' to NO"
176
+ calabash_warn(msgs.join("\n"))
177
+ end
178
+ return false
179
+ end
180
+
181
+ unless plist_set(hash[:inspector_full_size], 'bool', 'false', plist_path)
182
+ if verbose
183
+ msgs << "could not set '#{hash[:inspector_full_size]}' to NO"
184
+ calabash_warn(msgs.join("\n"))
185
+ end
186
+ return false
187
+ end
188
+
189
+ res = plist_set(hash[:inspector_frame], 'string', '{{270, -13}, {276, 166}}', plist_path)
190
+ unless res
191
+ if verbose
192
+ msgs << "could not set '#{hash[:inspector_frame]}'"
193
+ calabash_warn(msgs.join("\n"))
194
+ end
195
+ end
196
+ res
197
+ end
198
+
199
+
200
+ # a hash table of the accessibility properties that control whether or not
201
+ # accessibility is enabled and whether the AXInspector is visible.
202
+ # @return [Hash] table of accessibility properties found in the
203
+ # Library/Preferences/com.apple.Accessibility.plist
204
+ def accessibility_properties_hash
205
+ {
206
+ # this is required
207
+ :access_enabled => 'AccessibilityEnabled',
208
+ # i _think_ this is legacy
209
+ :app_access_enabled => 'ApplicationAccessibilityEnabled',
210
+
211
+ # i don't know what this does
212
+ :automation_enabled => 'AutomationEnabled',
213
+
214
+ # determines if the Accessibility Inspector is showing
215
+ :inspector_showing => 'AXInspectorEnabled',
216
+ # controls if the Accessibility Inspector is expanded or not expanded
217
+ :inspector_full_size => 'AXInspector.enabled',
218
+ # controls the frame of the Accessibility Inspector
219
+ # this is a 'string' => {{0, 0}, {276, 166}}
220
+ :inspector_frame => 'AXInspector.frame'
221
+ }
222
+ end
223
+
224
+ # the absolute path the the iPhone Simulator Application Support directory
225
+ # @return [String] absolute path
226
+ def simulator_app_support_dir
227
+ File.expand_path('~/Library/Application Support/iPhone Simulator')
228
+ end
229
+
230
+ # returns a list of absolute paths the existing simulator directories.
231
+ #
232
+ # a simulator 'exists' if has an Application Support directory. for
233
+ # example, the 6.1, 7.0.3-64, and 7.1 simulators exist if the following
234
+ # directories are present:
235
+ #
236
+ # ~/Library/Application Support/iPhone Simulator/Library/6.1
237
+ # ~/Library/Application Support/iPhone Simulator/Library/7.0.3-64
238
+ # ~/Library/Application Support/iPhone Simulator/Library/7.1
239
+ #
240
+ # @return[Array<String>] a list of absolute paths to simulator directories
241
+ def simulator_support_sdk_dirs
242
+ sim_app_support_path = simulator_app_support_dir()
243
+ Dir.glob("#{sim_app_support_path}/*").select { |path|
244
+ path =~ /(\d)\.(\d)\.?(\d)?(-64)?/
245
+ }
246
+ end
247
+
248
+ end
249
+ end
250
+ end