calabash 1.2.1 → 1.9.9.pre1
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.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +39 -0
- data/LICENSE +204 -21
- data/README.md +36 -6
- data/VERSIONING.md +16 -0
- data/bin/calabash +95 -0
- data/lib/calabash.rb +185 -1
- data/lib/calabash/android.rb +64 -0
- data/lib/calabash/android/adb.rb +277 -0
- data/lib/calabash/android/application.rb +110 -0
- data/lib/calabash/android/build.rb +12 -0
- data/lib/calabash/android/build/application.rb +13 -0
- data/lib/calabash/android/build/build_error.rb +11 -0
- data/lib/calabash/android/build/builder.rb +119 -0
- data/lib/calabash/android/build/java_keystore.rb +177 -0
- data/lib/calabash/android/build/resigner.rb +56 -0
- data/lib/calabash/android/build/test_server.rb +27 -0
- data/lib/calabash/android/console_helpers.rb +44 -0
- data/lib/calabash/android/cucumber.rb +3 -0
- data/lib/calabash/android/device.rb +965 -0
- data/lib/calabash/android/environment.rb +470 -0
- data/lib/calabash/android/gestures.rb +369 -0
- data/lib/calabash/android/interactions.rb +45 -0
- data/lib/calabash/android/lib/.irbrc +55 -0
- data/lib/calabash/android/lib/AndroidManifest.xml +51 -0
- data/lib/calabash/android/lib/TestServer.apk +0 -0
- data/lib/calabash/android/lib/calmd5/arm64-v8a/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/arm64-v8a/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/armeabi-v7a/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/armeabi-v7a/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/armeabi/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/armeabi/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/mips/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/mips/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/mips64/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/mips64/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/x86/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/x86/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/x86_64/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/x86_64/calmd5-pie +0 -0
- data/lib/calabash/android/lib/screenshot_taker.jar +0 -0
- data/lib/calabash/android/life_cycle.rb +37 -0
- data/lib/calabash/android/orientation.rb +30 -0
- data/lib/calabash/android/physical_buttons.rb +39 -0
- data/lib/calabash/android/screenshot.rb +9 -0
- data/lib/calabash/android/scroll.rb +5 -0
- data/lib/calabash/android/server.rb +10 -0
- data/lib/calabash/android/text.rb +54 -0
- data/lib/calabash/application.rb +74 -0
- data/lib/calabash/cli.rb +12 -0
- data/lib/calabash/cli/build.rb +33 -0
- data/lib/calabash/cli/console.rb +90 -0
- data/lib/calabash/cli/generate.rb +110 -0
- data/lib/calabash/cli/helpers.rb +130 -0
- data/lib/calabash/cli/resign.rb +33 -0
- data/lib/calabash/cli/run.rb +99 -0
- data/lib/calabash/cli/setup_keystore.rb +39 -0
- data/lib/calabash/color.rb +32 -0
- data/lib/calabash/console_helpers.rb +90 -0
- data/lib/calabash/defaults.rb +56 -0
- data/lib/calabash/device.rb +401 -0
- data/lib/calabash/environment.rb +75 -0
- data/lib/calabash/gestures.rb +384 -0
- data/lib/calabash/http.rb +8 -0
- data/lib/calabash/http/error.rb +15 -0
- data/lib/calabash/http/request.rb +42 -0
- data/lib/calabash/http/retriable_client.rb +156 -0
- data/lib/calabash/interactions.rb +105 -0
- data/lib/calabash/ios.rb +37 -0
- data/lib/calabash/ios/application.rb +119 -0
- data/lib/calabash/ios/conditions.rb +79 -0
- data/lib/calabash/ios/console_helpers.rb +72 -0
- data/lib/calabash/ios/device.rb +24 -0
- data/lib/calabash/ios/device/device_implementation.rb +779 -0
- data/lib/calabash/ios/device/gestures_mixin.rb +167 -0
- data/lib/calabash/ios/device/keyboard_mixin.rb +133 -0
- data/lib/calabash/ios/device/physical_device_mixin.rb +266 -0
- data/lib/calabash/ios/device/rotation_mixin.rb +124 -0
- data/lib/calabash/ios/device/routes/backdoor_route_mixin.rb +86 -0
- data/lib/calabash/ios/device/routes/condition_route_mixin.rb +62 -0
- data/lib/calabash/ios/device/routes/error.rb +8 -0
- data/lib/calabash/ios/device/routes/handle_route_mixin.rb +102 -0
- data/lib/calabash/ios/device/routes/map_route_mixin.rb +38 -0
- data/lib/calabash/ios/device/routes/playback_route_mixin.rb +70 -0
- data/lib/calabash/ios/device/routes/response_parser.rb +48 -0
- data/lib/calabash/ios/device/routes/uia_route_mixin.rb +238 -0
- data/lib/calabash/ios/device/runtime_attributes.rb +184 -0
- data/lib/calabash/ios/device/status_bar_mixin.rb +17 -0
- data/lib/calabash/ios/device/text_mixin.rb +19 -0
- data/lib/calabash/ios/device/uia_keyboard_mixin.rb +188 -0
- data/lib/calabash/ios/device/uia_mixin.rb +12 -0
- data/lib/calabash/ios/environment.rb +41 -0
- data/lib/calabash/ios/interactions.rb +10 -0
- data/lib/calabash/ios/lib/.irbrc +55 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_down_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_down_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_left_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_left_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_right_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_right_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_up_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_up_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_down_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_down_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_left_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_left_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_right_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_right_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_up_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_up_iphone.base64 +2 -0
- data/lib/calabash/ios/orientation.rb +117 -0
- data/lib/calabash/ios/scroll.rb +504 -0
- data/lib/calabash/ios/server.rb +73 -0
- data/lib/calabash/ios/text.rb +248 -0
- data/lib/calabash/ios/uia.rb +24 -0
- data/lib/calabash/lib/skeleton/config/cucumber.yml +6 -0
- data/lib/calabash/lib/skeleton/features/sample.feature +5 -0
- data/lib/calabash/lib/skeleton/features/step_definitions/calabash_steps.rb +29 -0
- data/lib/calabash/lib/skeleton/features/support/env.rb +54 -0
- data/lib/calabash/lib/skeleton/features/support/hooks.rb +83 -0
- data/lib/calabash/life_cycle.rb +111 -0
- data/lib/calabash/location.rb +51 -0
- data/lib/calabash/logger.rb +87 -0
- data/lib/calabash/orientation.rb +84 -0
- data/lib/calabash/page.rb +35 -0
- data/lib/calabash/patch.rb +14 -0
- data/lib/calabash/patch/array.rb +16 -0
- data/lib/calabash/patch/run_loop.rb +90 -0
- data/lib/calabash/query.rb +160 -0
- data/lib/calabash/query_result.rb +85 -0
- data/lib/calabash/screenshot.rb +89 -0
- data/lib/calabash/server.rb +16 -0
- data/lib/calabash/text.rb +76 -0
- data/lib/calabash/utility.rb +58 -0
- data/lib/calabash/version.rb +3 -1
- data/lib/calabash/wait.rb +474 -0
- metadata +462 -24
@@ -0,0 +1,111 @@
|
|
1
|
+
module Calabash
|
2
|
+
module LifeCycle
|
3
|
+
def start_app(path_or_application = nil, **opt)
|
4
|
+
path_or_application ||= Application.default
|
5
|
+
|
6
|
+
unless path_or_application
|
7
|
+
raise 'No application given, and Application.default is not set'
|
8
|
+
end
|
9
|
+
|
10
|
+
Device.default.start_app(path_or_application, opt.dup)
|
11
|
+
end
|
12
|
+
|
13
|
+
def stop_app
|
14
|
+
Device.default.stop_app
|
15
|
+
end
|
16
|
+
|
17
|
+
# Installs the given application. If the application is already installed,
|
18
|
+
# the application will be uninstalled, and installed afterwards. If no
|
19
|
+
# application is given, it will install `Application.default`.
|
20
|
+
#
|
21
|
+
# If the given application is an instance of
|
22
|
+
# `Calabash::Android::Application`, the same procedure is executed for the
|
23
|
+
# test-server of the application, if it is set.
|
24
|
+
#
|
25
|
+
# @param [String, Calabash::Application] path_or_application A path to the
|
26
|
+
# application, or an instance of `Calabash::Application`. Defaults to
|
27
|
+
# `Application.default`
|
28
|
+
def install_app(path_or_application = nil)
|
29
|
+
path_or_application ||= Application.default
|
30
|
+
|
31
|
+
unless path_or_application
|
32
|
+
raise 'No application given, and Application.default is not set'
|
33
|
+
end
|
34
|
+
|
35
|
+
Device.default.install_app(path_or_application)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Installs the given application *if it is not already installed*. If no
|
39
|
+
# application is given, it will ensure `Application.default` is installed.
|
40
|
+
# If the application has changed, it will be installed using the same
|
41
|
+
# approach as #{Calabash::LifeCycle#install_app}.
|
42
|
+
#
|
43
|
+
# If the given application is an instance of
|
44
|
+
# `Calabash::Android::Application`, the same procedure is executed for the
|
45
|
+
# test-server of the application, if it is set.
|
46
|
+
#
|
47
|
+
# @param [String, Calabash::Application] path_or_application A path to the
|
48
|
+
# application, or an instance of `Calabash::Application`. Defaults to
|
49
|
+
# `Application.default`
|
50
|
+
def ensure_app_installed(path_or_application = nil)
|
51
|
+
path_or_application ||= Application.default
|
52
|
+
|
53
|
+
unless path_or_application
|
54
|
+
raise 'No application given, and Application.default is not set'
|
55
|
+
end
|
56
|
+
|
57
|
+
Device.default.ensure_app_installed(path_or_application)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Uninstalls the given application. Does nothing if the application is
|
61
|
+
# already uninstalled. If no application is given, it will uninstall
|
62
|
+
# `Application.default`.
|
63
|
+
#
|
64
|
+
# @param [String, Calabash::Application] path_or_application A path to the
|
65
|
+
# application, or an instance of `Calabash::Application`. Defaults to
|
66
|
+
# `Application.default`
|
67
|
+
def uninstall_app(path_or_application = nil)
|
68
|
+
path_or_application ||= Application.default
|
69
|
+
|
70
|
+
unless path_or_application
|
71
|
+
raise 'No application given, and Application.default is not set'
|
72
|
+
end
|
73
|
+
|
74
|
+
Device.default.uninstall_app(path_or_application)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Clears the contents of the given application. This is roughly equivalent to
|
78
|
+
# reinstalling the application. If no application is given, it will clear
|
79
|
+
# `Application.default`.
|
80
|
+
#
|
81
|
+
# @param [String, Calabash::Application] path_or_application A path to the
|
82
|
+
# application, or an instance of `Calabash::Application`. Defaults to
|
83
|
+
# `Application.default`
|
84
|
+
def clear_app_data(path_or_application = nil)
|
85
|
+
path_or_application ||= Application.default
|
86
|
+
|
87
|
+
unless path_or_application
|
88
|
+
raise 'No application given, and Application.default is not set'
|
89
|
+
end
|
90
|
+
|
91
|
+
Device.default.clear_app_data(path_or_application)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Sends the current app to the background and resumes it after
|
95
|
+
# `for_seconds`. This should not exceed 60 seconds for iOS.
|
96
|
+
#
|
97
|
+
# On Android you can control the app lifecycle more granularly using
|
98
|
+
# #{Calabash::Android::Interactions#go_home} and
|
99
|
+
# #{Calabash::Android::LifeCycle#resume_app}.
|
100
|
+
def send_current_app_to_background(for_seconds = 10)
|
101
|
+
_send_current_app_to_background(for_seconds)
|
102
|
+
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
# !@visibility private
|
107
|
+
def _send_current_app_to_background(for_seconds)
|
108
|
+
abstract_method!
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'geocoder'
|
2
|
+
|
3
|
+
module Calabash
|
4
|
+
module Location
|
5
|
+
# Simulates gps location of the device/simulator.
|
6
|
+
# @note Seems UIAutomation is broken here on physical devices on iOS 7.1
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# set_location({latitude: 48.8567, longitude: 2.3508})
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# set_location(coordinates_for_place('The little mermaid, Copenhagen'))
|
13
|
+
#
|
14
|
+
# @param {Hash} options specifies which location to simulate
|
15
|
+
# @option options {Numeric} :latitude latitude of a gps coordinate (same
|
16
|
+
# coordinate system as Google maps)
|
17
|
+
# @option options {Numeric} :longitude longitude of a gps coordinate (same
|
18
|
+
# coordinate system as Google maps)
|
19
|
+
def set_location(location)
|
20
|
+
unless location.is_a?(Hash)
|
21
|
+
raise ArgumentError, "Expected location to be a Hash, not '#{location.class}'"
|
22
|
+
end
|
23
|
+
|
24
|
+
unless location[:latitude] || location[:longitude]
|
25
|
+
raise ArgumentError, "You must supply :latitude and :longitude"
|
26
|
+
end
|
27
|
+
|
28
|
+
Device.default.set_location(location)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Get the latitude and longitude for a certain place, resolved via Google
|
32
|
+
# maps api. This hash can be used in `set_location`.
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# coordinates_for_place('The little mermaid, Copenhagen')
|
36
|
+
# # => {:latitude => 55.6760968, :longitude => 12.5683371}
|
37
|
+
#
|
38
|
+
# @return [Hash] Latitude and longitude for the given place
|
39
|
+
# @raise [RuntimeError] If the place cannot be found
|
40
|
+
def coordinates_for_place(place)
|
41
|
+
result = Geocoder.search(place)
|
42
|
+
|
43
|
+
if result.empty?
|
44
|
+
raise "No result found for '#{place}'"
|
45
|
+
end
|
46
|
+
|
47
|
+
{latitude: result.first.latitude,
|
48
|
+
longitude: result.first.longitude}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Calabash
|
2
|
+
|
3
|
+
# A logger for Calabash.
|
4
|
+
class Logger
|
5
|
+
attr_accessor :default_log_level
|
6
|
+
|
7
|
+
def initialize(output=nil)
|
8
|
+
@output = output || STDOUT.dup
|
9
|
+
@default_log_level = :debug
|
10
|
+
end
|
11
|
+
|
12
|
+
# Log a message
|
13
|
+
#
|
14
|
+
# @param [String] message to log
|
15
|
+
# @param [Symbol] log_level
|
16
|
+
# Can be one of `{:info | :debug | :warn | :error }`
|
17
|
+
def log(message, log_level = default_log_level)
|
18
|
+
@output.write("#{message}\n") if should_log?(log_level)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# @!visibility private
|
24
|
+
def should_log?(log_level)
|
25
|
+
Logger.log_levels.include?(log_level)
|
26
|
+
end
|
27
|
+
|
28
|
+
public
|
29
|
+
|
30
|
+
if Environment::DEBUG
|
31
|
+
@@log_levels = [:info, :warn, :error, :debug]
|
32
|
+
else
|
33
|
+
@@log_levels = [:info, :warn, :error]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Log a message to the default output
|
37
|
+
#
|
38
|
+
# @param [String] message to log
|
39
|
+
# @param [Symbol] log_level (:debug, :info, :warning, :error)
|
40
|
+
# Can be one of `{:info | :debug | :warn | :error }`
|
41
|
+
def self.log(message, log_level = :info)
|
42
|
+
Logger.new.log(message, log_level)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Log a message to the default output with log level :info
|
46
|
+
#
|
47
|
+
# @param [String] message to log
|
48
|
+
def self.info(message)
|
49
|
+
log(message, :info)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Log a message to the default output with log level :debug
|
53
|
+
#
|
54
|
+
# @param [String] message to log
|
55
|
+
def self.debug(message)
|
56
|
+
log(message, :debug)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Log a message to the default output with log level :warn
|
60
|
+
#
|
61
|
+
# @param [String] message to log
|
62
|
+
def self.warn(message)
|
63
|
+
log(message, :warn)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Log a message to the default output with log level :error
|
67
|
+
#
|
68
|
+
# @param [String] message to log
|
69
|
+
def self.error(message)
|
70
|
+
log(message, :error)
|
71
|
+
end
|
72
|
+
|
73
|
+
# The log levels of the logger
|
74
|
+
#
|
75
|
+
# @return [Array] log levels
|
76
|
+
def self.log_levels
|
77
|
+
@@log_levels
|
78
|
+
end
|
79
|
+
|
80
|
+
# Set the log levels of the logger
|
81
|
+
#
|
82
|
+
# @param [Array] log_levels
|
83
|
+
def self.log_levels=(log_levels)
|
84
|
+
@@log_levels = log_levels
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Calabash
|
2
|
+
|
3
|
+
# Methods for querying an app's orientation and for rotating the app into
|
4
|
+
# different orientations.
|
5
|
+
module Orientation
|
6
|
+
# Is the device in the portrait orientation?
|
7
|
+
#
|
8
|
+
# @return [Boolean] Returns true if the device is in the 'up' or 'down'
|
9
|
+
# orientation.
|
10
|
+
def portrait?
|
11
|
+
_portrait?
|
12
|
+
end
|
13
|
+
|
14
|
+
# Is the device in the landscape orientation?
|
15
|
+
#
|
16
|
+
# @return [Boolean] Returns true if the device is in the 'left' or 'right'
|
17
|
+
# orientation.
|
18
|
+
def landscape?
|
19
|
+
_landscape?
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set the device orientation to portrait.
|
23
|
+
#
|
24
|
+
# In portrait orientation, the display of the device has a bigger width
|
25
|
+
# than height.
|
26
|
+
#
|
27
|
+
# On iOS, the presenting view controller must respond to rotation events.
|
28
|
+
# If the presenting view controller does not respond to rotation events,
|
29
|
+
# then no rotation will be performed.
|
30
|
+
def set_orientation_portrait
|
31
|
+
_set_orientation_portrait
|
32
|
+
end
|
33
|
+
|
34
|
+
# Set the device orientation to landscape.
|
35
|
+
#
|
36
|
+
# In landscape orientation, the display of the device has a bigger height
|
37
|
+
# than width.
|
38
|
+
#
|
39
|
+
# On iOS, the presenting view controller must respond to rotation events.
|
40
|
+
# If the presenting view controller does not respond to rotation events,
|
41
|
+
# then no rotation will be performed.
|
42
|
+
def set_orientation_landscape
|
43
|
+
_set_orientation_landscape
|
44
|
+
end
|
45
|
+
|
46
|
+
# Changes the orientation of the device.
|
47
|
+
#
|
48
|
+
# If the orientation is currently landscape, it will be set to portrait.
|
49
|
+
# If the orientation is currently portrait, it will be set to landscape.
|
50
|
+
#
|
51
|
+
# On iOS, the presenting view controller must respond to rotation events.
|
52
|
+
# If the presenting view controller does not respond to rotation events,
|
53
|
+
# then no rotation will be performed.
|
54
|
+
def change_orientation
|
55
|
+
if portrait?
|
56
|
+
set_orientation_landscape
|
57
|
+
elsif landscape?
|
58
|
+
set_orientation_portrait
|
59
|
+
else
|
60
|
+
raise 'Could not detect current orientation'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @!visibility private
|
65
|
+
def _portrait?
|
66
|
+
abstract_method!
|
67
|
+
end
|
68
|
+
|
69
|
+
# @!visibility private
|
70
|
+
def _landscape?
|
71
|
+
abstract_method!
|
72
|
+
end
|
73
|
+
|
74
|
+
# @!visibility private
|
75
|
+
def _set_orientation_portrait
|
76
|
+
abstract_method!
|
77
|
+
end
|
78
|
+
|
79
|
+
# @!visibility private
|
80
|
+
def _set_orientation_landscape
|
81
|
+
abstract_method!
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Calabash
|
2
|
+
class Page
|
3
|
+
# For auto-completion
|
4
|
+
include Calabash
|
5
|
+
|
6
|
+
def self.inherited(subclass)
|
7
|
+
# Define the page into global scope
|
8
|
+
name = subclass.to_s.split('::').last
|
9
|
+
|
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)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private_class_method :new
|
18
|
+
|
19
|
+
def initialize(world)
|
20
|
+
@world = world
|
21
|
+
end
|
22
|
+
|
23
|
+
def trait
|
24
|
+
raise 'Implement your own trait'
|
25
|
+
end
|
26
|
+
|
27
|
+
def await(timeout = nil)
|
28
|
+
wait_for_view(trait, timeout: timeout)
|
29
|
+
end
|
30
|
+
|
31
|
+
class StubPage
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Calabash
|
2
|
+
# @!visibility private
|
3
|
+
module Patch
|
4
|
+
require 'calabash/patch/array'
|
5
|
+
|
6
|
+
def self.apply_patches!
|
7
|
+
modules = Patch.constants(false)
|
8
|
+
|
9
|
+
modules.each do |constant|
|
10
|
+
Calabash.const_get(constant).send(:include, const_get(constant))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Calabash
|
2
|
+
# @!visibility private
|
3
|
+
module Patch
|
4
|
+
|
5
|
+
# @!visibility private
|
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
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'run_loop'
|
2
|
+
|
3
|
+
if RunLoop.const_defined?(:Ipa)
|
4
|
+
"\033[34mRunLoop::Ipa patch can be removed.\033[0m"
|
5
|
+
else
|
6
|
+
"\033[34mApplying RunLoop::Ipa patch.\033[0m"
|
7
|
+
|
8
|
+
# @!visibility private
|
9
|
+
module RunLoop
|
10
|
+
# A model of the an .ipa - a application binary for iOS devices.
|
11
|
+
class Ipa
|
12
|
+
|
13
|
+
|
14
|
+
# The path to this .ipa.
|
15
|
+
# @!attribute [r] path
|
16
|
+
# @return [String] A path to this .ipa.
|
17
|
+
attr_reader :path
|
18
|
+
|
19
|
+
# The bundle identifier of this ipa.
|
20
|
+
# @!attribute [r] bundle_identifier
|
21
|
+
# @return [String] The bundle identifier of this ipa; obtained by inspecting
|
22
|
+
# the app's Info.plist.
|
23
|
+
attr_reader :bundle_identifier
|
24
|
+
|
25
|
+
# Create a new ipa instance.
|
26
|
+
# @param [String] path_to_ipa The path the .ipa file.
|
27
|
+
# @return [Calabash::Ipa] A new ipa instance.
|
28
|
+
# @raise [RuntimeError] If the file does not exist.
|
29
|
+
# @raise [RuntimeError] If the file does not end in .ipa.
|
30
|
+
def initialize(path_to_ipa)
|
31
|
+
unless File.exist? path_to_ipa
|
32
|
+
raise "Expected an ipa at '#{path_to_ipa}'"
|
33
|
+
end
|
34
|
+
|
35
|
+
unless path_to_ipa.end_with?('.ipa')
|
36
|
+
raise "Expected '#{path_to_ipa}' to be an ipa"
|
37
|
+
end
|
38
|
+
@path = path_to_ipa
|
39
|
+
end
|
40
|
+
|
41
|
+
# @!visibility private
|
42
|
+
def to_s
|
43
|
+
"#<Ipa: #{bundle_identifier}: '#{path}'>"
|
44
|
+
end
|
45
|
+
|
46
|
+
# The bundle identifier of this ipa.
|
47
|
+
# @return [String] A string representation of this ipa's CFBundleIdentifier
|
48
|
+
# @raise [RuntimeError] If ipa does not expand into a Payload/<app name>.app
|
49
|
+
# directory.
|
50
|
+
# @raise [RuntimeError] If an Info.plist does exist in the .app.
|
51
|
+
def bundle_identifier
|
52
|
+
unless File.exist?(bundle_dir)
|
53
|
+
raise "Expected a '#{File.basename(path).split('.').first}.app'\nat path '#{payload_dir}'"
|
54
|
+
end
|
55
|
+
|
56
|
+
@bundle_identifier ||= lambda {
|
57
|
+
info_plist_path = File.join(bundle_dir, 'Info.plist')
|
58
|
+
unless File.exist? info_plist_path
|
59
|
+
raise "Expected an 'Info.plist' at '#{bundle_dir}'"
|
60
|
+
end
|
61
|
+
pbuddy = RunLoop::PlistBuddy.new
|
62
|
+
pbuddy.plist_read('CFBundleIdentifier', info_plist_path)
|
63
|
+
}.call
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def tmpdir
|
69
|
+
@tmpdir ||= Dir.mktmpdir
|
70
|
+
end
|
71
|
+
|
72
|
+
def payload_dir
|
73
|
+
@payload_dir ||= lambda {
|
74
|
+
FileUtils.cp(path, tmpdir)
|
75
|
+
zip_path = File.join(tmpdir, File.basename(path))
|
76
|
+
Dir.chdir(tmpdir) do
|
77
|
+
system('unzip', *['-q', zip_path])
|
78
|
+
end
|
79
|
+
File.join(tmpdir, 'Payload')
|
80
|
+
}.call
|
81
|
+
end
|
82
|
+
|
83
|
+
def bundle_dir
|
84
|
+
@bundle_dir ||= lambda {
|
85
|
+
Dir.glob(File.join(payload_dir, '*')).detect {|f| File.directory?(f) && f.end_with?('.app')}
|
86
|
+
}.call
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|