calabash 1.2.1 → 1.9.9.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|