calabash-cucumber 0.9.167 → 0.9.168.pre4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b9f317fce7bc0b7b9619b15814d0eeef21a8426d
4
- data.tar.gz: e7aa950deff8599ee64a2bc3884f62d3bc4d6b6f
3
+ metadata.gz: 1a55341596dc97fc2d39a498fd31bff55d587381
4
+ data.tar.gz: a846aed569db2f5ee058658da45bfcafc1eb8d90
5
5
  SHA512:
6
- metadata.gz: 2ec474c89f466cb9bbd92d1ebd85f2d38dfec59eddc952b8ad79450dd4694349595d6ce005d2398218f41fb3383509b2aeb19fda91883d103fa0aa1c3ff31125
7
- data.tar.gz: 3e6d58a8d8320b250ddf04bae3fc1b26d823b0c048d7365c78ba70fe3f62f43be88e2c936487e74d0499bb45e6466d38c7a1b77dc739c34e3d365ccdface780a
6
+ metadata.gz: f0e2c46b7c7b16499d9837e830df5eb58aef18af52d288ecd3f37b7f91f45e6571ac476fcb16c328847e1f237e99f4513750e54d78dbae2fb32d91b81358dd28
7
+ data.tar.gz: dcfd1ad3982aeaa3190b7356ead94529e3ec7d8b2f40502865d1e3d2198d16363115dd0a23d5f4c5166ffa552c8793aeb0dc9e8f497f0b9e1b46cfd7037116a5
data/.gitignore CHANGED
@@ -11,3 +11,7 @@ vendor
11
11
  pkg
12
12
  *.gem
13
13
  .ruby-version
14
+
15
+ # yard
16
+ .yardoc
17
+ doc
data/Rakefile CHANGED
@@ -17,47 +17,37 @@ task :build_server do
17
17
  raise <<EOF
18
18
  Unable to find calabash server checked out at #{dir}.
19
19
  Please checkout as #{dir} or set CALABASH_SERVER_PATH to point
20
- to Calabash server (branch 0.9.x).
20
+ to Calabash server (branch master).
21
21
  EOF
22
22
  end
23
23
 
24
24
  FileUtils.cd(dir) do
25
-
26
- pwd = FileUtils.pwd
27
- cmd = "#{pwd}/calabash/gitversioning-before.sh #{pwd}/calabash/LPGitVersionDefines.h"
28
- puts cmd
29
- `#{cmd}`
30
-
31
- puts 'Building Server'
32
- cmd = 'xcodebuild build -project calabash.xcodeproj -target Framework -configuration Debug -sdk iphonesimulator7.0'
33
- puts cmd
34
- puts `#{cmd}`
35
-
36
- cmd = "#{pwd}/calabash/gitversioning-after.sh #{pwd}/calabash/LPGitVersionDefines.h"
37
- puts cmd
38
- `#{cmd}`
39
-
25
+ puts 'INFO: building server...'
26
+ puts "INFO: $ cd #{File.expand_path(dir)} ; make"
27
+ `make`
40
28
  unless File.exist?(FRAMEWORK)
41
- raise 'Unable to build framework'
29
+ puts 'FAIL: unable to build calabash.framework'
30
+ puts "FAIL: run 'make' in #{File.expand_path(dir)} to diagnose"
31
+ raise
42
32
  end
43
33
 
44
- puts "Zipping down framework"
34
+ puts 'INFO: creating a zip archive of calabash.framework'
45
35
 
46
- zip_cmd = "zip -q -r #{ZIP_FILE} #{FRAMEWORK}"
47
- puts zip_cmd
48
- puts `#{zip_cmd}`
36
+ zip_cmd = "zip -y -q -r #{ZIP_FILE} #{FRAMEWORK}"
37
+ puts "INFO: $ cd #{File.expand_path(dir)} ; #{zip_cmd}"
38
+ `#{zip_cmd}`
49
39
  framework_zip = File.expand_path(ZIP_FILE)
50
40
  unless File.exist?(framework_zip)
41
+ puts 'FAIL: unable to create a zip archive of calabash.framework'
42
+ puts "FAIL: run '#{zip_cmd}' in #{File.expand_path(dir)} to diagnose"
51
43
  raise 'Unable to zip down framework...'
52
44
  end
53
45
  end
54
46
 
55
-
56
-
57
47
  FileUtils.mkdir_p('staticlib')
58
48
  output_path = File.join('staticlib', ZIP_FILE)
59
49
  FileUtils.mv(framework_zip,output_path, :force => true)
60
- puts "Server built to path #{output_path}"
50
+ puts "INFO: calabash.framework.zip installed in #{output_path}"
61
51
 
62
52
  end
63
53
 
@@ -7,9 +7,11 @@ def detect_accessibility_support
7
7
  dirs = Dir.glob(File.join(File.expand_path("~/Library"),"Application Support","iPhone Simulator","*.*","Library","Preferences"))
8
8
  dirs.each do |sim_pref_dir|
9
9
  fp = File.expand_path("#{sim_pref_dir}/com.apple.Accessibility.plist")
10
+ out = `defaults read "#{fp}" AXInspectorEnabled`
11
+ ax_inspector = out.split("\n")[0]=="0"
10
12
  out = `defaults read "#{fp}" ApplicationAccessibilityEnabled`
