calabash-cucumber 0.16.4 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/bin/calabash-ios-generate.rb +61 -10
  3. data/dylibs/libCalabashDyn.dylib +0 -0
  4. data/dylibs/libCalabashDynSim.dylib +0 -0
  5. data/features-skeleton/sample.feature +7 -0
  6. data/features-skeleton/steps/sample_steps.rb +45 -0
  7. data/features-skeleton/support/01_launch.rb +31 -33
  8. data/features-skeleton/support/dry_run.rb +2 -0
  9. data/features-skeleton/support/env.rb +1 -0
  10. data/features-skeleton/support/patches/cucumber.rb +15 -0
  11. data/lib/calabash-cucumber.rb +4 -0
  12. data/lib/calabash-cucumber/actions/instruments_actions.rb +0 -5
  13. data/lib/calabash-cucumber/actions/playback_actions.rb +1 -6
  14. data/lib/calabash-cucumber/connection_helpers.rb +50 -1
  15. data/lib/calabash-cucumber/core.rb +94 -72
  16. data/lib/calabash-cucumber/dot_dir.rb +18 -0
  17. data/lib/calabash-cucumber/launch/simulator_launcher.rb +35 -3
  18. data/lib/calabash-cucumber/launcher.rb +46 -99
  19. data/lib/calabash-cucumber/logging.rb +79 -0
  20. data/lib/calabash-cucumber/playback_helpers.rb +2 -2
  21. data/lib/calabash-cucumber/store/preferences.rb +223 -0
  22. data/lib/calabash-cucumber/uia.rb +0 -43
  23. data/lib/calabash-cucumber/usage_tracker.rb +192 -0
  24. data/lib/calabash-cucumber/version.rb +2 -2
  25. data/lib/calabash-cucumber/wait_helpers.rb +32 -0
  26. data/scripts/.irbrc +19 -4
  27. data/staticlib/calabash.framework.zip +0 -0
  28. data/staticlib/libFrankCalabash.a +0 -0
  29. metadata +31 -19
  30. data/features-skeleton/my_first.feature +0 -12
  31. data/features-skeleton/step_definitions/calabash_steps.rb +0 -1
  32. data/features-skeleton/step_definitions/my_first_steps.rb +0 -4
  33. data/features-skeleton/support/02_pre_stop_hooks.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 016c8a3a27e8b446b227901e7f276da121de8ea7
4
- data.tar.gz: 86aeca1d34e5697bcc0de385ef2d9fc2c9814807
3
+ metadata.gz: 21ca53be02da9a57cb4baffd965a8ce32d959a80
4
+ data.tar.gz: 30ff3a6acafdca9a4d45b5855418e37d122a0cc0
5
5
  SHA512:
6
- metadata.gz: 57b0a72c1d5e1594105c4b9dbe8807ed98cb3b5e0dfb85df19e7ea3e018555971549b58e1353d9524addef4a7c49a38150507335178361a13876059bd32c853d
7
- data.tar.gz: 962e4026c8acc4a669f0ee029c3661ea402aff169e0026357f09cb6448579bc8e874dfb0c4f44699ddd0d428622bf3643ed1e37ffe830dd5843b2c4c922bae17
6
+ metadata.gz: b20587099e1b708a242899de712604b6e675768a453a09f91ba5f5dd4b4a4e088c72343ff6577b40a945c9eaca58fd3ef54af5a2711af20c16090844ae353629
7
+ data.tar.gz: 2b532a1873a5b4a56c141b363b0e9acb46b6012b228fe34f19afb78ab40066444df869ced45d35ffd300e5b90e604729ded46827441546cc6bf79b3a1a8d9d82
@@ -2,22 +2,73 @@
2
2
  def calabash_scaffold
3
3
  if File.exists?(@features_dir)
4
4
  puts "A features directory already exists. Stopping..."
5
- #puts "Run calabash-ios update for update instructions."
6
5
  exit 1
7
6
  end
7
+
8
8
  msg("Question") do
