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 +4 -4
- data/.gitignore +4 -0
- data/Rakefile +14 -24
- data/bin/calabash-ios-setup.rb +5 -2
- data/bin/calabash-ios-sim.rb +16 -4
- data/calabash-cucumber.gemspec +2 -2
- data/doc/x-platform-testing.md +1 -1
- data/lib/calabash-cucumber.rb +2 -0
- data/lib/calabash-cucumber/core.rb +87 -6
- data/lib/calabash-cucumber/device.rb +9 -6
- data/lib/calabash-cucumber/environment_helpers.rb +3 -2
- data/lib/calabash-cucumber/http_helpers.rb +8 -15
- data/lib/calabash-cucumber/ipad_1x_2x.rb +190 -0
- data/lib/calabash-cucumber/keyboard_helpers.rb +1 -0
- data/lib/calabash-cucumber/keychain_helpers.rb +149 -0
- data/lib/calabash-cucumber/launcher.rb +11 -2
- data/lib/calabash-cucumber/operations.rb +4 -1
- data/lib/calabash-cucumber/version.rb +2 -2
- data/lib/calabash-cucumber/wait_helpers.rb +62 -7
- data/scripts/data/com.apple.Accessibility-5.1.plist +0 -0
- metadata +13 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a55341596dc97fc2d39a498fd31bff55d587381
|
4
|
+
data.tar.gz: a846aed569db2f5ee058658da45bfcafc1eb8d90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f0e2c46b7c7b16499d9837e830df5eb58aef18af52d288ecd3f37b7f91f45e6571ac476fcb16c328847e1f237e99f4513750e54d78dbae2fb32d91b81358dd28
|
7
|
+
data.tar.gz: dcfd1ad3982aeaa3190b7356ead94529e3ec7d8b2f40502865d1e3d2198d16363115dd0a23d5f4c5166ffa552c8793aeb0dc9e8f497f0b9e1b46cfd7037116a5
|
data/.gitignore
CHANGED
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
|
20
|
+
to Calabash server (branch master).
|
21
21
|
EOF
|
22
22
|
end
|
23
23
|
|
24
24
|
FileUtils.cd(dir) do
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
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
|
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
|
-
|
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 "
|
50
|
+
puts "INFO: calabash.framework.zip installed in #{output_path}"
|
61
51
|
|
62
52
|
end
|
63
53
|
|
data/bin/calabash-ios-setup.rb
CHANGED
@@ -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)) ||
|
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
|
data/bin/calabash-ios-sim.rb
CHANGED
@@ -27,11 +27,23 @@ def calabash_sim_reset
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def calabash_sim_accessibility
|
30
|
-
|
31
|
-
|
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
|
-
|
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}"
|
data/calabash-cucumber.gemspec
CHANGED
@@ -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.
|
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.
|
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
|
|
data/doc/x-platform-testing.md
CHANGED
data/lib/calabash-cucumber.rb
CHANGED
@@ -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
|
-
|
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
|
168
|
-
screenshot_and_raise '
|
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,
|
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
|
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
|
-
|
57
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
54
|
+
@http.get(options[:uri], options[:body])
|
60
55
|
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
@@ -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
|
-
|
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
|
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)
|
@@ -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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
Binary file
|
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.
|
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-
|
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.
|
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.
|
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.
|
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.
|
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:
|
389
|
+
version: 1.3.1
|
387
390
|
requirements: []
|
388
391
|
rubyforge_project:
|
389
392
|
rubygems_version: 2.0.3
|