calabash 1.9.9.pre3 → 2.0.0.prelegacy

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 (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
@@ -1,7 +1,8 @@
1
1
  require 'geocoder'
2
2
 
3
3
  module Calabash
4
- # @!visibility private
4
+
5
+ # An API for setting the location of your app.
5
6
  module Location
6
7
  # Simulates gps location of the device/simulator.
7
8
  # @note Seems UIAutomation is broken here on physical devices on iOS 7.1
@@ -2,7 +2,6 @@ module Calabash
2
2
 
3
3
  # Methods for querying an app's orientation and for rotating the app into
4
4
  # different orientations.
5
- # @!visibility private
6
5
  module Orientation
7
6
  # Is the device in the portrait orientation?
8
7
  #
data/lib/calabash/page.rb CHANGED
@@ -1,29 +1,75 @@
1
1
  module Calabash
2
+
3
+ # A base class for the Page Object Model (POM) or Page Object Pattern.
4
+ #
5
+ # We recommend the POM for testing cross-platform apps.
6
+ #
7
+ # We have a great examples of using the POM in the Calabash 2.0 repository.
8
+ # * https://github.com/calabash/calabash/tree/develop/samples/wordpress
9
+ # * https://github.com/calabash/calabash/tree/develop/samples/shared-page-logic
2
10
  class Page
3
11
  # For auto-completion
4
12
  include Calabash
5
13
 
14
+ # @!visibility private
6
15
  def self.inherited(subclass)
7
16
  # Define the page into global scope
8
- name = subclass.to_s.split('::').last
17
+ full_name = subclass.name
9
18
 
10
- unless Object.const_defined?(name.to_sym)
11
- # We need a unique type for this constant
12
- clz = Class.new(StubPage)
13
- Object.const_set(name.to_sym, clz)
19
+ if full_name == 'IOS' || full_name == 'Android'
20
+ raise "Invalid page name '#{full_name}'"
21
+ end
22
+
23
+ os_scope = full_name.split('::').first
24
+
25
+ if os_scope == 'IOS' || os_scope == 'Android'
26
+ page_name = full_name.split('::', 2).last
27
+
28
+ unless Calabash.is_defined?(page_name)
29
+ scopes = page_name.split('::')
30
+
31
+ previous_scope = ''
32
+
33
+ scopes[0..-2].each do |scope|
34
+ old_scope = Calabash.recursive_const_get("Object::#{os_scope}#{previous_scope}")
35
+ new_scope = Calabash.recursive_const_get("Object#{previous_scope}")
36
+
37
+ old_const = old_scope.const_get(scope.to_sym)
38
+
39
+ if new_scope.const_defined?(scope.to_sym)
40
+ new_scope.send(:remove_const, scope.to_sym)
41
+ end
42
+
43
+ new_scope.const_set(scope.to_sym, old_const.class.allocate)
44
+
45
+ previous_scope << "::#{scope}"
46
+ end
47
+
48
+ simple_page_name = page_name.split('::').last.to_sym
49
+ new_scope = Calabash.recursive_const_get("Object#{previous_scope}")
50
+
51
+ unless new_scope.const_defined?(simple_page_name, false)
52
+ clz = Class.new(StubPage)
53
+ new_scope.const_set(simple_page_name, clz)
54
+ end
55
+ end
14
56
  end
15
57
  end
16
58
 
17
59
  private_class_method :new
18
60
 
61
+ # @!visibility private
19
62
  def initialize(world)
20
63
  @world = world
21
64
  end
22
65
 
66
+ # A query that distinguishes your page.
67
+ # @return [String, Hash, Calabash::Query] A query.
23
68
  def trait
24
69
  raise 'Implement your own trait'
25
70
  end
26
71
 
72
+ # Waits for the page trait to appear.
27
73
  def await(options={})
28
74
  wait_for_view(trait, options)
29
75
  end
@@ -3,6 +3,7 @@ module Calabash
3
3
  module Patch
4
4
  require 'calabash/patch/array'
5
5
 
6
+ # @!visibility private
6
7
  def self.apply_patches!
7
8
  modules = Patch.constants(false)
8
9
 
@@ -4,13 +4,13 @@ module Calabash
4
4
 
5
5
  # @!visibility private
6
6
  module Array
7
- def to_pct
8
- if length != 2
9
- raise RangeError, "Cannot convert #{self} to {:x, :y} hash"
10
- end
11
-
12
- {x: self.[](0), y: self.[](1)}
13
- end
7
+ # def to_pct
8
+ # if length != 2
9
+ # raise RangeError, "Cannot convert #{self} to {:x, :y} hash"
10
+ # end
11
+ #
12
+ # {x: self.[](0), y: self.[](1)}
13
+ # end
14
14
  end
15
15
  end
16
16
  end
@@ -1,8 +1,6 @@
1
1
  module Calabash
2
2
 
3
3
  # A representation of a Calabash query.
4
- # @todo Query needs more documentation.
5
- # @todo Query needs some methods moved to private or doc'd private.
6
4
  class Query
7
5
  # @!visibility private
8
6
  def self.web_query?(query_string)
@@ -110,6 +108,13 @@ module Calabash
110
108
  result
111
109
  end
112
110
 
111
+ # Create a new query. The returned instance will be frozen (immutable).
112
+ #
113
+ # @example
114
+ # Calabash::Query.new({marked: 'mark'})
115
+ # Calabash::Query.new("myview")
116
+ #
117
+ # @param [String, Hash, Calabash::Query] query The query to create
113
118
  def initialize(query)
114
119
  unless query.is_a?(Query) || query.is_a?(Hash) || query.is_a?(String)
115
120
  raise ArgumentError, "Invalid argument for query: '#{query}' (#{query.class})"
@@ -120,6 +125,13 @@ module Calabash
120
125
  freeze
121
126
  end
122
127
 
128
+ # Parse the query to it's string representation.
129
+ #
130
+ # @example
131
+ # puts Calabash::Query.new({marked: 'foo'})
132
+ # # => "* marked:'foo'"
133
+ #
134
+ # @!visibility private
123
135
  def to_s
124
136
  if @query.is_a?(Query)
125
137
  @query.to_s
@@ -130,14 +142,17 @@ module Calabash
130
142
  end
131
143
  end
132
144
 
145
+ # @!visibility private
133
146
  def inspect
134
147
  "<Calabash::Query #{@query.inspect}>"
135
148
  end
136
149
 
150
+ # @!visibility private
137
151
  def web_query?
138
152
  Query.web_query?(to_s)
139
153
  end
140
154
 
155
+ # @!visibility private
141
156
  WEB_QUERY_INDICATORS =
142
157
  [
143
158
  {
@@ -4,6 +4,7 @@ module Calabash
4
4
  # It will, unlike `Array`, raise an IndexError instead of returning nil if
5
5
  # an entry outside bounds is accessed.
6
6
  class QueryResult < Array
7
+ # @!visibility private
7
8
  def self.create(result, query)
8
9
  query_result = QueryResult.send(:new, query)
9
10
  query_result.send(:initialize_copy, result)
@@ -13,36 +14,43 @@ module Calabash
13
14
 
14
15
  private_class_method :new
15
16
 
17
+ # @!visibility private
16
18
  attr_reader :query
17
19
 
20
+ # @!visibility private
18
21
  def initialize(query)
19
22
  @query = Query.new(query)
20
23
  end
21
24
 
25
+ # @!visibility private
22
26
  def first(*several_variants)
23
27
  ensure_in_bounds(0)
24
28
 
25
29
  super(*several_variants)
26
30
  end
27
31
 
32
+ # @!visibility private
28
33
  def last(*several_variants)
29
34
  ensure_in_bounds(-1)
30
35
 
31
36
  super(*several_variants)
32
37
  end
33
38
 
39
+ # @!visibility private
34
40
  def [](index)
35
41
  ensure_in_bounds(index)
36
42
 
37
43
  super(index)
38
44
  end
39
45
 
46
+ # @!visibility private
40
47
  def at(index)
41
48
  ensure_in_bounds(index)
42
49
 
43
50
  super(index)
44
51
  end
45
52
 
53
+ # @!visibility private
46
54
  def fetch(*several_variants)
47
55
  unless block_given? || several_variants.length > 1
48
56
  ensure_in_bounds(several_variants.first)
@@ -51,7 +59,12 @@ module Calabash
51
59
  super(*several_variants)
52
60
  end
53
61
 
62
+ # @!visibility private
54
63
  def ensure_in_bounds(index)
64
+ unless index.is_a?(Numeric)
65
+ return true
66
+ end
67
+
55
68
  if empty?
56
69
  raise IndexError, "Query result is empty"
57
70
  end
@@ -65,6 +78,7 @@ module Calabash
65
78
  end
66
79
  end
67
80
 
81
+ # @!visibility private
68
82
  def self.recursive_freeze(object)
69
83
  if object.is_a?(Array)
70
84
  object.each do |entry|
@@ -4,7 +4,6 @@ require 'pathname'
4
4
  module Calabash
5
5
 
6
6
  # A public API for taking screenshots.
7
- # @!visibility private
8
7
  module Screenshot
9
8
  # @!visibility private
10
9
  def self.screenshot_directory_prefix
@@ -12,6 +11,7 @@ module Calabash
12
11
  end
13
12
 
14
13
  # Set the screenshot directory prefix.
14
+ # @!visibility private
15
15
  def self.screenshot_directory_prefix=(value)
16
16
  if class_variable_defined?(:@@screenshots_taken) &&
17
17
  @@screenshots_taken != 0
@@ -24,22 +24,36 @@ module Calabash
24
24
  # @!visibility private
25
25
  self.screenshot_directory_prefix = 'test_run_'
26
26
 
27
- # Takes a screenshot and saves it. The file is stored in the directory
28
- # given by the ENV variable $CAL_SCREENSHOT_DIR, or by default in
29
- # the relative directory 'screenshots'. The files are saved in a
27
+ # Takes a screenshot and saves it.
28
+ #
29
+ # If `name` is a relative path or a file name, then the file is stored in
30
+ # the directory specified by the ENV variable CAL_SCREENSHOT_DIR, or by
31
+ # default in the relative directory 'screenshots'. The files are saved in a
30
32
  # sub directory named test_run_n, where n is unique and incrementing for
31
33
  # each new test run. The filename of the screenshot will be `name`.
32
34
  # If `name` is not given (nil), the screenshot will be saved as
33
35
  # screenshot_N, where N is the total amount of screenshots taken for the
34
36
  # test run.
35
37
  #
38
+ # If the name given is an absolute path, then Calabash will save the
39
+ # screenshot to the absolute directory given.
40
+ #
41
+ # If the name given starts with ./ (e.g. `screenshot('./foo.png')`) then
42
+ # the filename will be saved relative to the current working directory.
43
+ #
44
+ # If the file specified by `name` has no extension then the filename will
45
+ # default to name + '.png'.
46
+ #
47
+ # If the directories specified do not exist, Calabash will create them.
48
+ #
36
49
  # @param [String] name Name of the screenshot.
37
50
  # @return [String] Path to the screenshot
38
51
  def screenshot(name=nil)
39
52
  Device.default.screenshot(name)
40
53
  end
41
54
 
42
- # Takes a screenshot and embeds it in the test report.
55
+ # Takes a screenshot and embeds it in the test report. This method is only
56
+ # available/useful when running in the context of cucumber.
43
57
  # @see Screenshot#screenshot
44
58
  def screenshot_embed(name=nil)
45
59
  path = screenshot(name)
@@ -58,11 +72,17 @@ module Calabash
58
72
 
59
73
  @@screenshots_taken += 1
60
74
 
61
- unless Dir.exist?(File.expand_path(screenshot_directory))
62
- FileUtils.mkdir_p(File.expand_path(screenshot_directory))
75
+ if name.start_with?('./')
76
+ name = File.join(Dir.pwd, "#{name[2..-1]}")
77
+ end
78
+
79
+ file_name = File.expand_path(name, screenshot_directory)
80
+
81
+ unless Dir.exist?(File.dirname(file_name))
82
+ FileUtils.mkdir_p(File.dirname(file_name))
63
83
  end
64
84
 
65
- File.join(screenshot_directory, name)
85
+ file_name
66
86
  end
67
87
 
68
88
  # @!visibility private
data/lib/calabash/text.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  module Calabash
2
2
 
3
3
  # A public API for entering text.
4
- # @!visibility private
5
4
  module Text
6
5
  # Enter `text` into the currently focused view.
7
6
  #
@@ -15,7 +14,8 @@ module Calabash
15
14
  # @see Calabash::Text#enter_text
16
15
  #
17
16
  # @param [String] text The text to type.
18
- # @param query A query describing the view to enter text into.
17
+ # @param [String, Hash, Calabash::Query] query A query describing the view
18
+ # to enter text into.
19
19
  def enter_text_in(query, text)
20
20
  _enter_text_in(query, text)
21
21
  end
@@ -28,27 +28,110 @@ module Calabash
28
28
  # Clears the text `view`
29
29
  # @see Calabash::Text#clear_text
30
30
  #
31
- # @param query A query describing the view to clear text in.
31
+ # @param [String, Hash, Calabash::Query] query A query describing the view
32
+ # to clear text in.
32
33
  def clear_text_in(query)
33
34
  _clear_text_in(query)
34
35
  end
35
36
 
36
- # @todo add docs
37
- def tap_current_keyboard_action_key
38
- _tap_current_keyboard_action_key
37
+ # Taps the keyboard action key. On iOS there is only one action key, which
38
+ # is the blue coloured key on the standard keyboard. On Android, there can
39
+ # be multiple actions keys available depending on the keyboard, but one key
40
+ # is often replacing the enter key, becoming the default action key. The
41
+ # view in focus on Android asks to keyboard to show one action key, but the
42
+ # keyboard might not adhere to this.
43
+ #
44
+ # On iOS some examples include:
45
+ # * Return
46
+ # * Next
47
+ # * Go
48
+ # * Join
49
+ # * Search
50
+ #
51
+ # On Android some examples include:
52
+ # * Search
53
+ # * Next
54
+ # * Previous
55
+ #
56
+ # See http://developer.android.com/reference/android/view/inputmethod/EditorInfo.html
57
+ #
58
+ # @example
59
+ # tap_keyboard_action_key(:search)
60
+ # tap_keyboard_action_key(:send)
61
+ # tap_keyboard_action_key(:next)
62
+ # tap_keyboard_action_key(:previous)
63
+ #
64
+ # Notice that, for Android, Calabash does not ensure that this particular action key is
65
+ # actually available on the current keyboard.
66
+ #
67
+ # Not all keyboards have an action key. For example, on iOS, numeric keyboards
68
+ # do not have an action key. On Android, if no action key is set for the
69
+ # view, the enter key is pressed instead.
70
+ #
71
+ # @param [Symbol] action_key The action key to press. This is only
72
+ # used for Android.
73
+ # @raise [ArgumentError] If action_key if set for iOS.
74
+ def tap_keyboard_action_key(action_key = nil)
75
+ _tap_keyboard_action_key(action_key)
76
+ true
39
77
  end
40
78
 
41
79
  # Escapes single quotes in `string`.
42
80
  #
43
81
  # @example
44
- # > escape_quotes("Let's get this done.")
82
+ # escape_single_quotes("Let's get this done.")
45
83
  # => "Let\\'s get this done."
84
+ #
85
+ # @example
86
+ # query("* text:'#{escape_single_quotes("Let's go")}'")
87
+ # # Equivalent to
88
+ # query("* text:'Let\\'s go'")
89
+ #
46
90
  # @param [String] string The string to escape.
47
91
  # @return [String] A string with its single quotes properly escaped.
48
92
  def escape_single_quotes(string)
49
93
  Text.escape_single_quotes(string)
50
94
  end
51
95
 
96
+ # Returns true if there is a visible keyboard.
97
+ # On Android, if a physical keyboard is connected, this method will always
98
+ # return true.
99
+ #
100
+ # @return [Boolean] Returns true if there is a visible keyboard.
101
+ def keyboard_visible?
102
+ _keyboard_visible?
103
+ end
104
+
105
+ # Waits for a keyboard to appear.
106
+ #
107
+ # @see Calabash::Wait.default_options
108
+ #
109
+ # @param [Number] timeout How long to wait for the keyboard.
110
+ # @raise [Calabash::Wait::TimeoutError] Raises error if no keyboard
111
+ # appears.
112
+ def wait_for_keyboard(timeout=nil)
113
+ keyboard_timeout = keyboard_wait_timeout(timeout)
114
+ message = "Timed out after #{keyboard_timeout} seconds waiting for the keyboard to appear"
115
+ wait_for(message, timeout: keyboard_timeout) do
116
+ keyboard_visible?
117
+ end
118
+ end
119
+
120
+ # Waits for the keyboard to disappear.
121
+ #
122
+ # @see Calabash::Wait.default_options
123
+ #
124
+ # @param [Number] timeout How log to wait for the keyboard to disappear.
125
+ # @raise [Calabash::Wait::TimeoutError] Raises error if any keyboard is
126
+ # visible after the `timeout`.
127
+ def wait_for_no_keyboard(timeout=nil)
128
+ keyboard_timeout = keyboard_wait_timeout(timeout)
129
+ message = "Timed out after #{keyboard_timeout} seconds waiting for the keyboard to disappear"
130
+ wait_for(message, timeout: keyboard_timeout) do
131
+ !keyboard_visible?
132
+ end
133
+ end
134
+
52
135
  # @!visibility private
53
136
  def _enter_text(text)
54
137
  abstract_method!
@@ -70,7 +153,12 @@ module Calabash
70
153
  end
71
154
 
72
155
  # @!visibility private
73
- def _tap_current_keyboard_action_key
156
+ def _tap_keyboard_action_key(action_key)
157
+ abstract_method!
158
+ end
159
+
160
+ # @!visibility private
161
+ def _keyboard_visible?
74
162
  abstract_method!
75
163
  end
76
164
 
@@ -78,5 +166,14 @@ module Calabash
78
166
  def self.escape_single_quotes(string)
79
167
  string.gsub("'", "\\\\'")
80
168
  end
169
+
170
+ # @!visibility private
171
+ def keyboard_wait_timeout(timeout)
172
+ if timeout.nil?
173
+ Calabash::Gestures::DEFAULT_GESTURE_WAIT_TIMEOUT
174
+ else
175
+ timeout
176
+ end
177
+ end
81
178
  end
82
179
  end