9
- puts "I'm about to create a subdirectory called features."
10
- puts "features will contain all your calabash tests."
11
- puts "Please hit return to confirm that's what you want."
9
+ puts "I'm about to create a features directory here:"
10
+ puts ""
11
+ puts "#{ENV["PWD"]}/features"
12
+ puts ""
13
+ puts "This directory will contain all of your calabash tests."
14
+ puts "Shall I proceed? (Y/n)"
12
15
  end
13
- exit 2 unless STDIN.gets.chomp == ''
14
16
 
15
- FileUtils.cp_r(@source_dir, @features_dir)
17
+ response = STDIN.gets.chomp.downcase
18
+ proceed = response == "" || response == "y"
19
+
20
+ puts ""
16
21
 
17
- msg("Info") do
18
- puts "Features subdirectory created. \n"
19
- puts "Make sure you've build your -cal scheme in XCode and "
20
- puts "try executing \n\ncucumber"
22
+ if !proceed
23
+ puts "Skipping installation of features/ directory"
24
+ else
25
+ FileUtils.cp_r(@source_dir, @features_dir)
26
+ puts "Created: #{ENV["PWD"]}/features"
27
+ puts ""
21
28
  end
22
29
 
30
+ gemfile = File.join(ENV["PWD"], "Gemfile")
31
+
32
+ version = Calabash::Cucumber::VERSION
33
+ gemline = "gem \"calabash-cucumber\", \">= #{version}\", \"< 2.0\"\n"
34
+
35
+ if File.exist?(gemfile)
36
+ printf "Found a Gemfile..."
37
+ contents = File.read(gemfile).force_encoding('utf-8')
38
+ if contents[/calabash-cucumber/, 0]
39
+ puts "and it contains calabash-cucumber!"
40
+ puts ""
41
+ else
42
+ puts "but it doesn't contain calabash-cucumber!"
43
+ puts "You'll have to add it yourself"
44
+ puts ""
45
+ puts "> #{gemline}"
46
+ puts ""
47
+ end
48
+ else
49
+ msg("Question") do
50
+ puts "I want to create a Gemfile for you."
51
+ puts "Shall I proceed? (Y/n)"
52
+
53
+ response = STDIN.gets.chomp.downcase
54
+ proceed = response == "" || response == "y"
55
+
56
+ puts ""
57
+
58
+ if !proceed
59
+ puts "Skipping installation of Gemfile"
60
+ else
61
+ File.open(gemfile, "w") do |file|
62
+ file.write("source \"https://rubygems.org\"\n")
63
+ file.write("\n")
64
+ file.write(gemline)
65
+ file.write("\n")
66
+ end
67
+ puts "Created: #{gemfile}"
68
+ puts""
69
+ end
70
+ end
71
+ end
72
+ puts "My work is done here."
23
73
  end
74
+
Binary file
@@ -0,0 +1,7 @@
1
+ Feature: Sample Feature
2
+
3
+ Scenario: Sample Scenario
4
+ Given the app has launched
5
+ And I have done a specific thing
6
+ When I do something
7
+ Then something should happen
@@ -0,0 +1,45 @@
1
+ Given(/^the app has launched$/) do
2
+ wait_for do
3
+ !query("*").empty?
4
+ end
5
+ end
6
+
7
+ And(/^I have done a specific thing$/) do
8
+ # Example: Given I am logged in
9
+ # wait_for do
10
+ # !query("* marked:'username'").empty?
11
+ # end
12
+ #
13
+ # touch("* marked:'username'")
14
+ # wait_for_keyboard
15
+ # keyboard_enter_text("cleveruser27")
16
+ #
17
+ # touch("* marked:'password'")
18
+ # wait_for_keyboard
19
+ # keyboard_enter_text("pa$$w0rd")
20
+ #
21
+ # wait_for_element_exists("* marked:'Login'")
22
+ # touch("* marked:'Login'")
23
+
24
+ # Remember: any Ruby is allowed in your step definitions
25
+ did_something = true
26
+
27
+ unless did_something
28
+ fail 'Expected to have done something'
29
+ end
30
+ end
31
+
32
+ When(/^I do something$/) do
33
+ # Example: When I create a new entry
34
+ # tap("* marked:'new_entry'")
35
+ # wait_for_keyboard
36
+ # keyboard_enter_text("* marked:'entry_title'", 'My Entry')
37
+ #
38
+ # tap("* marked:'submit'")
39
+ end
40
+
41
+ Then(/^something should happen$/) do
42
+ # Example: Then I should see the entry on my home page
43
+ # wait_for_element_exists("* text:'My Entry'")
44
+ end
45
+
@@ -1,46 +1,44 @@
1
- ########################################
2
- # #
3
- # Important Note #
4
- # #
5
- # When running calabash-ios tests at #
6
- # www.xamarin.com/test-cloud #
7
- # the methods invoked by #
8
- # CalabashLauncher are overridden. #
9
- # It will automatically ensure #
10
- # running on device, installing apps #
11
- # etc. #
12
- # #
13
- ########################################
14
-
15
1
  require 'calabash-cucumber/launcher'