11
-
12
- if not(File.exists?(fp)) || out.split("\n")[0] == "0"
13
+ app_acc = out.split("\n")[0]=="0"
14
+ if not(File.exists?(fp)) || ax_inspector == "0" || app_acc == "0"
13
15
  msg("Warn") do
14
16
  puts "Accessibility is not enabled for simulator: #{sim_pref_dir}"
15
17
  puts "Enabled accessibility as described here:"
@@ -32,6 +34,7 @@ def calabash_setup(args)
32
34
  project_name, project_path, xpath = find_project_files(args)
33
35
  setup_project(project_name, project_path, xpath)
34
36
 
37
+ calabash_sim_accessibility
35
38
  detect_accessibility_support
36
39
 
37
40
  msg("Setup done") do
@@ -27,11 +27,23 @@ def calabash_sim_reset
27
27
  end
28
28
 
29
29
  def calabash_sim_accessibility
30
- dirs = Dir.glob(File.join(File.expand_path("~/Library"), "Application Support", "iPhone Simulator", "*.*", "Library", "Preferences"))
31
- dirs.each do |sim_pref_dir|
30
+ Calabash::Cucumber::SimulatorHelper.stop
31
+ old = ['5.*','6.*','7.0*'].map do |x|
32
+ Dir.glob(File.join(File.expand_path("~/Library"), "Application Support", "iPhone Simulator", "7.0*", "Library", "Preferences"))
33
+ end.flatten
34
+
35
+ rest = Dir.glob(File.join(File.expand_path("~/Library"), "Application Support", "iPhone Simulator", "*.*", "Library", "Preferences"))
36
+ rest = rest - old
37
+ (old+rest).each do |sim_pref_dir|
32
38
  fp = File.expand_path("#{@script_dir}/data/")
33
- FileUtils.cp("#{fp}/com.apple.Accessibility.plist", sim_pref_dir)
39
+ if rest.include?(sim_pref_dir)
40
+ tgt = 'com.apple.Accessibility-5.1.plist'
41
+ else
42
+ tgt = 'com.apple.Accessibility.plist'
43
+ end
44
+ FileUtils.cp("#{fp}/#{tgt}", File.join(sim_pref_dir, 'com.apple.Accessibility.plist'))
34
45
  end
46
+
35
47
  end
36
48
 
37
49
 
@@ -125,7 +137,7 @@ end
125
137
 
126
138
  def calabash_sim_device(args)
127
139
  quit_sim
128
- options = ["iPad","iPad_Retina", "iPhone", "iPhone_Retina", "iPhone_Retina_4inch"]
140
+ options = ["iPad", "iPad_Retina", "iPhone", "iPhone_Retina", "iPhone_Retina_4inch"]
129
141
  if args.length != 1 or not options.find { |x| x == args[0] }
130
142
  print_usage
131
143
  puts "Unrecognized args: #{args}"
@@ -21,12 +21,12 @@ Gem::Specification.new do |s|
21
21
  s.add_dependency( "json" )
22
22
  s.add_dependency( 'edn')
23
23
  s.add_dependency( "CFPropertyList" )
24
- s.add_dependency( "sim_launcher", "0.4.6")
24
+ s.add_dependency( "sim_launcher", "~> 0.4.9")
25
25
  s.add_dependency( "slowhandcuke" )
26
26
  s.add_dependency( "geocoder", "~>1.1.8")
27
27
  s.add_dependency( "httpclient","~> 2.3.3")
28
28
  s.add_dependency( "bundler", "~> 1.1")
29
- s.add_dependency( "run_loop", "~> 0.1.4" )
29
+ s.add_dependency( "run_loop", "~> 0.2.0.pre1" )
30
30
  s.add_dependency( "awesome_print")
31
31
  s.add_dependency( "xamarin-test-cloud", "~> 0.9.27")
32
32
 
@@ -131,7 +131,7 @@ class WordPressComPage < Calabash::IBase
131
131
 
132
132
  def login(user)
133
133
  touch("view marked:'Username'")
134
- await_keyboard
134
+ wait_for_keyboard
135
135
 
136
136
  keyboard_enter_text user[:email]
137
137
 
@@ -1,7 +1,9 @@
1
1
  require 'calabash-cucumber/core'
2
2
  require 'calabash-cucumber/tests_helpers'
3
3
  require 'calabash-cucumber/keyboard_helpers'
4
+ require 'calabash-cucumber/keychain_helpers'
4
5
  require 'calabash-cucumber/wait_helpers'
5
6
  require 'calabash-cucumber/operations'
6
7
  require 'calabash-cucumber/version'
7
8
  require 'calabash-cucumber/date_picker'
9
+ require 'calabash-cucumber/ipad_1x_2x'
@@ -160,12 +160,36 @@ module Calabash
160
160
  views_touched
161
161
  end
162
162
 
