calabash 1.9.9.pre3 → 2.0.0.prelegacy

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -33
  3. data/bin/calabash +45 -36
  4. data/lib/calabash.rb +137 -13
  5. data/lib/calabash/android.rb +6 -0
  6. data/lib/calabash/android/adb.rb +25 -1
  7. data/lib/calabash/android/application.rb +14 -3
  8. data/lib/calabash/android/build/builder.rb +16 -3
  9. data/lib/calabash/android/build/java_keystore.rb +10 -0
  10. data/lib/calabash/android/build/resigner.rb +23 -1
  11. data/lib/calabash/android/build/test_server.rb +2 -0
  12. data/lib/calabash/android/defaults.rb +1 -0
  13. data/lib/calabash/android/device.rb +55 -3
  14. data/lib/calabash/android/environment.rb +10 -0
  15. data/lib/calabash/android/interactions.rb +106 -3
  16. data/lib/calabash/android/legacy.rb +143 -0
  17. data/lib/calabash/android/lib/TestServer.apk +0 -0
  18. data/lib/calabash/android/life_cycle.rb +6 -4
  19. data/lib/calabash/android/physical_buttons.rb +12 -1
  20. data/lib/calabash/android/screenshot.rb +1 -0
  21. data/lib/calabash/android/scroll.rb +115 -0
  22. data/lib/calabash/android/server.rb +3 -1
  23. data/lib/calabash/android/text.rb +16 -25
  24. data/lib/calabash/application.rb +29 -0
  25. data/lib/calabash/cli/build.rb +15 -1
  26. data/lib/calabash/cli/console.rb +9 -5
  27. data/lib/calabash/cli/generate.rb +5 -0
  28. data/lib/calabash/cli/helpers.rb +7 -1
  29. data/lib/calabash/cli/resign.rb +1 -0
  30. data/lib/calabash/cli/run.rb +10 -6
  31. data/lib/calabash/cli/setup_keystore.rb +2 -0
  32. data/lib/calabash/color.rb +7 -0
  33. data/lib/calabash/console_helpers.rb +4 -2
  34. data/lib/calabash/defaults.rb +1 -0
  35. data/lib/calabash/device.rb +8 -9
  36. data/lib/calabash/environment.rb +5 -0
  37. data/lib/calabash/gestures.rb +159 -66
  38. data/lib/calabash/http/retriable_client.rb +3 -1
  39. data/lib/calabash/interactions.rb +68 -5
  40. data/lib/calabash/ios.rb +4 -0
  41. data/lib/calabash/ios/application.rb +8 -1
  42. data/lib/calabash/ios/conditions.rb +3 -1
  43. data/lib/calabash/ios/date_picker.rb +412 -0
  44. data/lib/calabash/ios/defaults.rb +1 -0
  45. data/lib/calabash/ios/device.rb +1 -0
  46. data/lib/calabash/ios/device/device_implementation.rb +33 -16
  47. data/lib/calabash/ios/device/gestures_mixin.rb +202 -48
  48. data/lib/calabash/ios/device/ipad_1x_2x_mixin.rb +253 -0
  49. data/lib/calabash/ios/device/keyboard_mixin.rb +2 -0
  50. data/lib/calabash/ios/device/rotation_mixin.rb +11 -8
  51. data/lib/calabash/ios/device/routes/condition_route_mixin.rb +1 -0
  52. data/lib/calabash/ios/device/routes/handle_route_mixin.rb +5 -1
  53. data/lib/calabash/ios/device/routes/map_route_mixin.rb +1 -0
  54. data/lib/calabash/ios/device/routes/response_parser.rb +1 -0
  55. data/lib/calabash/ios/device/routes/uia_route_mixin.rb +44 -6
  56. data/lib/calabash/ios/device/runtime_attributes.rb +4 -5
  57. data/lib/calabash/ios/device/text_mixin.rb +2 -0
  58. data/lib/calabash/ios/device/uia_keyboard_mixin.rb +9 -0
  59. data/lib/calabash/ios/device/uia_mixin.rb +1 -0
  60. data/lib/calabash/ios/gestures.rb +82 -8
  61. data/lib/calabash/ios/interactions.rb +30 -1
  62. data/lib/calabash/ios/orientation.rb +21 -21
  63. data/lib/calabash/ios/runtime.rb +154 -2
  64. data/lib/calabash/ios/slider.rb +70 -0
  65. data/lib/calabash/ios/text.rb +11 -47
  66. data/lib/calabash/ios/uia.rb +24 -2
  67. data/lib/calabash/legacy.rb +7 -0
  68. data/lib/calabash/lib/skeleton/config/cucumber.yml +1 -3
  69. data/lib/calabash/lib/skeleton/features/support/dry_run.rb +8 -0
  70. data/lib/calabash/lib/skeleton/features/support/env.rb +15 -1
  71. data/lib/calabash/life_cycle.rb +78 -32
  72. data/lib/calabash/location.rb +2 -1
  73. data/lib/calabash/orientation.rb +0 -1
  74. data/lib/calabash/page.rb +51 -5
  75. data/lib/calabash/patch.rb +1 -0
  76. data/lib/calabash/patch/array.rb +7 -7
  77. data/lib/calabash/query.rb +17 -2
  78. data/lib/calabash/query_result.rb +14 -0
  79. data/lib/calabash/screenshot.rb +28 -8
  80. data/lib/calabash/text.rb +105 -8
  81. data/lib/calabash/utility.rb +6 -6
  82. data/lib/calabash/version.rb +1 -1
  83. data/lib/calabash/wait.rb +37 -11
  84. metadata +14 -7