16
2
 
3
+ # You can find examples of more complicated launch hooks in these
4
+ # two repositories:
5
+ #
6
+ # https://github.com/calabash/ios-smoke-test-app/blob/master/CalSmokeApp/features/support/01_launch.rb
7
+ # https://github.com/calabash/ios-webview-test-app/blob/master/CalWebViewApp/features/support/01_launch.rb
17
8
 
18
- # APP_BUNDLE_PATH = "#{ENV['HOME']}/Library/Developer/Xcode/DerivedData/??/Build/Products/Calabash-iphonesimulator/??.app"
19
- # You may uncomment the above to overwrite the APP_BUNDLE_PATH
20
- # However the recommended approach is to let Calabash find the app itself
21
- # or set the environment variable APP_BUNDLE_PATH
9
+ module Calabash::Launcher
10
+ @@launcher = nil
22
11
 
12
+ def self.launcher
13
+ @@launcher ||= Calabash::Cucumber::Launcher.new
14
+ end
23
15
 
24
- Before do |scenario|
25
- @calabash_launcher = Calabash::Cucumber::Launcher.new
26
- unless @calabash_launcher.calabash_no_launch?
27
- @calabash_launcher.relaunch
28
- @calabash_launcher.calabash_notify(self)
16
+ def self.launcher=(launcher)
17
+ @@launcher = launcher
29
18
  end
30
19
  end
31
20
 
21
+ Before do |scenario|
22
+ launcher = Calabash::Launcher.launcher
23
+ options = {
24
+ # Add launch options here.
25
+ }
26
+
27
+ launcher.relaunch(options)
28
+ launcher.calabash_notify(self)
29
+ end
30
+
32
31
  After do |scenario|
33
- unless @calabash_launcher.calabash_no_stop?
32
+ # Calabash can shutdown the app cleanly by calling the app life cycle methods
33
+ # in the UIApplicationDelegate. This is really nice for CI environments, but
34
+ # not so good for local development.
35
+ #
36
+ # See the documentation for NO_STOP for a nice debugging workflow
37
+ #
38
+ # http://calabashapi.xamarin.com/ios/file.ENVIRONMENT_VARIABLES.html#label-NO_STOP
39
+ # http://calabashapi.xamarin.com/ios/Calabash/Cucumber/Core.html#console_attach-instance_method
40
+ unless launcher.calabash_no_stop?
34
41
  calabash_exit
35
- if @calabash_launcher.active?
36
- @calabash_launcher.stop
37
- end
38
42
  end
39
43
  end
40
44
 
41
- at_exit do
42
- launcher = Calabash::Cucumber::Launcher.new
43
- if launcher.simulator_target?
44
- launcher.simulator_launcher.stop unless launcher.calabash_no_stop?
45
- end
46
- end
@@ -0,0 +1,2 @@
1
+ require 'calabash-cucumber'
2
+
@@ -6,3 +6,4 @@ require 'calabash-cucumber/cucumber'
6
6
  # require 'calabash-cucumber/wait_helpers'
7
7
  # require 'calabash-cucumber/operations'
8
8
  # World(Calabash::Cucumber::Operations)