163
-
164
- def scroll_to_row_with_mark(row_id, options={:query => 'tableView',
163
+ # scrolls to +mark+ in a UITableView
164
+ #
165
+ # calls the +:scrollToRowWithMark+ server route
166
+ #
167
+ # scroll_to_row_with_mark(mark, {:scroll_position => :top}) #=> scroll to the top of the item with the given +mark+
168
+ # scroll_to_row_with_mark(mark, {:scroll_position => :bottom}) #=> scroll to the bottom of the item with the given +mark+
169
+ #
170
+ # allowed options
171
+ # :query => a query string
172
+ # default => 'tableView'
173
+ # example => "tableView marked:'hit songs'"
174
+ #
175
+ # :scroll_position => the position to scroll to
176
+ # default => :middle
177
+ # allowed => {:top | :middle | :bottom}
178
+ #
179
+ # :animate => animate the scrolling
180
+ # default => true
181
+ # allowed => {true | false}
182
+ #
183
+ # raises an exception if the scroll cannot be performed.
184
+ # * the +mark+ is nil
185
+ # * the +:query+ finds no table view
186
+ # * table view does not contain a cell with the given +mark+
187
+ # * +:scroll_position+ is invalid
188
+ def scroll_to_row_with_mark(mark, options={:query => 'tableView',
165
189
  :scroll_position => :middle,
166
190
  :animate => true})
167
- if row_id.nil?
168
- screenshot_and_raise 'row_id argument cannot be nil'
191
+ if mark.nil?
192
+ screenshot_and_raise 'mark argument cannot be nil'
169
193
  end
170
194
 
171
195
  uiquery = options[:query] || 'tableView'
@@ -180,7 +204,7 @@ module Calabash
180
204
  args << options[:animate]
181
205
  end
182
206
 
183
- views_touched=map(uiquery, :scrollToRowWithMark, row_id, *args)
207
+ views_touched=map(uiquery, :scrollToRowWithMark, mark, *args)
184
208
  msg = options[:failed_message] || "Unable to scroll: '#{uiquery}' to: #{options}"
185
209
  assert_map_results(views_touched, msg)
186
210
  views_touched
@@ -237,13 +261,70 @@ module Calabash
237
261
  if opts[:failed_message]
238
262
  msg = opts[:failed_message]
239
263
  else
240
- msg = "unable to scroll: '#{uiquery}' to row '#{item}' in section '#{section}'"
264
+ msg = "unable to scroll: '#{uiquery}' to item '#{item}' in section '#{section}'"
241
265
  end
242
266
 
243
267
  assert_map_results(views_touched, msg)
244
268
  views_touched
245
269
  end
246
270
 
271
+ # scrolls to +mark+ in a UICollectionView
272
+ #
273
+ # calls the +:collectionViewScrollToItemWithMark+ server route
274
+ #
275
+ # scroll_to_collection_view_item_with_mark(mark, {:scroll_position => :top}) #=> scroll to the top of the item with the given +mark+
276
+ # scroll_to_collection_view_item_with_mark(mark, {:scroll_position => :bottom}) #=> scroll to the bottom of the item with the given +mark+
277
+ #
278
+ # allowed options
279
+ # :query => a query string
280
+ # default => 'collectionView'
281
+ # example => "collectionView marked:'hit songs'"
282
+ #
283
+ # :scroll_position => the position to scroll to
284
+ # default => :top
285
+ # allowed => {:top | :center_vertical | :bottom | :left | :center_horizontal | :right}
286
+ #
287
+ # :animate => animate the scrolling
288
+ # default => true
289
+ # allowed => {true | false}
290
+ #
291
+ # :failed_message => the message to display on failure
292
+ # default => nil - will display a default failure message
293
+ # allowed => any string
294
+ #
295
+ # raises an exception if the scroll cannot be performed.
296
+ # * the +mark+ is nil
297
+ # * the +:query+ finds no collection view
298
+ # * collection view does not contain a cell with the given +mark+
299
+ # * +:scroll_position+ is invalid
300
+ def scroll_to_collection_view_item_with_mark(mark, opts={})
301
+ default_options = {:query => 'collectionView',
302
+ :scroll_position => :top,
303
+ :animate => true,
304
+ :failed_message => nil}
305
+ opts = default_options.merge(opts)
306
+ uiquery = opts[:query]
307
+
308
+ if mark.nil?
309
+ raise 'mark argument cannot be nil'
310
+ end
311
+
312
+ args = []
313
+ scroll_position = opts[:scroll_position]
314
+ candidates = [:top, :center_vertical, :bottom, :left, :center_horizontal, :right]
315
+ unless candidates.include?(scroll_position)
316
+ raise "scroll_position '#{scroll_position}' is not one of '#{candidates}'"
317
+ end
318
+
319
+ args << scroll_position
320
+ args << opts[:animate]
321
+
322
+ views_touched=map(uiquery, :collectionViewScrollToItemWithMark, mark, *args)
323
+ msg = opts[:failed_message] || "Unable to scroll: '#{uiquery}' to cell with mark: '#{mark}' with #{opts}"
324
+ assert_map_results(views_touched, msg)
325
+ views_touched
326
+ end
327
+
247
328
  def send_app_to_background(secs)
248
329
  launcher.actions.send_app_to_background(secs)
249
330
  end
@@ -18,6 +18,7 @@ module Calabash
18
18
  attr_reader :system
19
19
  attr_reader :framework_version
20
20
  attr_reader :iphone_app_emulated_on_ipad
21
+ attr_reader :iphone_4in
21
22
 
22
23
  attr_accessor :udid
23
24
 
@@ -30,6 +31,7 @@ module Calabash
30
31
  @ios_version = version_data['iOS_version']
31
32
  @framework_version = version_data['version']
32
33
  @iphone_app_emulated_on_ipad = version_data['iphone_app_emulated_on_ipad']
34
+ @iphone_4in = version_data['4inch']
33
35
  end
34
36
 
35
37
  def simulator?
@@ -52,12 +54,13 @@ module Calabash
52
54
  device_family.eql? GESTALT_IPAD
53
55
  end
54
56
 
57
+ def iphone_4in?
58
+ @iphone_4in
59
+ end
60
+
55
61
  def iphone_5?
56
- if simulator?
57
- !simulator_details.scan(GESTALT_IPHONE5).empty?
58
- else
59
- system.split(/[\D]/).delete_if { |x| x.eql?('') }.first.eql?('5')
60
- end
62
+ _deprecated('0.9.168', "use 'iphone_4in?' instead", :warn)
63
+ iphone_4in?
61
64
  end
62
65
 
63
66
  def version_hash (version_str)
@@ -85,7 +88,7 @@ module Calabash
85
88
 
86
89
  def screen_size
87
90
  return { :width => 768, :height => 1024 } if ipad?
88
- return { :width => 320, :height => 568 } if iphone_5?
91
+ return { :width => 320, :height => 568 } if iphone_4in?
89
92
  { :width => 320, :height => 480 }
90
93
  end
91
94
 
@@ -72,14 +72,15 @@ module Calabash
72
72
  #
73
73
  # raises an error if the server cannot be reached
74
74
  def iphone_5?
75
- _default_device_or_create().iphone_5?
75
+ _deprecated('0.9.168', "use 'iphone_4in?' instead", :warn)
76
+ iphone_4in?
76
77
  end
77
78
 
78
79
  # returns +true+ if the target device or simulator is a 4in model
79
80
  #
80
81
  # raises an error if the server cannot be reached
81
82
  def iphone_4in?
82
- iphone_5?
83
+ _default_device_or_create().iphone_4in?
83
84
  end
84
85
 
85
86
  # returns +true+ if the OS major version is 5
@@ -40,8 +40,6 @@ module Calabash
40
40
  end
41
41
 
42
42
  def make_http_request(options)
43
-
44
- body = nil
45
43
  retryable_errors = options[:retryable_errors] || RETRYABLE_ERRORS
46
44
  CAL_HTTP_RETRY_COUNT.times do |count|
47
45
  previous_debug_dev = nil
@@ -49,19 +47,16 @@ module Calabash
49
47
  if not @http
50
48
  @http = init_request(options)
51
49
  end
52
- #if options[:debug] || (ENV['DEBUG_HTTP'] == '1' && options[:debug] != false)
53
- # previous_debug_dev = @http.debug_dev
54
- # @http.debug_dev = $stdout
55
- #end
56
- if options[:method] == :post
57
- body = @http.post(options[:uri], options[:body]).body
50
+
51
+ response = if options[:method] == :post
52
+ @http.post(options[:uri], options[:body])
58
53
  else
59
- body = @http.get(options[:uri], options[:body]).body
54
+ @http.get(options[:uri], options[:body])
60
55
  end
61
- #if options[:debug] || (ENV['DEBUG_HTTP'] == '1' && options[:debug] != false)
62
- # @http.debug_dev = previous_debug_dev
63
- #end
64
- break
56
+
57
+ raise Errno::ECONNREFUSED if response.status_code == 502
58
+
59
+ return response.body
65
60
  rescue Exception => e
66
61
 
67
62
  if retryable_errors.include?(e) || retryable_errors.any? { |c| e.is_a?(c) }
@@ -85,8 +80,6 @@ module Calabash
85
80
  end
86
81
  end
87
82
  end
88
-
89
- body
90
83
  end
91
84
 
92
85
  def init_request(options={})
@@ -0,0 +1,190 @@
1
+ require 'calabash-cucumber/environment_helpers'
2
+ require 'calabash-cucumber/query_helpers'
3
+ require 'calabash-cucumber/http_helpers'
4
+ require 'calabash-cucumber/failure_helpers'
5
+ require 'calabash-cucumber/uia'
6
+
7
+ module Calabash
8
+ module Cucumber
9
+ module IPad
10
+
11
+ class Emulation
12
+ include Calabash::Cucumber::FailureHelpers
13
+ include Calabash::Cucumber::HTTPHelpers
14
+ include Calabash::Cucumber::QueryHelpers
15
+ include Calabash::Cucumber::UIA
16
+
17
+ # NOTE to maintainers - when adding a localization, please notice that
18
+ # the keys and values are semantically reversed.
19
+ #
20
+ # you should read the hash as:
21
+ #
22
+ # :emulated_1x #=> what button is showing when the app is emulated at 2X?
23
+ # :emulated_2x #=> what button is showing when the app is emulated at 1X?
24
+ IPAD_1X_2X_BUTTON_LABELS = {
25
+ :en => {:emulated_1x => '2X',
26
+ :emulated_2x => '1X'}
27
+ }
28
+
29
+ attr_reader :scale
30
+ attr_reader :lang_code
31
+
32
+ @button_names_hash = nil
33
+
34
+ def initialize (lang_code=:en)
35
+ @button_names_hash = IPAD_1X_2X_BUTTON_LABELS[lang_code]
36
+ if @button_names_hash.nil?
37
+ raise "could not find 1X/2X buttons for language code '#{lang_code}'"
38
+ end
39
+
40
+ @lang_code = lang_code
41
+ @scale = _internal_ipad_emulation_scale
42
+ end
43
+
44
+ def tap_ipad_scale_button
45
+ key = @scale
46
+ name = @button_names_hash[key]
47
+
48
+ res = uia_call_windows([:view, {:marked => "#{name}"}], :tap)
49
+
50
+ # ':nil' is a very strange success return value...
51
+ if res.is_a?(Hash) or res != ':nil'
52
+ screenshot_and_raise "could not touch scale button '#{name}' - '#{res['value']}'"
53
+ end
54
+ end
55
+
56
+ private
57
+ def _internal_ipad_emulation_scale
58
+ hash = @button_names_hash
59
+ val = nil
60
+ hash.values.each do |button_name|
61
+ res = uia_call_windows([:view, {:marked => "#{button_name}"}], :name)
62
+
63
+ if res == button_name
64
+ val = button_name
65
+ break
66
+ end
67
+ end
68
+
69
+ if val.nil?
70
+ raise "could not find iPad scale button with '#{hash.values}'"
71
+ end
72
+
73
+ if val == hash[:emulated_1x]
74
+ :emulated_1x
75
+ elsif val == hash[:emulated_2x]
76
+ :emulated_2x
77
+ else
78
+ raise "unrecognized emulation scale '#{val}'"
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ # ensures that iPhone apps emulated on an iPad are displayed at +scale+.
85
+ #
86
+ # starting in iOS 7, iPhone apps emulated on the iPad always launch at 2x.
87
+ # calabash cannot currently interact with such apps in 2x mode (trust us,
88
+ # we've tried).
89
+ #
90
+ # +scale+ must be one of { +:emulated_1x+ | +:emulated_2x+ }
91
+ #
92
+ # is it is recommended that clients call this convenience method:
93
+ #
94
+ # +ensure_ipad_emulation_1x+ #=> ensures the app is displayed in 1x mode
95
+ #
96
+ # takes these optional arguments
97
+ #
98
+ # :lang_code #=> a language code for matching the name of the 'scale' button
99
+ # :wait_after_touch #=> how long to wait after the 'scale' button is touched
100
+ #
101
+ # the default values are:
102
+ #
103
+ # :lang_code => :en
104
+ # :wait_after_touch => 0.4
105
+ #
106
+ # +IMPORTANT+ if this is not an iphone app emulated on a ipad, then calling
107
+ # this function has no effect.
108
+ #
109
+ # raises an exception if:
110
+ # * the app was +not+ launched with Instruments i.e. there is no <tt>run_loop</tt>
111
+ # * an invalid +scale+ is passed
112
+ # * an unknown language code is passed
113
+ # * the 'scale' button cannot be touched
114
+ def ensure_ipad_emulation_scale(scale, opts={})
115
+ return unless iphone_app_emulated_on_ipad?
116
+
117
+ unless uia_available?
118
+ raise 'this function requires the app be launched with instruments'
119
+ end
120
+
121
+ allowed = [:emulated_1x, :emulated_2x]
122
+ unless allowed.include?(scale)
123
+ raise "'#{scale}' is not one of '#{allowed}' allowed args"
124
+ end
125
+
126
+ default_opts = {:lang_code => :en,
127
+ :wait_after_touch => 0.4}
128
+ merged_opts = default_opts.merge(opts)
129
+
130
+ obj = Emulation.new(merged_opts[:lang_code])
131
+
132
+ actual_scale = obj.scale
133
+
134
+ if actual_scale != scale
135
+ obj.tap_ipad_scale_button
136
+ end
137
+
138
+ sleep(merged_opts[:wait_after_touch])
139
+
140
+ end
141
+
142
+ # ensures that iPhone apps emulated on an iPad are displayed at +1X+.
143
+ #
144
+ # here is an example of how to use this function in your +Before+ launch
145
+ # hooks:
146
+ #
147
+ # Before do |scenario|
148
+ # @calabash_launcher = Calabash::Cucumber::Launcher.new
149
+ # unless @calabash_launcher.calabash_no_launch?
150
+ # @calabash_launcher.relaunch
151
+ # @calabash_launcher.calabash_notify(self)
152
+ # # ensure emulated apps are at 1x
153
+ # ensure_ipad_emulation_1x
154
+ # end
155
+ # # do other stuff to prepare the test environment
156
+ # end
157
+ #
158
+ # takes these optional arguments
159
+ #
160
+ # :lang_code #=> a language code for matching the name of the 'scale' button
161
+ # :wait_after_touch #=> how long to wait after the 'scale' button is touched
162
+ #
163
+ # the default values are:
164
+ #
165
+ # :lang_code => :en
166
+ # :wait_after_touch => 0.4
167
+ #
168
+ # +IMPORTANT+ if this is not an iphone app emulated on a ipad, then calling
169
+ # this function has no effect.
170
+ #
171
+ # raises an exception if:
172
+ # * the app was +not+ launched with Instruments i.e. there is no <tt>run_loop</tt>
173
+ # * an unknown language code is passed
174
+ # * the 'scale' button cannot be touched
175
+ def ensure_ipad_emulation_1x(opts={})
176
+ ensure_ipad_emulation_scale(:emulated_1x, opts)
177
+ end
178
+
179
+ private
180
+ # ensures iPhone apps running on an iPad are emulated at 2X
181
+ #
182
+ # you should never need to call this function - calabash cannot interact
183
+ # with iPhone apps emulated on the iPad in 2x mode.
184
+ def _ensure_ipad_emulation_2x(opts={})
185
+ ensure_ipad_emulation_scale(:emulated_2x, opts)
186
+ end
187
+
188
+ end
189
+ end
190
+ end
@@ -278,6 +278,7 @@ module Calabash
278
278
  _ensure_can_enter_text
279
279
  if uia_available?
280
280
  text_before = _text_from_first_responder()
281
+ text_before = text_before.gsub("\n","\\n") if text_before
281
282
  uia_type_string(text, text_before)
282
283
  else
283
284
  text.each_char do |ch|
@@ -0,0 +1,149 @@
1
+ require 'calabash-cucumber/core'
2
+
3
+ module Calabash
4
+ module Cucumber
5
+ # KeychainHelpers provide a helpers to access the iOS keychain.
6
+ #
7
+ # == Simulator Note
8
+ #
9
+ # When running on the simulator, the keychain is *not* sandboxed between
10
+ # applications like it is on a real device. These methods will return
11
+ # keychain records from *all* applications on the simulator, which may
12
+ # result in strange behavior if you aren't expecting it.
13
+ #
14
+ # @see http://goo.gl/JrFJMM Details about why some operations report
15
+ # +FAILURE+ and what can be done on the client side mitigate
16
+ #
17
+ # @see https://github.com/soffes/sskeychain SSKeychain
18
+ module KeychainHelpers
19
+
20
+
21
+ # sends appropriately-configured +GET+ request to the +keychain+ server
22
+ # endpoint. do not call this function directly; use one of the helper
23
+ # functions provided.
24
+ #
25
+ # @see keychain_accounts
26
+ # @see keychain_account_for_service
27
+ # @return [Array<Hash>] contents of the iOS keychain
28
+ # @param [Hash] options
29
+ # @raise [RuntimeError] if http request does not report success
30
+ def _keychain_get(options={})
31
+ res = http({:method => :get, :raw => true, :path => 'keychain'}, options)
32
+ res = JSON.parse(res)
33
+ if res['outcome'] != 'SUCCESS'
34
+ raise "get keychain with options '#{options}' failed because: '#{res['reason']}'\n'#{res['details']}'"
35
+ end
36
+
37
+ res['results']
38
+ end
39
+
40
+ # asks the keychain for all of the account records
41
+ #
42
+ # The hash keys are defined by the +SSKeychain+ library.
43
+ #
44
+ # @see https://github.com/soffes/sskeychain SSKeychain
45
+ #
46
+ # The following keys are the most commonly useful:
47
+ #
48
+ # +svce+ #=> the service
49
+ # +acct+ #=> the account (often a username)
50
+ # +cdat+ #=> the creation date
51
+ # +mdat+ #=> the last-modified date
52
+ #
53
+ # @raise [RuntimeError] if http request does not report success
54
+ # @return [Array<Hash>] of all account records saved in the iOS keychain.
55
+ def keychain_accounts
56
+ _keychain_get
57
+ end
58
+
59
+ # @return [Array<Hash>] of all account records saved in the iOS keychain
60
+ # filtered by +service+.
61
+ #
62
+ # @see keychain_accounts
63
+ #
64
+ # @raise [RuntimeError] if http request does not report success
65
+ def keychain_accounts_for_service(service)
66
+ _keychain_get({:service => service})
67
+ end
68
+
69
+ # ask the keychain for an account password
70
+ #
71
+ # *IMPORTANT*
72
+ # On the XTC, the password cannot returned as plain text.
73
+ # When using this keychain_password in your steps you can condition on
74
+ # the XTC environment using +xamarin_test_cloud?+
75
+ #
76
+ # @see Calabash::Cucumber::EnvironmentHelpers
77
+ #
78
+ # @raise [RuntimeError] if http request does not report success
79
+ # @raise [RuntimeError] if +service+ and +account+ pair does not contain
80
+ # a password
81
+ #
82
+ # @return [String,Array<Hash>] password stored in keychain for +service+
83
+ # and +account+. *NB* on the XTC this returns an Array with one Hash
84
+ def keychain_password(service, account)
85
+ _keychain_get({:service => service, :account => account}).first
86
+ end
87
+
88
+ # sends appropriately-configured +POST+ request to the +keychain+ server
89
+ # endpoint. do not call this function directly; use one of the helper
90
+ # functions provided.
91
+ #
92
+ # @see keychain_clear
93
+ # @see keychain_clear_accounts_for_service
94
+ # @see keychain_delete_password
95
+ # @see keychain_set_password
96
+ #
97
+ # @return [nil]
98
+ # @raise [RuntimeError] if http request does not report success
99
+ def _keychain_post(options={})
100
+ raw = http({:method => :post, :path => 'keychain'}, options)
101
+ res = JSON.parse(raw)
102
+ if res['outcome'] != 'SUCCESS'
103
+ raise "post keychain with options '#{options}' failed because: #{res['reason']}\n#{res['details']}"
104
+ end
105
+ nil
106
+ end
107
+
108
+ # On the iOS Simulator this clears *all* keychain entries for *all*
109
+ # applications.
110
+ #
111
+ # On a physical device, this will clear all entries for the target
112
+ # application.
113
+ #
114
+ # @return [nil]
115
+ #
116
+ # @raise [RuntimeError] if http request does not report success
117
+ def keychain_clear
118
+ _keychain_post
119
+ end
120
+
121
+ # Clear all entries in the keychain restricted to a single +service+.
122
+ #
123
+ # @return [nil]
124
+ #
125
+ # @raise [RuntimeError] if http request does not report success
126
+ def keychain_clear_accounts_for_service(service)
127
+ _keychain_post({:service => service})
128
+ end
129
+
130
+ # Delete a single keychain record for the given +service+ and +account+
131
+ # pair.
132
+ #
133
+ # @raise [RuntimeError] if http request does not report success
134
+ def keychain_delete_password(service, account)
135
+ _keychain_post(:service => service, :account => account)
136
+ end
137
+
138
+ # Set the password for a given service and account pair.
139
+ #
140
+ # @return nil
141
+ #
142
+ # @raise [RuntimeError] if http request does not report success
143
+ def keychain_set_password(service, account, password)
144
+ _keychain_post(:service => service, :account => account, :password => password)
145
+ end
146
+
147
+ end
148
+ end
149
+ end
@@ -253,8 +253,12 @@ class Calabash::Cucumber::Launcher
253
253
  end
254
254
 
255
255
  def default_launch_method
256
- return :instruments unless sdk_version || use_instruments_env?
256
+ if RunLoop::Core.above_or_eql_version?('5.1', RunLoop::Core.xcode_version)
257
+ return use_sim_launcher_env? ? :sim_launcher : :instruments
258
+ end
259
+
257
260
  return :instruments if sdk_version.start_with?('7') # Only instruments supported for iOS7+
261
+
258
262
  sim_detector = SimLauncher::SdkDetector.new()
259
263
  available = sim_detector.available_sdk_versions.reject { |v| v.start_with?('7') }
260
264
  if available.include?(sdk_version)
@@ -379,10 +383,11 @@ class Calabash::Cucumber::Launcher
379
383
  rescue RunLoop::TimeoutError => e
380
384
  last_err = e
381
385
  if ENV['CALABASH_FULL_CONSOLE_OUTPUT'] == '1'
382
- puts "retrying run loop..."
386
+ puts 'retrying run loop...'
383
387
  end
384
388
  end
385
389
  end
390
+ Calabash::Cucumber::SimulatorHelper.stop
386
391
  raise StartError.new(last_err)
387
392
  end
388
393
 
@@ -499,6 +504,10 @@ class Calabash::Cucumber::Launcher
499
504
  ENV['LAUNCH_VIA'] == 'instruments'
500
505
  end
501
506
 
507
+ def use_sim_launcher_env?
508
+ ENV['LAUNCH_VIA'] == 'sim_launcher'
509
+ end
510
+
502
511
  def reset_between_scenarios?
503
512
  ENV['RESET_BETWEEN_SCENARIOS']=="1"
504
513
  end
@@ -1,6 +1,7 @@
1
1
  require 'calabash-cucumber/core'
2
2
  require 'calabash-cucumber/tests_helpers'
3
3
  require 'calabash-cucumber/keyboard_helpers'
4
+ require 'calabash-cucumber/keychain_helpers'
4
5
  require 'calabash-cucumber/wait_helpers'
5
6
  require 'calabash-cucumber/launcher'
6
7
  require 'net/http'
@@ -9,7 +10,7 @@ require 'json'
9
10
  require 'set'
10
11
  require 'calabash-cucumber/version'
11
12
  require 'calabash-cucumber/date_picker'
12
-
13
+ require 'calabash-cucumber/ipad_1x_2x'
13
14
 
14
15
  if not Object.const_defined?(:CALABASH_COUNT)
15
16
  #compatability with IRB
@@ -25,7 +26,9 @@ module Calabash
25
26
  include Calabash::Cucumber::TestsHelpers
26
27
  include Calabash::Cucumber::WaitHelpers
27
28
  include Calabash::Cucumber::KeyboardHelpers
29
+ include Calabash::Cucumber::KeychainHelpers
28
30
  include Calabash::Cucumber::DatePicker
31
+ include Calabash::Cucumber::IPad
29
32
 
30
33
  def page(clz,*args)
31
34
  clz.new(self,*args)
@@ -1,6 +1,6 @@
1
1
  module Calabash
2
2
  module Cucumber
3
- VERSION = '0.9.167'
4
- FRAMEWORK_VERSION = '0.9.167'
3
+ VERSION = '0.9.168.pre4'
4
+ FRAMEWORK_VERSION = '0.9.168'
5
5
  end
6
6
  end
@@ -14,13 +14,16 @@ module Calabash
14
14
  CALABASH_CONDITIONS = {:none_animating => "NONE_ANIMATING",
15
15
  :no_network_indicator => "NO_NETWORK_INDICATOR"}
16
16
 
17
-
18
- def wait_for(options_or_timeout=
19
- {:timeout => 30,
20
- :retry_frequency => 0.3,
21
- :post_timeout => 0,
22
- :timeout_message => 'Timed out waiting...',
23
- :screenshot_on_error => true}, &block)
17
+ # 'post_timeout' is the time to wait after a wait function returns true
18
+ DEFAULT_OPTS = {
19
+ :timeout => 30,
20
+ :retry_frequency => 0.3,
21
+ :post_timeout => 0,
22
+ :timeout_message => 'Timed out waiting...',
23
+ :screenshot_on_error => true
24
+ }.freeze
25
+
26
+ def wait_for(options_or_timeout=DEFAULT_OPTS, &block)
24
27
  #note Hash is preferred, number acceptable for backwards compat
25
28
  default_timeout = 30
26
29
  timeout = options_or_timeout || default_timeout
@@ -99,6 +102,9 @@ module Calabash
99
102
  end
100
103
  #options for wait_for apply
101
104
  def wait_for_elements_do_not_exist(elements_arr, options={})
105
+ if elements_arr.is_a?(String)
106
+ elements_arr = [elements_arr]
107
+ end
102
108
  options[:timeout_message] = options[:timeout_message] || "Timeout waiting for no elements matching: #{elements_arr.join(",")}"
103
109
  wait_for(options) do
104
110
  elements_arr.none? { |q| element_exists(q) }
@@ -192,6 +198,55 @@ module Calabash
192
198
  (msg.is_a?(String) ? WaitError.new(msg) : msg)
193
199
  end
194
200
 
201
+ # Performs a lambda action until the element (a query string) appears.
202
+ # The default action is to do nothing.
203
+ #
204
+ # Raises an error if no uiquery is specified. Same options as wait_for
205
+ # which are timeout, retry frequency, post_timeout, timeout_message, and
206
+ # screenshot on error.
207
+ #
208
+ # Example usage:
209
+ # until_element_exists("Button", :action => lambda { swipe("up") })
210
+ def until_element_exists(uiquery, opts = {})
211
+ extra_opts = { :until_exists => uiquery, :action => lambda { ; } }
212
+ opts = DEFAULT_OPTS.merge(extra_opts).merge(opts)
213
+ wait_poll(opts) do
214
+ opts[:action].call
215
+ end
216
+ end
217
+
218
+ # Performs a lambda action until the element (a query string) disappears.
219
+ # The default action is to do nothing.
220
+ #
221
+ # Raises an error if no uiquery is specified. Same options as wait_for
222
+ # which are timeout, retry frequency, post_timeout, timeout_message, and
223
+ # screenshot on error.
224
+ #
225
+ # Example usage:
226
+ # until_element_does_not_exist("Button", :action => lambda { swipe("up") })
227
+ def until_element_does_not_exist(uiquery, opts = {})
228
+ condition = lambda { element_exists(uiquery) ? false : true }
229
+ extra_opts = { :until => condition, :action => lambda { ; } }
230
+ opts = DEFAULT_OPTS.merge(extra_opts).merge(opts)
231
+ wait_poll(opts) do
232
+ opts[:action].call
233
+ end
234
+ end
235
+
236
+ # Performs a lambda action once the element exists.
237
+ # The default behavior is to touch the specified element.
238
+ #
239
+ # Raises an error if no uiquery is specified. Same options as wait_for
240
+ # which are timeout, retry frequency, post_timeout, timeout_message, and
241
+ # screenshot on error.
242
+ #
243
+ # Example usage: when_element_exists("Button", :timeout => 10)
244
+ def when_element_exists(uiquery, opts = {})
245
+ action = { :action => lambda { touch uiquery } }
246
+ opts = DEFAULT_OPTS.merge(action).merge(opts)
247
+ wait_for_elements_exist([uiquery], opts)
248
+ opts[:action].call
249
+ end
195
250
 
196
251
  end
197
252
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: calabash-cucumber
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.167
4
+ version: 0.9.168.pre4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karl Krukow
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-14 00:00:00.000000000 Z
11
+ date: 2014-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cucumber
@@ -84,16 +84,16 @@ dependencies:
84
84
  name: sim_launcher
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '='
87
+ - - ~>
88
88
  - !ruby/object:Gem::Version
89
- version: 0.4.6
89
+ version: 0.4.9
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '='
94
+ - - ~>
95
95
  - !ruby/object:Gem::Version
96
- version: 0.4.6
96
+ version: 0.4.9
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: slowhandcuke
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -156,14 +156,14 @@ dependencies:
156
156
  requirements:
157
157
  - - ~>
158
158
  - !ruby/object:Gem::Version
159
- version: 0.1.4
159
+ version: 0.2.0.pre1
160
160
  type: :runtime
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - ~>
165
165
  - !ruby/object:Gem::Version
166
- version: 0.1.4
166
+ version: 0.2.0.pre1
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: awesome_print
169
169
  requirement: !ruby/object:Gem::Requirement
@@ -240,7 +240,9 @@ files:
240
240
  - lib/calabash-cucumber/http_helpers.rb
241
241
  - lib/calabash-cucumber/ibase.rb
242
242
  - lib/calabash-cucumber/ios7_operations.rb
243
+ - lib/calabash-cucumber/ipad_1x_2x.rb
243
244
  - lib/calabash-cucumber/keyboard_helpers.rb
245
+ - lib/calabash-cucumber/keychain_helpers.rb
244
246
  - lib/calabash-cucumber/launch/simulator_helper.rb
245
247
  - lib/calabash-cucumber/launcher.rb
246
248
  - lib/calabash-cucumber/map.rb
@@ -363,6 +365,7 @@ files:
363
365
  - scripts/EmptyAppHack.app/en.lproj/InfoPlist.strings
364
366
  - scripts/data/.GlobalPreferences.plist
365
367
  - scripts/data/clients.plist
368
+ - scripts/data/com.apple.Accessibility-5.1.plist
366
369
  - scripts/data/com.apple.Accessibility.plist
367
370
  - scripts/launch.rb
368
371
  - scripts/reset_simulator.scpt
@@ -381,9 +384,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
381
384
  version: '0'
382
385
  required_rubygems_version: !ruby/object:Gem::Requirement
383
386
  requirements:
384
- - - '>='
387
+ - - '>'
385
388
  - !ruby/object:Gem::Version
386
- version: '0'
389
+ version: 1.3.1
387
390
  requirements: []
388
391
  rubyforge_project:
389
392
  rubygems_version: 2.0.3