@@ -0,0 +1,143 @@
1
+ if ENV["APP_PATH"]
2
+ Calabash::Environment::APP_PATH = ENV["APP_PATH"]
3
+ end
4
+
5
+ if ENV["TEST_APP_PATH"]
6
+ Calabash::Environment::TEST_SERVER_PATH = ENV["TEST_APP_PATH"]
7
+ end
8
+
9
+
10
+ module Calabash
11
+ module Android
12
+ class Device < Calabash::Device
13
+ def screen_on?
14
+ true
15
+ end
16
+
17
+ def md5_checksum(file_path)
18
+ "samplechecksum"
19
+ end
20
+
21
+ def adb_install_app(application)
22
+ @logger.log "Patch: Installing #{application.path}"
23
+
24
+ begin
25
+ result = adb.command("install #{application.path}", timeout: 60)
26
+ rescue ADB::ADBCallError => e
27
+ raise "Failed to install the application on device: '#{e.message}'"
28
+ end
29
+
30
+ if result.lines.last.downcase.chomp != 'success'
31
+ raise "Could not install app '#{application.identifier}': #{result.chomp}"
32
+ end
33
+
34
+ unless installed_packages.include?(application.identifier)
35
+ raise "App '#{application.identifier}' was not installed"
36
+ end
37
+ end
38
+
39
+ def _start_app(application, options={})
40
+ env_options = {}
41
+
42
+ options.fetch(:extras, {}).each do |k, v|
43
+ env_options[k] = v
44
+ end
45
+
46
+ env_options[:target_package] = application.identifier
47
+
48
+ if options[:activity]
49
+ env_options[:main_activity] = options[:activity]
50
+ end
51
+
52
+ env_options[:test_server_port] = server.test_server_port
53
+ env_options[:class] = options.fetch(:class, 'sh.calaba.instrumentationbackend.InstrumentationBackend')
54
+
55
+ if application.test_server.nil?
56
+ raise 'Invalid application. No test-server set.'
57
+ end
58
+
59
+ unless app_installed?(application.identifier)
60
+ raise "The application '#{application.identifier}' is not installed"
61
+ end
62
+
63
+ unless app_installed?(application.test_server.identifier)
64
+ raise "The test-server '#{application.test_server.identifier}' is not installed"
65
+ end
66
+
67
+ installed_app = installed_apps.find{|app| app[:package] == application.identifier}
68
+ installed_app_md5_checksum = md5_checksum(installed_app[:path])
69
+
70
+ if application.md5_checksum != installed_app_md5_checksum
71
+ raise "The specified app is not the same as the installed app (#{application.md5_checksum} != #{installed_app_md5_checksum})."
72
+ end
73
+
74
+ installed_test_server = installed_apps.find{|app| app[:package] == application.test_server.identifier}
75
+ installed_test_server_md5_checksum = md5_checksum(installed_test_server[:path])
76
+
77
+ if application.test_server.md5_checksum != installed_test_server_md5_checksum
78
+ raise "The specified test-server is not the same as the installed test-server (#{application.test_server.md5_checksum} != #{installed_test_server_md5_checksum})."
79
+ end
80
+
81
+ # We have to forward the port ourselves, as an old test-server could be
82
+ # running on the old port. If the retriable client was able to
83
+ # determine if the port had been forwarded, we would not need this.
84
+ port_forward(server.endpoint.port, server.test_server_port)
85
+
86
+ # For now, the test-server cannot rebind an existing socket.
87
+ # So we have to stop any running Calabash servers from the client
88
+ # for now.
89
+ if test_server_responding?
90
+ @logger.log("A test-server is already running on port #{server.test_server_port}")
91
+ @logger.log("Trying to stop it")
92
+
93
+ begin
94
+ _stop_app
95
+ rescue => _
96
+ raise 'Failed to stop old running test-server'
97
+ end
98
+ end
99
+
100
+ extras = ''
101
+
102
+ env_options.each_pair do |key, val|
103
+ extras = "#{extras} -e #{key.to_s} #{val.to_s}"
104
+ end
105
+
106
+ begin
107
+ instrument(application,
108
+ 'sh.calaba.instrumentationbackend.CalabashInstrumentationTestRunner',
109
+ extras)
110
+ rescue ADB::ADBCallError => e
111
+ raise "Failed to start the application: '#{e.stderr.lines.first.chomp}'"
112
+ end
113
+
114
+ begin
115
+ Retriable.retriable(tries: 30, interval: 1, timeout: 30, on: RetryError) do
116
+ unless test_server_responding?
117
+ raise RetryError
118
+ end
119
+ end
120
+ rescue RetryError => _
121
+ @logger.log('Could not contact test-server', :error)
122
+ @logger.log('For information, see the adb logcat', :error)
123
+ raise 'Could not contact test-server'
124
+ end
125
+
126
+ begin
127
+ Retriable.retriable(tries: 10, interval: 1, timeout: 10) do
128
+ unless test_server_ready?
129
+ raise RetryError
130
+ end
131
+ end
132
+ rescue RetryError => _
133
+ @logger.log('Test-server was never ready', :error)
134
+ @logger.log('For information, see the adb logcat', :error)
135
+ raise 'Test-server was never ready'
136
+ end
137
+
138
+ # Return true to avoid cluttering the console
139
+ true
140
+ end
141
+ end
142
+ end
143
+ end
@@ -1,16 +1,19 @@
1
1
  module Calabash