9
+
@@ -0,0 +1,15 @@
1
+ # override cucumber pending on the XTC
2
+ if ENV['XAMARIN_TEST_CLOUD'] == '1'
3
+ module Cucumber
4
+ module RbSupport
5
+ def pending(message = 'TODO')
6
+ raise "PENDING: #{message}"
7
+ end
8
+
9
+ def ask(message, _)
10
+ raise "Cannot ask: '#{message}'; User interaction is not allowed on the XTC"
11
+ end
12
+ end
13
+ end
14
+ World(Cucumber::RbSupport)
15
+ end
@@ -1,3 +1,7 @@
1
+ require "calabash-cucumber/logging"
2
+ require "calabash-cucumber/dot_dir"
3
+ require "calabash-cucumber/store/preferences"
4
+ require "calabash-cucumber/usage_tracker.rb"
1
5
  require 'calabash-cucumber/core'
2
6
  require 'calabash-cucumber/tests_helpers'
3
7
  require 'calabash-cucumber/keyboard_helpers'
@@ -15,11 +15,6 @@ class Calabash::Cucumber::InstrumentsActions
15
15
  query_action(options, :uia_tap_offset)
16
16
  end
17
17
 
18
- # @!visibility private
19
- def wait_tap(options)
20
- uia_wait_tap(options[:query], options)
21
- end
22
-
23
18
  # @!visibility private
24
19
  def double_tap(options)
25
20
  query_action(options, :uia_double_tap_offset)
@@ -13,11 +13,6 @@ class Calabash::Cucumber::PlaybackActions
13
13
  playback('touch', options)
14
14
  end
15
15
 
16
- # @!visibility private
17
- def wait_tap(options)
18
- touch(options)
19
- end
20
-
21
16
  # @!visibility private
22
17
  def double_tap(options)
23
18
  playback('double_tap', options)
@@ -111,4 +106,4 @@ class Calabash::Cucumber::PlaybackActions
111
106
  def error_message(gesture)
112
107
  "Gesture: '#{gesture}' not supported unless running with instruments."
113
108
  end
114
- end
109
+ end
@@ -3,6 +3,8 @@ require 'calabash-cucumber/connection'
3
3
  module Calabash
4
4
  module Cucumber
5
5
 
6
+ class ResponseError < RuntimeError ; end
7
+
6
8
  # @!visibility private
7
9
  module ConnectionHelpers
8
10
 
@@ -16,6 +18,53 @@ module Calabash
16
18
  Calabash::Cucumber::Connection.instance
17
19
  end
18
20
 
21
+ # @!visibility private
22
+ def response_body_to_hash(body)
23
+ if body.nil? || body == ""
24
+ raise ResponseError,
25
+ "Server replied with an empty response. Your app has probably crashed"
26
+ end
27
+
28
+ begin
29
+ hash = JSON.parse(body)
30
+ rescue TypeError, JSON::ParserError => e
31
+ raise ResponseError,
32
+ %Q{Could not parse server response '#{body}':
33
+
34
+ #{e}
35
+
36
+ This usually means your app has crashed.
37
+ }
38
+ end
39
+
40
+ outcome = hash['outcome']
41
+
42
+ case outcome
43
+ when 'FAILURE'
44
+ reason = hash['reason']
45
+ if reason.nil? || reason.empty?
46
+ hash['reason'] = 'Server provided no reason.'
47
+ end
48
+
49
+ details = hash['details']
50
+ if details.nil? || details.empty?
51
+ hash['details'] = 'Server provided no details.'
52
+ end
53
+
54
+ when 'SUCCESS'
55
+ if !hash.has_key?('results')
56
+ raise ResponseError,
57
+ %Q{Server responded with '#{outcome}'
58
+ but response #{hash} does not contain 'results' key
59
+ }
60
+ end
61
+ else
62
+ raise ResponseError,
63
+ %Q{Server responded with an invalid outcome: '#{hash["outcome"]}'}
64
+ end
65
+ hash
66
+ end
67
+
19
68
  end
20
69
  end
21
- end
70
+ end
@@ -161,7 +161,7 @@ module Calabash
161
161
  # If the view is not visible `touch` will fail. If the view is animating
162
162
  # `touch` will *silently* fail.
163
163
  # By default, taps the center of the view.
164
- # @see #wait_tap
164
+ # @see Calabash::Cucumber::WaitHelpers#wait_tap
165
165
  # @see Calabash::Cucumber::Operations#tap_mark
