calabash-cucumber 0.16.4 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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