2
2
  module Android
3
- # @!visibility private
3
+ # Android specific life cyle methods.
4
4
  module LifeCycle
5
5
  # Resume an application. If the application is already focused, nothing
6
6
  # will happen.
7
7
  #
8
8
  # @example
9
9
  # go_home
10
+ # # Do something
10
11
  # resume_app
11
12
  #
12
- # @param [String, Calabash::Application] path_or_application The
13
- # application to resume.
13
+ # @param [String, Calabash::Application] path_or_application A path to the
14
+ # application, or an instance of {Calabash::Application}.
15
+ # Defaults to
16
+ # {Calabash::Defaults#default_application Calabash.default_application}
14
17
  def resume_app(path_or_application = nil)
15
18
  path_or_application ||= Application.default
16
19
 
@@ -23,7 +26,6 @@ module Calabash
23
26
  true
24
27
  end
25
28
 
26
-
27
29
  # @!visibility private
28
30
  def _send_current_app_to_background(for_seconds)
29
31
  package = focused_package
@@ -1,38 +1,49 @@
1
1
  module Calabash
2
2
  module Android
3
+ # Simulates pressing a *physical* button on the device. Use these methods
4
+ # carefully, as only a few devices have hardware key input. They can,
5
+ # however, be very useful for testing behaviour that would be hard to
6
+ # replicate otherwise.
3
7
  # @!visibility private
4
8
  module PhysicalButtons
5
9
 
6
- # @todo: Add note about this class being easily abused
10
+ # @!visibility private
7
11
  def press_button(key)
8
12
  Device.default.perform_action('press_key', key)
9
13
  true
10
14
  end
11
15
 
16
+ # @!visibility private
12
17
  def press_back_button
13
18
  press_button('KEYCODE_BACK')
14
19
  end
15
20
 
21
+ # @!visibility private
16
22
  def press_menu_button
17
23
  press_button('KEYCODE_MENU')
18
24
  end
19
25
 
26
+ # @!visibility private
20
27
  def press_down_button
21
28
  press_button('KEYCODE_DPAD_DOWN')
22
29
  end
23
30
 
31
+ # @!visibility private
24
32
  def press_up_button
25
33
  press_button('KEYCODE_DPAD_UP')
26
34
  end
27
35
 
36
+ # @!visibility private
28
37
  def press_left_button
29
38
  press_button('KEYCODE_DPAD_LEFT')
30
39
  end
31
40
 
41
+ # @!visibility private
32
42
  def press_right_button