166
166
  # @see #tap_point
167
167
  # @param {String} uiquery query describing view to tap. Note `nil` is allowed and is interpreted as
@@ -178,7 +178,7 @@ module Calabash
178
178
  end
179
179
 
180
180
  # Performs the `tap` gesture on an absolute coordinate.
181
- # @see #wait_tap
181
+ # @see Calabash::Cucumber::WaitHelpers#wait_tap
182
182
  # @see Calabash::Cucumber::Operations#tap_mark
183
183
  # @see #touch
184
184
  # @param {Numeric} x x-coordinate to tap
@@ -188,33 +188,6 @@ module Calabash
188
188
  touch(nil, offset: {x:x, y:y})
189
189
  end
190
190
 
191
- # Performs the `tap` gesture on the (first) view that matches query `uiquery`.
192
- #
193
- # As opposed to `touch`, `wait_tap` is a high-level method that combines:
194
- #
195
- # 1. waiting for the view to appear,
196
- # 2. waiting for animations to complete on the view (and it's parents) and
197
- # 3. actually tapping the view.
198
- #
199
- # This removes the common boiler-plate trio: `wait_for_element_exists`,
200
- # `wait_for_none_animating`, `touch`.
201
- #
202
- # By default, taps the center of the view.
203
- # @see #touch
204
- # @see #tap_point
205
- # @param {String} uiquery query describing view to tap. Note `nil` is not allowed.
206
- # @param {Hash} options option for modifying the details of the touch
207
- # @option options {Hash} :offset (nil) optional offset to tap point. Offset has an `:x` and `:y` key
208
- # the tap will be performed on the center of the view plus the offset.
209
- # @option options {Hash} :timeout (30) maximum number of seconds to wait for the view to appear
210
- # @option options {Hash} :frequency (0.2) polling frequency to for checking if the view is present (>= 0.1)
211
- # @return {Array<Hash>} serialized version of the tapped view
212
- def wait_tap(uiquery, options={})
213
- # noinspection RubyUnusedLocalVariable
214
- _uiquery, options = extract_query_and_options(uiquery, options)
215
- launcher.actions.wait_tap(options)
216
- end
217
-
218
191
  # Performs the "double tap" gesture on the (first) view that matches query `uiquery`.
219
192
  #
220
193
  # @note This assumes the view is visible and not animating.
@@ -653,11 +626,67 @@ module Calabash
653
626
  views_touched
654
627
  end
655
628
 
656
- # Sends app to background. Simulates pressing the home button.
657
- # @param {Fixnum} secs number of seconds to be in the background
658
- # `should not be more than 60 secs`
659
- def send_app_to_background(secs)
660
- launcher.actions.send_app_to_background(secs)
629
+ # Sends the app to the background.
630
+ #
631
+ # Sending the app to the background for more than 60 seconds may
632
+ # cause unpredicatable results.
633
+ #
634
+ # @param [Numeric] seconds How long to send the app to the background.
635
+ # @raise [ArgumentError] if `seconds` argument is < 1.0
636
+ def send_app_to_background(seconds)
637
+ if seconds < 1.0
638
+ raise ArgumentError, "Seconds '#{seconds}' must be >= 1.0"
639
+ end
640
+
641
+ parameters = {
642
+ :duration => seconds
643
+ }
644
+
645
+ begin
646
+ body = http({:method => :post, :path => "suspend"}, parameters)
647
+ result = response_body_to_hash(body)
648
+ rescue RuntimeError => e
649
+ raise RuntimeError, e
650
+ end
651
+
652
+ if result["outcome"] != "SUCCESS"
653
+ raise RuntimeError,
654
+ %Q{Could not send app to background:
655
+ reason => '#{result["reason"]}'
656
+ details => '#{result["details"]}'
657
+ }
658
+ end
659
+ result["results"]
660
+ end
661
+
662
+ # Cause the device to shake.
663
+ #
664
+ # @param [Numeric] seconds How long to shake the device
665
+ # @raise [ArgumentError] if `seconds` argument is <= 0.0
666
+ def shake(seconds)
667
+ if seconds <= 0.0
668
+ raise ArgumentError, "Seconds '#{seconds}' must be >= 0.0"
669
+ end
670
+
671
+ parameters = {
672
+ :duration => seconds
673
+ }
674
+
675
+ begin
676
+ body = http({:method => :post, :path => "shake"}, parameters)
677
+ result = response_body_to_hash(body)
678
+ rescue RuntimeError => e
679
+ raise RuntimeError, e
680
+ end
681
+
682
+ if result["outcome"] != "SUCCESS"
683
+ raise RuntimeError,
684
+ %Q{Could not shake the device:
685
+ reason => '#{result["reason"]}'
686
+ details => '#{result["details"]}'
687
+ }
688
+ end
689
+ result["results"]
661
690
  end
662
691
 
663
692
  # Simulates gps location of the device/simulator.
@@ -798,50 +827,43 @@ module Calabash
798
827
 
799
828
  # Calls a method on the app's AppDelegate object.
800
829
  #
801
- # This is an escape hatch for calling an arbitrary hook inside
802
- # (the test build) of your app. Commonly used to "go around" the UI for
803
- # speed purposes or reset the app to a good known state.
830
+ # Use this to call an arbitrary Objective-C or Swift method in your
831
+ # app's UIApplicationDelegate.
804
832
  #
805
- # You must create a method on you app delegate of the form:
833
+ # Commonly used to "go around" the UI speed purposes or reset the app to
834
+ # a good known state.
806
835
  #
807
- # - (NSString *) calabashBackdoor:(NSString *)aIgnorable;
836
+ # @note For methods that take arguments, don't forget to include the
837
+ # trailing ":"
808
838
  #
809
- # or if you want to pass parameters
810
- #
811
- # - (NSString *) calabashBackdoor:(NSDictionary *)params;
812
- # @example
813
- # backdoor("calabashBackdoor:", '')
814
- # @example
815
- # backdoor("calabashBackdoor:", {example:'param'})
816
- # @param {String} selector the selector to perform on the app delegate
817
- # @param {Object} argument the argument to pass to the selector
818
- # @return {Object} the result of performing the selector with the argument (serialized)
819
- def backdoor(selector, argument)
820
-
821
- unless selector.end_with?(':')
822
- messages =
823
- [
824
- "Selector '#{selector}' is missing a trailing ':'",
825
- 'Valid backdoor selectors must take one argument.',
826
- "Before 0.15.0, the server will append a trailing ':'.",
827
- ' After 0.15.0, this behavior is scheduled to change.',
828
- '',
829
- 'http://developer.xamarin.com/guides/testcloud/calabash/working-with/backdoors/#backdoor_in_iOS',
830
- ''
831
- ]
832
- _deprecated('0.15.0', messages.join("\n"), :warn)
833
- end
834
-
835
- json = {
839
+ # @param [String] selector the selector to perform on the app delegate
840
+ # @param [Object] argument the argument to pass to the selector
841
+ # @return [Object] the result of performing the selector with the argument
842
+ def backdoor(selector, *arguments)
843
+ parameters = {
836
844
  :selector => selector,
837
- :arg => argument
845
+ :arguments => arguments
838
846
  }
839
- res = http({:method => :post, :path => 'backdoor'}, json)
840
- res = JSON.parse(res)
841
- if res['outcome'] != 'SUCCESS'
842
- screenshot_and_raise "backdoor #{json} failed because:\n\n#{res['reason']}\n#{res['details']}"
847
+
848
+ begin
849
+ body = http({:method => :post, :path => "backdoor"}, parameters)
850
+ result = response_body_to_hash(body)
851
+ rescue RuntimeError => e
852
+ raise RuntimeError, e
853
+ end
854
+
855
+ if result["outcome"] != "SUCCESS"
856
+ raise RuntimeError,
857
+ %Q{backdoor call failed:
858
+ selector => '#{selector}'
859
+ arguments => '#{arguments}'
860
+ reason => '#{result["reason"]}'
861
+
862
+ #{result["details"]}
863
+
864
+ }
843
865
  end
844
- res['result']
866
+ result["results"]
845
867
  end
846
868
 
847
869
  # Attempts to shut the app down gracefully by simulating the transition