33
43
  press_button('KEYCODE_DPAD_RIGHT')
34
44
  end
35
45
 
46
+ # @!visibility private
36
47
  def press_enter_button
37
48
  press_button('KEYCODE_ENTER')
38
49
  end
@@ -2,6 +2,7 @@ module Calabash
2
2
  module Android
3
3
  # @!visibility private
4
4
  module Screenshot
5
+ # @!visibility private
5
6
  SCREENSHOT_JAR_PATH = File.join(File.dirname(__FILE__), 'lib', 'screenshot_taker.jar')
6
7
  end
7
8
  end
@@ -0,0 +1,115 @@
1
+ module Calabash
2
+ module Android
3
+ # Scrolling invokes methods on the views to get to the right items. This
4
+ # behaviour is not doable by the users. For real gestures interacting with
5
+ # the screen, see {Calabash::Gestures}.
6
+ module Scroll
7
+ # Scroll the first view matched by `query` in `direction`.
8
+ #
9
+ # @param [String, Hash, Calabash::Query] query A query describing the
10
+ # view to scroll.
11
+ # @param [Symbol] direction The direction to scroll. Valid directions are:
12
+ # :up, :down, :left, and :right
13
+ def scroll(query, direction)
14
+ allowed_directions = [:up, :down, :left, :right]
15
+
16
+ dir_symbol = direction.to_sym
17
+
18
+ unless allowed_directions.include?(dir_symbol)
19
+ raise ArgumentError,
20
+ "Expected '#{direction}' to be one of #{allowed_directions.join(',')}"
21
+ end
22
+
23
+ view = wait_for_view(query, timeout: Calabash::Gestures::DEFAULT_GESTURE_WAIT_TIMEOUT)
24
+
25
+ result = query("#{Query.new(query)} index:0", :getFirstVisiblePosition)
26
+
27
+ if result.length == 0
28
+ raise "Failed to scroll view '#{query}'"
29
+ end
30
+
31
+ if result.first.is_a?(Hash) && result.first.has_key?("error")
32
+ # View is not of type android.widget.AbsListView
33
+ scroll_x = 0
34
+ scroll_y = 0
35
+ width = view['rect']['width']
36
+ height = view['rect']['height']
37
+
38
+ if direction == :up
39
+ scroll_y = -height / 2
40
+ elsif direction == :down
41
+ scroll_y = height / 2
42
+ elsif direction == :left
43
+ scroll_x = -width / 2
44
+ elsif direction == :right
45
+ scroll_x = width / 2
46
+ end
47
+
48
+ result = query("#{Query.new(query)} index:0", {scrollBy: [scroll_x.to_i, scroll_y.to_i]})
49
+
50
+ if result.length == 0
51
+ raise "Failed to scroll view '#{query}'"
52
+ end
53
+
54
+ if result.first.is_a?(Hash) && result.first.has_key?('error')
55
+ raise "Failed to scroll view: #{result.first['error']}"
56
+ end
57
+ else
58
+ # View is of type android.widget.AbsListView
59
+ unless [:up, :down].include?(dir_symbol)
60
+ raise ArgumentError,
61
+ "Can only scroll listviews :up or :down, not #{direction}"
62
+ end
63
+
64
+ first_position = result.first.to_i
65
+ result = query("#{Query.new(query)} index:0", :getLastVisiblePosition)
66
+
67
+ if result.length == 0
68
+ raise "Failed to scroll view '#{Query.new(query)}'"
69
+ end
70
+
71
+ last_position = result.first.to_i
72
+
73
+
74
+ selection_index = if direction == :up
75
+ [first_position + [first_position - last_position + 1, -1].min, 0].max
76
+ elsif direction == :down
77
+ first_position + [last_position - first_position, 1].max
78
+ end
79
+
80
+ result = query("#{Query.new(query)} index:0", setSelection: selection_index)
81
+
82
+ if result.length == 0
83
+ raise "Failed to scroll view '#{query}'"
84
+ end
85
+ end
86
+
87
+ true
88
+ end
89
+
90
+ # Scroll to `item` in `query`. If `query` matches multiple views, the
91
+ # first view matching `query` is scrolled.
92
+ #
93
+ # @param [String, Hash, Calabash::Query] query A query describing the
94
+ # view to scroll.
95
+ # @param [Numeric] item The item number to scroll to. This value is
96
+ # 0-indexed.
97
+ def scroll_to_row(query, item)
98
+ wait_for_view(query, timeout: Calabash::Gestures::DEFAULT_GESTURE_WAIT_TIMEOUT)
99
+ result = query("#{Query.new(query)} index:0", setSelection: item)
100
+
101
+ if result.length == 0
102
+ raise "Failed to scroll view '#{query}'"
103
+ end
104
+
105
+ result.length.times do |i|
106
+ if result[i].is_a?(Hash) && result[i].has_key?('error')
107
+ raise "Unable to scroll view number '#{i}' matching '#{query}'. #{result[i]['error']}"
108
+ end
109
+ end
110
+
111
+ true
112
+ end
113
+ end
114
+ end
115
+ end
@@ -2,8 +2,10 @@ module Calabash
2
2
  module Android
3
3
  # A representation of the Calabash Android test server.
4
4
  class Server < ::Calabash::Server
5
+ # The default Android test server.
5
6
  def self.default
6
- Server.new(URI.parse('http://127.0.0.1:33765'))
7
+ endpoint = Environment::DEVICE_ENDPOINT
8
+ Server.new(endpoint)
7
9
  end
8
10
  end
9
11
  end
@@ -1,31 +1,13 @@
1
1
  module Calabash
2
2
  module Android
3
- # @!visibility private
3
+ # Android specific text-related actions.
4
4
  module Text
5
+ # Dismisses the current keyboard. This is equivalent to the user
6
+ # pressing the back button if the keyboard is showing. If the keyboard is
7
+ # already hidden/dismissed, nothing is done.
5
8
  def dismiss_keyboard
6
9
  Device.default.perform_action('hide_soft_keyboard')
7
- end
8
-
9
- # Taps a keyboard action key on the keyboard. Notice that Calabash does
10
- # not ensure that this particular action key is actually available on the
11
- # current keyboard.
12
- #
13
- # @example
14
- # tap_keyboard_action_key(:normal)
15
- # tap_keyboard_action_key(:unspecified)
16
- # tap_keyboard_action_key(:none)
17
- # tap_keyboard_action_key(:go)
18
- # tap_keyboard_action_key(:search)
19
- # tap_keyboard_action_key(:send)
20
- # tap_keyboard_action_key(:next)
21
- # tap_keyboard_action_key(:done)
22
- # tap_keyboard_action_key(:previous)
23
- #
24
- # @see http://developer.android.com/reference/android/view/inputmethod/EditorInfo.html
25
- #
26
- # @param [Symbol] action_key The key to press.
27
- def tap_keyboard_action_key(action_key)
28
- Device.default.perform_action('press_user_action_button', action_key.to_s)
10
+ sleep 0.5
29
11
  end
30
12
 
31
13
  # @!visibility private
@@ -53,8 +35,17 @@ module Calabash
53
35
  end
54
36
 
55
37
  # @!visibility private
56
- def _tap_current_keyboard_action_key
57
- Device.default.perform_action('press_user_action_button')
38
+ def _tap_keyboard_action_key(action_key)
39
+ if action_key.nil?
40
+ Device.default.perform_action('press_user_action_button')
41
+ else
42
+ Device.default.perform_action('press_user_action_button', action_key.to_s)
43
+ end
44
+ end
45
+
46
+ # @!visibility private
47
+ def _keyboard_visible?
48
+ Device.default.keyboard_visible?
58
49
  end
59
50
  end
60
51
  end
@@ -21,6 +21,21 @@ module Calabash
21
21
 
22
22
  attr_reader :path
23
23
 
24
+ # Get the application from the default environment.
25
+ def self.default_from_environment
26
+ application_path = Environment::APP_PATH
27
+
28
+ if application_path.nil?
29
+ raise 'No application path is set. Specify application with environment variable CAL_APP'
30
+ end
31
+
32
+ unless File.exist?(application_path)
33
+ raise "Application '#{application_path}' does not exist"
34
+ end
35
+
36
+ Application.from_path(application_path)
37
+ end
38
+
24
39
  # Create an application from a path
25
40
  #
26
41
  # @return [Calabash::Android::Application, Calabash::IOS::Application] An
@@ -51,6 +66,20 @@ module Calabash
51
66
  ensure_application_path
52
67
  end
53
68
 
69
+ # Is this application an android application
70
+ #
71
+ # @return [Boolean] true if this application is an android application
72
+ def android_application?
73
+ false
74
+ end
75
+
76
+ # Is this application an iOS application
77
+ #
78
+ # @return [Boolean] true if this application is an iOS application
79
+ def ios_application?
80
+ false
81
+ end
82
+
54
83
  # @!visibility private
55
84
  def to_s
56
85
  "#<Application #{path}>"