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,79 @@
|
|
1
|
+
module Calabash
|
2
|
+
module IOS
|
3
|
+
module Conditions
|
4
|
+
|
5
|
+
# Waits for all elements to stop animating.
|
6
|
+
#
|
7
|
+
# @param [Numeric] timeout How long to wait for the animations to stop.
|
8
|
+
# @return [nil] when the condition is satisfied
|
9
|
+
# @raise [Calabash::Cucumber::WaitHelpers::WaitError] when the timeout is exceeded
|
10
|
+
def wait_for_animations(timeout=2)
|
11
|
+
message = "Timed out after #{timeout} seconds wait for all views to stop animating."
|
12
|
+
|
13
|
+
wait_for_condition(CALABASH_CONDITIONS[:none_animating],
|
14
|
+
timeout,
|
15
|
+
message)
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
# Waits for all elements matching `query` to stop animating.
|
20
|
+
#
|
21
|
+
# @param [String] query The view to wait for.
|
22
|
+
# @param [Numeric] timeout How long to wait for the animations to stop.
|
23
|
+
# @return [nil] When the condition is satisfied.
|
24
|
+
# @raise [Calabash::Wait::TimeoutError] When the timeout is exceeded.
|
25
|
+
def wait_for_animations_in(query, timeout=2)
|
26
|
+
|
27
|
+
if query.nil? || query == ''
|
28
|
+
raise ArgumentError, 'Query argument must not be nil or the empty string'
|
29
|
+
end
|
30
|
+
|
31
|
+
message = "Timed out after #{timeout} waiting for views matching '#{query}' to stop animating."
|
32
|
+
|
33
|
+
wait_for_condition(CALABASH_CONDITIONS[:none_animating],
|
34
|
+
timeout,
|
35
|
+
message,
|
36
|
+
query)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Waits for the status-bar network indicator to stop animating
|
40
|
+
# (network activity done).
|
41
|
+
#
|
42
|
+
# param [Numeric] timeout How long to wait for the animations to stop.
|
43
|
+
# @return [nil] When the condition is satisfied.
|
44
|
+
# @raise [Calabash::Wait::TimeoutError] When the timeout is exceeded.
|
45
|
+
def wait_for_no_network_indicator(timeout=15)
|
46
|
+
message = "Timed out after #{timeout} waiting for the network indicator to stop animating."
|
47
|
+
|
48
|
+
wait_for_condition(CALABASH_CONDITIONS[:no_network_indicator],
|
49
|
+
timeout,
|
50
|
+
message)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
CALABASH_CONDITIONS = {:none_animating => 'NONE_ANIMATING',
|
56
|
+
:no_network_indicator => 'NO_NETWORK_INDICATOR'}
|
57
|
+
|
58
|
+
# @!visibility private
|
59
|
+
#
|
60
|
+
# Waits for condition.
|
61
|
+
#
|
62
|
+
# @param [String] condition The condition to wait for.
|
63
|
+
# @param [Numeric] timeout How long to wait.
|
64
|
+
# @param [String] timeout_message The message used when raising an error if
|
65
|
+
# the condition is not satisfied.
|
66
|
+
# @param [String] query Views matching this query will have the condition
|
67
|
+
# applied to them. Will be ignored for some conditions e.g.
|
68
|
+
# NO_NETWORK_INDICATOR
|
69
|
+
# @return [nil] When the condition is satisfied.
|
70
|
+
# @raise [Calabash::Wait::TimeoutError] When the timeout is exceeded.
|
71
|
+
def wait_for_condition(condition, timeout, timeout_message, query='*')
|
72
|
+
unless Device.default.condition_route(condition, timeout, query)
|
73
|
+
raise Calabash::Wait::TimeoutError, timeout_message
|
74
|
+
end
|
75
|
+
true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Calabash
|
2
|
+
module ConsoleHelpers
|
3
|
+
def self.render(data, indentation)
|
4
|
+
if visible?(data)
|
5
|
+
type = data['type']
|
6
|
+
|
7
|
+
str_type = if data['type'] == 'dom'
|
8
|
+
"#{Color.yellow("[")}#{type}:#{Color.yellow("#{data['nodeName']}]")} "
|
9
|
+
else
|
10
|
+
Color.yellow("[#{type}] ")
|
11
|
+
end
|
12
|
+
|
13
|
+
str_id = data['id'] ? "[id:#{Color.blue(data['id'])}] " : ''
|
14
|
+
str_label = data['label'] ? "[label:#{Color.green(data['label'])}] " : ''
|
15
|
+
str_text = data['value'] ? "[text:#{Color.magenta(data['value'])}] " : ''
|
16
|
+
output("#{str_type}#{str_id}#{str_label}#{str_text}", indentation)
|
17
|
+
output("\n", indentation)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.visible?(data)
|
22
|
+
(data['visible'] == 1) || data['children'].map{|child| visible?(child)}.any?
|
23
|
+
end
|
24
|
+
|
25
|
+
# Attach the current Calabash run-loop to a console.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# You have encountered a failing cucumber Scenario.
|
29
|
+
# You open the console and want to start investigating the cause of the failure.
|
30
|
+
#
|
31
|
+
# Use
|
32
|
+
#
|
33
|
+
# > console_attach
|
34
|
+
#
|
35
|
+
# to connect to the current run-loop so you can perform gestures.
|
36
|
+
#
|
37
|
+
# @param [Symbol] uia_strategy Optionally specify the uia strategy, which
|
38
|
+
# can be one of :shared_element, :preferences, :host. If you don't
|
39
|
+
# know which to choose, don't specify one and calabash will try deduce
|
40
|
+
# the correct strategy to use based on the environment variables used
|
41
|
+
# when starting the console.
|
42
|
+
#
|
43
|
+
# @return [Hash] The hash will contain the current device, the path to the
|
44
|
+
# current application, and the run-loop strategy.
|
45
|
+
#
|
46
|
+
# @raise [RuntimeError] If the app is not running.
|
47
|
+
def console_attach(uia_strategy=nil)
|
48
|
+
Calabash::Application.default = Calabash::IOS::Application.default_from_environment
|
49
|
+
|
50
|
+
identifier = Calabash::IOS::Device.default_identifier_for_application(Calabash::Application.default)
|
51
|
+
server = Calabash::IOS::Server.default
|
52
|
+
|
53
|
+
device = Calabash::IOS::Device.new(identifier, server)
|
54
|
+
Calabash::Device.default = device
|
55
|
+
|
56
|
+
begin
|
57
|
+
Calabash::Device.default.ensure_test_server_ready({:timeout => 4})
|
58
|
+
rescue RuntimeError => e
|
59
|
+
if e.to_s == 'Calabash server did not respond'
|
60
|
+
raise RuntimeError, 'You can only attach to a running Calabash iOS App'
|
61
|
+
else
|
62
|
+
raise e
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
run_loop_device = device.send(:run_loop_device)
|
67
|
+
result = Calabash::Device.default.send(:attach_to_run_loop, run_loop_device, uia_strategy)
|
68
|
+
result[:application] = Calabash::Application.default
|
69
|
+
result
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Calabash
|
2
|
+
module IOS
|
3
|
+
|
4
|
+
require 'calabash/ios/device/runtime_attributes'
|
5
|
+
require 'calabash/ios/device/routes/error'
|
6
|
+
require 'calabash/ios/device/routes/response_parser'
|
7
|
+
require 'calabash/ios/device/routes/handle_route_mixin'
|
8
|
+
require 'calabash/ios/device/routes/map_route_mixin'
|
9
|
+
require 'calabash/ios/device/routes/uia_route_mixin'
|
10
|
+
require 'calabash/ios/device/routes/condition_route_mixin'
|
11
|
+
require 'calabash/ios/device/routes/backdoor_route_mixin'
|
12
|
+
require 'calabash/ios/device/routes/playback_route_mixin'
|
13
|
+
require 'calabash/ios/device/gestures_mixin'
|
14
|
+
require 'calabash/ios/device/physical_device_mixin'
|
15
|
+
require 'calabash/ios/device/status_bar_mixin'
|
16
|
+
require 'calabash/ios/device/rotation_mixin'
|
17
|
+
require 'calabash/ios/device/keyboard_mixin'
|
18
|
+
require 'calabash/ios/device/uia_keyboard_mixin'
|
19
|
+
require 'calabash/ios/device/text_mixin'
|
20
|
+
require 'calabash/ios/device/uia_mixin'
|
21
|
+
require 'calabash/ios/device/device_implementation'
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,779 @@
|
|
1
|
+
module Calabash
|
2
|
+
module IOS
|
3
|
+
|
4
|
+
# An iOS Device is an iOS Simulator or physical device.
|
5
|
+
class Device < ::Calabash::Device
|
6
|
+
|
7
|
+
include Calabash::IOS::PhysicalDeviceMixin
|
8
|
+
include Calabash::IOS::Routes::ResponseParser
|
9
|
+
include Calabash::IOS::Routes::HandleRouteMixin
|
10
|
+
include Calabash::IOS::Routes::MapRouteMixin
|
11
|
+
include Calabash::IOS::Routes::UIARouteMixin
|
12
|
+
include Calabash::IOS::Routes::ConditionRouteMixin
|
13
|
+
include Calabash::IOS::Routes::BackdoorRouteMixin
|
14
|
+
include Calabash::IOS::Routes::PlaybackRouteMixin
|
15
|
+
include Calabash::IOS::StatusBarMixin
|
16
|
+
include Calabash::IOS::RotationMixin
|
17
|
+
include Calabash::IOS::KeyboardMixin
|
18
|
+
include Calabash::IOS::UIAKeyboardMixin
|
19
|
+
include Calabash::IOS::TextMixin
|
20
|
+
include Calabash::IOS::UIAMixin
|
21
|
+
|
22
|
+
include Calabash::IOS::GesturesMixin
|
23
|
+
|
24
|
+
# @todo Should these be public?
|
25
|
+
# @todo If public, document!
|
26
|
+
attr_reader :run_loop
|
27
|
+
attr_reader :uia_strategy
|
28
|
+
attr_reader :start_options
|
29
|
+
|
30
|
+
# Returns the default simulator identifier. The string that is return
|
31
|
+
# can be used as an argument to `instruments`.
|
32
|
+
#
|
33
|
+
# You can set the default simulator identifier by setting the
|
34
|
+
# `CAL_DEVICE_ID` environment variable. If this value is not set, then
|
35
|
+
# the default simulator identifier will indicate the highest supported
|
36
|
+
# iPhone 5s Simulator SDK. For example, when the active Xcode is 6.3,
|
37
|
+
# the default value will be "iPhone 5s (8.3 Simulator)".
|
38
|
+
#
|
39
|
+
# @see Calabash::Environment::DEVICE_IDENTIFIER
|
40
|
+
#
|
41
|
+
# @return [String] An instruments-ready simulator identifier.
|
42
|
+
# @raise [RuntimeError] When `CAL_DEVICE_ID` is set, this method will
|
43
|
+
# raise an error if no matching simulator can be found.
|
44
|
+
def self.default_simulator_identifier
|
45
|
+
identifier = Environment::DEVICE_IDENTIFIER
|
46
|
+
|
47
|
+
if identifier.nil?
|
48
|
+
RunLoop::Core.default_simulator
|
49
|
+
else
|
50
|
+
run_loop_device = Device.fetch_matching_simulator(identifier)
|
51
|
+
if run_loop_device.nil?
|
52
|
+
raise "Could not find a simulator with a UDID or name matching '#{identifier}'"
|
53
|
+
end
|
54
|
+
run_loop_device.instruments_identifier
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the default physical device identifier. The string that is
|
59
|
+
# return can be used as an argument to `instruments`.
|
60
|
+
#
|
61
|
+
# You can set the default physical device identifier by setting the
|
62
|
+
# `CAL_DEVICE_ID` environment variable. If this value is not set,
|
63
|
+
# Calabash will try to detect available devices.
|
64
|
+
# * If no devices are available, this method will raise an error.
|
65
|
+
# * If more than one device is available, this method will raise an error.
|
66
|
+
# * If only one device is available, this method will return the UDID
|
67
|
+
# of that device.
|
68
|
+
#
|
69
|
+
# @see Calabash::Environment::DEVICE_IDENTIFIER
|
70
|
+
#
|
71
|
+
# @return [String] An instruments-ready device identifier.
|
72
|
+
# @raise [RuntimeError] When `CAL_DEVICE_ID` is set, this method will
|
73
|
+
# raise an error if no matching physical device can be found.
|
74
|
+
# @raise [RuntimeError] When `CAL_DEVICE_ID` is not set and no physical
|
75
|
+
# devices are available.
|
76
|
+
# @raise [RuntimeError] When `CAL_DEVICE_ID` is not set and more than one
|
77
|
+
# physical device is available.
|
78
|
+
def self.default_physical_device_identifier
|
79
|
+
identifier = Environment::DEVICE_IDENTIFIER
|
80
|
+
|
81
|
+
if identifier.nil?
|
82
|
+
connected_devices = RunLoop::XCTools.new.instruments(:devices)
|
83
|
+
if connected_devices.empty?
|
84
|
+
raise 'There are no physical devices connected.'
|
85
|
+
elsif connected_devices.count > 1
|
86
|
+
raise 'There is more than one physical devices connected. Use CAL_DEVICE_ID to indicate which you want to connect to.'
|
87
|
+
else
|
88
|
+
connected_devices.first.instruments_identifier
|
89
|
+
end
|
90
|
+
else
|
91
|
+
run_loop_device = Device.fetch_matching_physical_device(identifier)
|
92
|
+
if run_loop_device.nil?
|
93
|
+
raise "Could not find a physical device with a UDID or name matching '#{identifier}'"
|
94
|
+
end
|
95
|
+
run_loop_device.instruments_identifier
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns the default identifier for an application. If the application
|
100
|
+
# is a simulator bundle (.app), the default simulator identifier is
|
101
|
+
# returned. If the application is a device binary (.ipa), the default
|
102
|
+
# physical device identifier is returned.
|
103
|
+
#
|
104
|
+
# @see Calabash::IOS::Device#default_simulator_identifier
|
105
|
+
# @see Calabash::IOS::Device#default_physical_device_identifier
|
106
|
+
#
|
107
|
+
# @return [String] An instruments ready identifier based on whether the
|
108
|
+
# application is for a simulator or physical device.
|
109
|
+
# @raise [RuntimeError] If the application is not a .app or .ipa.
|
110
|
+
def self.default_identifier_for_application(application)
|
111
|
+
if application.simulator_bundle?
|
112
|
+
default_simulator_identifier
|
113
|
+
elsif application.device_binary?
|
114
|
+
default_physical_device_identifier
|
115
|
+
else
|
116
|
+
raise "Invalid application #{application} for iOS platform."
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Create a new iOS Device.
|
121
|
+
#
|
122
|
+
# @param [String] identifier The name or UDID of a simulator or physical
|
123
|
+
# device.
|
124
|
+
# @param [Calabash::IOS::Server] server A representation of the embedded
|
125
|
+
# Calabash server.
|
126
|
+
#
|
127
|
+
# @return [Calabash::IOS::Device] A representation of an iOS Simulator or
|
128
|
+
# physical device.
|
129
|
+
# @raise [RuntimeError] If the server points to localhost and the
|
130
|
+
# identifier is not for a simulator.
|
131
|
+
#
|
132
|
+
# @todo My inclination is to defer calling out to simctl or instruments
|
133
|
+
# here to find the RunLoop::Device that matches identifier. These are
|
134
|
+
# very expensive calls.
|
135
|
+
def initialize(identifier, server)
|
136
|
+
super
|
137
|
+
|
138
|
+
Calabash::IOS::Device.expect_compatible_server_endpoint(identifier, server)
|
139
|
+
end
|
140
|
+
|
141
|
+
# @!visibility private
|
142
|
+
def test_server_responding?
|
143
|
+
begin
|
144
|
+
http_client.get(Calabash::HTTP::Request.new('version')).status.to_i == 200
|
145
|
+
rescue Calabash::HTTP::Error => _
|
146
|
+
false
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# @!visibility private
|
151
|
+
def to_s
|
152
|
+
if @run_loop_device
|
153
|
+
run_loop_device.to_s
|
154
|
+
else
|
155
|
+
"#<iOS Device '#{identifier}'>"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# @!visibility private
|
160
|
+
def inspect
|
161
|
+
to_s
|
162
|
+
end
|
163
|
+
|
164
|
+
# The device family of this device.
|
165
|
+
#
|
166
|
+
# @example
|
167
|
+
# # will be one of
|
168
|
+
# iPhone
|
169
|
+
# iPod
|
170
|
+
# iPad
|
171
|
+
#
|
172
|
+
# @return [String] the device family
|
173
|
+
# @raise [RuntimeError] If the app has not been launched.
|
174
|
+
def device_family
|
175
|
+
# For iOS Simulators, this can be obtained by asking the run_loop_device
|
176
|
+
# and analyzing the name of the device. This does not require the app
|
177
|
+
# to be launched, but it is expensive (takes many seconds).
|
178
|
+
|
179
|
+
# For physical devices, this can only be obtained using a third-party
|
180
|
+
# tool like ideviceinfo or asking the server.
|
181
|
+
expect_runtime_attributes_available(__method__)
|
182
|
+
runtime_attributes.device_family
|
183
|
+
end
|
184
|
+
|
185
|
+
# The form factor of the device under test.
|
186
|
+
#
|
187
|
+
# Will be one of:
|
188
|
+
#
|
189
|
+
# * ipad
|
190
|
+
# * iphone 4in
|
191
|
+
# * iphone 3.5in
|
192
|
+
# * iphone 6
|
193
|
+
# * iphone 6+
|
194
|
+
# * unknown # if no information can be found.
|
195
|
+
#
|
196
|
+
# @note iPod is not on this list for a reason! An iPod has an iPhone
|
197
|
+
# form factor. If you need to detect an iPod use `device_family`. Also
|
198
|
+
# note that there are no iPod simulators.
|
199
|
+
#
|
200
|
+
# @return [String] The form factor of the device under test.
|
201
|
+
# @raise [RuntimeError] If the app has not been launched.
|
202
|
+
def form_factor
|
203
|
+
# For iOS Simulators, this can be obtained by asking the run_loop_device
|
204
|
+
# and analyzing the name of the device. This does not require the app
|
205
|
+
# to be launched, but it is expensive (takes many seconds).
|
206
|
+
|
207
|
+
# For physical devices, this can only be obtained using a third-party
|
208
|
+
# tool like ideviceinfo or asking the server.
|
209
|
+
expect_runtime_attributes_available(__method__)
|
210
|
+
runtime_attributes.form_factor
|
211
|
+
end
|
212
|
+
|
213
|
+
# @!visibility private
|
214
|
+
# The iOS version on the test device.
|
215
|
+
#
|
216
|
+
# @return [RunLoop::Version] The major.minor.patch[.pre\d] version of the
|
217
|
+
# iOS version on the device.
|
218
|
+
def ios_version
|
219
|
+
# Can be obtain by asking for a device's run_loop_device. This does not
|
220
|
+
# require the app to be launched, but it is expensive
|
221
|
+
# (takes many seconds). run_loop_device is memoized so the expense
|
222
|
+
# is only incurred 1x per device instance.
|
223
|
+
|
224
|
+
# Can also be obtained by asking the server after the app is launched
|
225
|
+
# on the device which would be cheaper.
|
226
|
+
run_loop_device.version
|
227
|
+
end
|
228
|
+
|
229
|
+
# Is the app that is running an iPhone-only app emulated on an iPad?
|
230
|
+
#
|
231
|
+
# @note If the app is running in emulation mode, there will be a 1x or 2x
|
232
|
+
# scale button visible on the iPad.
|
233
|
+
#
|
234
|
+
# @return [Boolean] true if the app running on this devices is an
|
235
|
+
# iPhone-only app emulated on an iPad
|
236
|
+
# @raise [RuntimeError] If the app has not been launched.
|
237
|
+
def iphone_app_emulated_on_ipad?
|
238
|
+
# It is possible to find this information on iOS Simulators without
|
239
|
+
# launching the app. It is not possible to find this information
|
240
|
+
# when targeting a physical device unless a third-party tool is used.
|
241
|
+
expect_runtime_attributes_available(__method__)
|
242
|
+
runtime_attributes.iphone_app_emulated_on_ipad?
|
243
|
+
end
|
244
|
+
|
245
|
+
# Is this device a physical device?
|
246
|
+
# @return [Boolean] Returns true if this device is a physical device.
|
247
|
+
def physical_device?
|
248
|
+
# Can be obtain by asking for a device's run_loop_device. This does not
|
249
|
+
# require the app to be launched, but it is expensive
|
250
|
+
# (takes many seconds). run_loop_device is memoized so the expense
|
251
|
+
# is only incurred 1x per device instance.
|
252
|
+
|
253
|
+
# Can also be obtained by asking the server after the app is launched
|
254
|
+
# on the device which would be cheaper.
|
255
|
+
run_loop_device.physical_device?
|
256
|
+
end
|
257
|
+
|
258
|
+
# Information about the runtime screen dimensions of the app under test.
|
259
|
+
#
|
260
|
+
# This is a hash of form:
|
261
|
+
#
|
262
|
+
# ```
|
263
|
+
# {
|
264
|
+
# :sample => 1,
|
265
|
+
# :height => 1334,
|
266
|
+
# :width => 750,
|
267
|
+
# :scale" => 2
|
268
|
+
# }
|
269
|
+
# ```
|
270
|
+
#
|
271
|
+
# @return [Hash] screen dimensions, scale and down/up sampling fraction.
|
272
|
+
# @raise [RuntimeError] If the app has not been launched.
|
273
|
+
def screen_dimensions
|
274
|
+
# This can only be obtained at runtime because of iOS scaling and
|
275
|
+
# sampling.
|
276
|
+
expect_runtime_attributes_available(__method__)
|
277
|
+
runtime_attributes.screen_dimensions
|
278
|
+
end
|
279
|
+
|
280
|
+
# The version of the embedded Calabash server that is running in the
|
281
|
+
# app under test on this device.
|
282
|
+
#
|
283
|
+
# @return [RunLoop::Version] The major.minor.patch[.pre\d] version of the
|
284
|
+
# embedded Calabash server
|
285
|
+
# @raise [RuntimeError] If the app has not been launched.
|
286
|
+
def server_version
|
287
|
+
# It is possible to find this information without launching the app but
|
288
|
+
# it's probably best to ask the server for this information after the
|
289
|
+
# app has launched.
|
290
|
+
expect_runtime_attributes_available(__method__)
|
291
|
+
runtime_attributes.server_version
|
292
|
+
end
|
293
|
+
|
294
|
+
# Is this device a simulator?
|
295
|
+
# @return [Boolean] Returns true if this device is a simulator.
|
296
|
+
def simulator?
|
297
|
+
# Can be obtain by asking for a device's run_loop_device. This does not
|
298
|
+
# require the app to be launched, but it is expensive
|
299
|
+
# (takes many seconds). run_loop_device is memoized so the expense
|
300
|
+
# is only incurred 1x per device instance.
|
301
|
+
|
302
|
+
# Can also be obtained by asking the server after the app is launched
|
303
|
+
# on the device which would be cheaper.
|
304
|
+
run_loop_device.simulator?
|
305
|
+
end
|
306
|
+
|
307
|
+
# @see Calabash::Location#set_location
|
308
|
+
def set_location(location)
|
309
|
+
if physical_device?
|
310
|
+
raise 'Setting the location is not supported on physical devices'
|
311
|
+
end
|
312
|
+
|
313
|
+
location_data =
|
314
|
+
{
|
315
|
+
'latitude' => location[:latitude],
|
316
|
+
'longitude' => location[:longitude]
|
317
|
+
}
|
318
|
+
|
319
|
+
uia_serialize_and_call(:setLocation, location_data)
|
320
|
+
end
|
321
|
+
|
322
|
+
private
|
323
|
+
|
324
|
+
attr_reader :runtime_attributes
|
325
|
+
|
326
|
+
# @!visibility private
|
327
|
+
def _start_app(application, options={})
|
328
|
+
if application.simulator_bundle?
|
329
|
+
start_app_on_simulator(application, options)
|
330
|
+
|
331
|
+
elsif application.device_binary?
|
332
|
+
start_app_on_physical_device(application, options)
|
333
|
+
else
|
334
|
+
raise "Invalid application #{application} for iOS platform."
|
335
|
+
end
|
336
|
+
{
|
337
|
+
:device => self,
|
338
|
+
:application => application,
|
339
|
+
:uia_strategy => uia_strategy
|
340
|
+
}
|
341
|
+
end
|
342
|
+
|
343
|
+
# @!visibility private
|
344
|
+
def start_app_on_simulator(application, options)
|
345
|
+
@run_loop_device ||= Device.fetch_matching_simulator(identifier)
|
346
|
+
|
347
|
+
if @run_loop_device.nil?
|
348
|
+
raise "Could not find a simulator with a UDID or name matching '#{identifier}'"
|
349
|
+
end
|
350
|
+
|
351
|
+
expect_valid_simulator_state_for_starting(application, @run_loop_device)
|
352
|
+
|
353
|
+
start_app_with_device_and_options(application, @run_loop_device, options)
|
354
|
+
wait_for_server_to_start
|
355
|
+
end
|
356
|
+
|
357
|
+
# @todo No unit tests.
|
358
|
+
# @!visibility private
|
359
|
+
def expect_valid_simulator_state_for_starting(application, run_loop_device)
|
360
|
+
bridge = run_loop_bridge(run_loop_device, application)
|
361
|
+
|
362
|
+
expect_app_installed_on_simulator(bridge)
|
363
|
+
|
364
|
+
installed_app = Calabash::IOS::Application.new(bridge.fetch_app_dir)
|
365
|
+
expect_matching_sha1s(installed_app, application)
|
366
|
+
end
|
367
|
+
|
368
|
+
# @!visibility private
|
369
|
+
def start_app_on_physical_device(application, options)
|
370
|
+
# @todo Cannot check to see if app is already installed.
|
371
|
+
# @todo Cannot check to see if app is different.
|
372
|
+
|
373
|
+
@run_loop_device ||= Device.fetch_matching_physical_device(identifier)
|
374
|
+
|
375
|
+
if @run_loop_device.nil?
|
376
|
+
raise "Could not find a physical device with a UDID or name matching '#{identifier}'"
|
377
|
+
end
|
378
|
+
|
379
|
+
start_app_with_device_and_options(application, @run_loop_device, options)
|
380
|
+
wait_for_server_to_start
|
381
|
+
end
|
382
|
+
|
383
|
+
# @!visibility private
|
384
|
+
def start_app_with_device_and_options(application, run_loop_device, user_defined_options)
|
385
|
+
start_options = merge_start_options!(application, run_loop_device, user_defined_options)
|
386
|
+
@run_loop = RunLoop.run(start_options)
|
387
|
+
@uia_strategy = @run_loop[:uia_strategy]
|
388
|
+
end
|
389
|
+
|
390
|
+
# @!visibility private
|
391
|
+
def wait_for_server_to_start(options={})
|
392
|
+
ensure_test_server_ready(options)
|
393
|
+
device_info = fetch_runtime_attributes
|
394
|
+
@runtime_attributes = new_device_runtime_info(device_info)
|
395
|
+
end
|
396
|
+
|
397
|
+
# @!visibility private
|
398
|
+
def new_device_runtime_info(device_info)
|
399
|
+
RuntimeAttributes.new(device_info)
|
400
|
+
end
|
401
|
+
|
402
|
+
# @!visibility private
|
403
|
+
def _stop_app
|
404
|
+
begin
|
405
|
+
if test_server_responding?
|
406
|
+
parameters = default_stop_app_parameters
|
407
|
+
request = request_factory('exit', parameters)
|
408
|
+
http_client.get(request)
|
409
|
+
else
|
410
|
+
true
|
411
|
+
end
|
412
|
+
rescue Calabash::HTTP::Error => e
|
413
|
+
raise "Could send 'exit' to the app: #{e}"
|
414
|
+
ensure
|
415
|
+
@runtime_attributes = nil
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# @!visibility private
|
420
|
+
def _screenshot(path)
|
421
|
+
request = request_factory('screenshot', {:path => path})
|
422
|
+
begin
|
423
|
+
screenshot = http_client.get(request)
|
424
|
+
File.open(path, 'wb') { |file| file.write screenshot.body }
|
425
|
+
rescue Calabash::HTTP::Error => e
|
426
|
+
raise "Could not send 'screenshot' to the app: #{e}"
|
427
|
+
end
|
428
|
+
path
|
429
|
+
end
|
430
|
+
|
431
|
+
# @!visibility private
|
432
|
+
def _install_app(application)
|
433
|
+
if application.simulator_bundle?
|
434
|
+
@run_loop_device ||= Device.fetch_matching_simulator(identifier)
|
435
|
+
|
436
|
+
if @run_loop_device.nil?
|
437
|
+
raise "Could not find a simulator with a UDID or name matching '#{identifier}'"
|
438
|
+
end
|
439
|
+
|
440
|
+
install_app_on_simulator(application, @run_loop_device)
|
441
|
+
elsif application.device_binary?
|
442
|
+
@run_loop_device ||= Device.fetch_matching_physical_device(identifier)
|
443
|
+
|
444
|
+
if @run_loop_device.nil?
|
445
|
+
raise "Could not find a physical device with a UDID or name matching '#{identifier}'"
|
446
|
+
end
|
447
|
+
install_app_on_physical_device(application, @run_loop_device.udid)
|
448
|
+
else
|
449
|
+
raise "Invalid application #{application} for iOS platform."
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
# @!visibility private
|
454
|
+
def _ensure_app_installed(application)
|
455
|
+
if application.simulator_bundle?
|
456
|
+
@run_loop_device ||= Device.fetch_matching_simulator(identifier)
|
457
|
+
|
458
|
+
if @run_loop_device.nil?
|
459
|
+
raise "Could not find a simulator with a UDID or name matching '#{identifier}'"
|
460
|
+
end
|
461
|
+
|
462
|
+
bridge = run_loop_bridge(@run_loop_device, application)
|
463
|
+
|
464
|
+
if bridge.app_is_installed?
|
465
|
+
installed_app = Calabash::IOS::Application.new(bridge.fetch_app_dir)
|
466
|
+
|
467
|
+
if installed_app.same_sha1_as?(application)
|
468
|
+
true
|
469
|
+
else
|
470
|
+
@logger.log("The sha1 checksum has changed (#{installed_app.sha1} != #{application.sha1}.", :info)
|
471
|
+
install_app_on_simulator(application, @run_loop_device, bridge)
|
472
|
+
end
|
473
|
+
else
|
474
|
+
install_app_on_simulator(application, @run_loop_device, bridge)
|
475
|
+
end
|
476
|
+
elsif application.device_binary?
|
477
|
+
|
478
|
+
@run_loop_device ||= Device.fetch_matching_physical_device(identifier)
|
479
|
+
|
480
|
+
if @run_loop_device.nil?
|
481
|
+
raise "Could not find a physical device with a UDID or name matching '#{identifier}'"
|
482
|
+
end
|
483
|
+
|
484
|
+
ensure_app_installed_on_physical_device(application, @run_loop_device.udid)
|
485
|
+
else
|
486
|
+
raise "Invalid application #{application} for iOS platform."
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
# @!visibility private
|
491
|
+
def _clear_app_data(application)
|
492
|
+
if application.simulator_bundle?
|
493
|
+
@run_loop_device ||= Device.fetch_matching_simulator(identifier)
|
494
|
+
|
495
|
+
if @run_loop_device.nil?
|
496
|
+
raise "Could not find a simulator with a UDID or name matching '#{identifier}'"
|
497
|
+
end
|
498
|
+
|
499
|
+
bridge = run_loop_bridge(@run_loop_device, application)
|
500
|
+
if bridge.app_is_installed?
|
501
|
+
clear_app_data_on_simulator(application, @run_loop_device, bridge)
|
502
|
+
else
|
503
|
+
true
|
504
|
+
end
|
505
|
+
elsif application.device_binary?
|
506
|
+
@run_loop_device ||= Device.fetch_matching_physical_device(identifier)
|
507
|
+
|
508
|
+
if @run_loop_device.nil?
|
509
|
+
raise "Could not find a physical device with a UDID or name matching '#{identifier}'"
|
510
|
+
end
|
511
|
+
|
512
|
+
clear_app_data_on_physical_device(application, @run_loop_device.udid)
|
513
|
+
else
|
514
|
+
raise "Invalid application #{application} for iOS platform."
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
# @!visibility private
|
519
|
+
def enter_text(text)
|
520
|
+
# @todo implement this
|
521
|
+
raise 'ni'
|
522
|
+
end
|
523
|
+
|
524
|
+
# @!visibility private
|
525
|
+
def clear_app_data_on_simulator(application, run_loop_device, bridge)
|
526
|
+
begin
|
527
|
+
bridge.reset_app_sandbox
|
528
|
+
true
|
529
|
+
rescue StandardError => e
|
530
|
+
raise "Could not clear app data for #{application.identifier} on #{run_loop_device}: #{e}"
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
# @!visibility private
|
535
|
+
def _uninstall_app(application)
|
536
|
+
if application.simulator_bundle?
|
537
|
+
@run_loop_device ||= Device.fetch_matching_simulator(identifier)
|
538
|
+
|
539
|
+
if @run_loop_device.nil?
|
540
|
+
raise "Could not find a simulator with a UDID or name matching '#{identifier}'"
|
541
|
+
end
|
542
|
+
|
543
|
+
bridge = run_loop_bridge(@run_loop_device, application)
|
544
|
+
if bridge.app_is_installed?
|
545
|
+
uninstall_app_on_simulator(application, @run_loop_device, bridge)
|
546
|
+
else
|
547
|
+
true
|
548
|
+
end
|
549
|
+
elsif application.device_binary?
|
550
|
+
@run_loop_device ||= Device.fetch_matching_physical_device(identifier)
|
551
|
+
|
552
|
+
if @run_loop_device.nil?
|
553
|
+
raise "Could not find a physical device with a UDID or name matching '#{identifier}'"
|
554
|
+
end
|
555
|
+
|
556
|
+
uninstall_app_on_physical_device(application, @run_loop_device.udid)
|
557
|
+
else
|
558
|
+
raise "Invalid application #{application} for iOS platform."
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
# @!visibility private
|
563
|
+
def uninstall_app_on_simulator(application, run_loop_device, bridge)
|
564
|
+
begin
|
565
|
+
bridge.uninstall
|
566
|
+
true
|
567
|
+
rescue e
|
568
|
+
raise "Could not uninstall #{application.identifier} on #{run_loop_device}: #{e}"
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
# @!visibility private
|
573
|
+
def default_stop_app_parameters
|
574
|
+
{
|
575
|
+
:post_resign_active_delay => 0.4,
|
576
|
+
:post_will_terminate_delay => 0.4,
|
577
|
+
:exit_code => 0
|
578
|
+
}
|
579
|
+
end
|
580
|
+
|
581
|
+
# @!visibility private
|
582
|
+
def request_factory(route, parameters={})
|
583
|
+
Calabash::HTTP::Request.new(route, parameters)
|
584
|
+
end
|
585
|
+
|
586
|
+
# @!visibility private
|
587
|
+
# RunLoop::Device is incredibly slow; don't call it more than once.
|
588
|
+
def run_loop_device
|
589
|
+
@run_loop_device ||= RunLoop::Device.device_with_identifier(identifier)
|
590
|
+
end
|
591
|
+
|
592
|
+
# @!visibility private
|
593
|
+
# Do not memoize this. The Bridge initializer does a bunch of work to
|
594
|
+
# prepare the environment for simctl actions.
|
595
|
+
def run_loop_bridge(run_loop_simulator_device, application)
|
596
|
+
RunLoop::Simctl::Bridge.new(run_loop_simulator_device, application.path)
|
597
|
+
end
|
598
|
+
|
599
|
+
# @!visibility private
|
600
|
+
def install_app_on_simulator(application, run_loop_device, run_loop_bridge = nil)
|
601
|
+
begin
|
602
|
+
|
603
|
+
if run_loop_bridge.nil?
|
604
|
+
bridge = run_loop_bridge(run_loop_device, application)
|
605
|
+
else
|
606
|
+
bridge = run_loop_bridge
|
607
|
+
end
|
608
|
+
|
609
|
+
bridge.uninstall
|
610
|
+
bridge.install
|
611
|
+
rescue StandardError => e
|
612
|
+
raise "Could not install #{application} on #{run_loop_device}: #{e}"
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
# @!visibility private
|
617
|
+
# Expensive!
|
618
|
+
def Device.fetch_matching_simulator(udid_or_name)
|
619
|
+
sim_control = RunLoop::SimControl.new
|
620
|
+
sim_control.simulators.detect do |sim|
|
621
|
+
sim.instruments_identifier == udid_or_name ||
|
622
|
+
sim.udid == udid_or_name
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
626
|
+
# @!visibility private
|
627
|
+
# Very expensive!
|
628
|
+
def Device.fetch_matching_physical_device(udid_or_name)
|
629
|
+
xctools = RunLoop::XCTools.new
|
630
|
+
xctools.instruments(:devices).detect do |device|
|
631
|
+
device.name == udid_or_name ||
|
632
|
+
device.udid == udid_or_name
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
636
|
+
# @!visibility private
|
637
|
+
# @todo Should this take a run_loop_device as an argument, rather than
|
638
|
+
# an identifier? Since calls to instruments and simctl are very
|
639
|
+
# expensive we want to do as few of them as possible. Maybe the
|
640
|
+
# localhost? check should be done outside of this method? If nothing
|
641
|
+
# else, the result of Device.fetch_matching_simulator should be captured
|
642
|
+
# in @run_loop_device.
|
643
|
+
def self.expect_compatible_server_endpoint(identifier, server)
|
644
|
+
if server.localhost?
|
645
|
+
run_loop_device = Device.fetch_matching_simulator(identifier)
|
646
|
+
if run_loop_device.nil?
|
647
|
+
Logger.error("The identifier for this device is '#{identifier}'")
|
648
|
+
Logger.error('which resolves to a physical device.')
|
649
|
+
Logger.error("The server endpoint '#{server.endpoint}' is for an iOS Simulator.")
|
650
|
+
Logger.error('Use CAL_ENDPOINT to specify the IP address of your device')
|
651
|
+
Logger.error("Ex. $ CAL_ENDPOINT=http://10.0.1.2:37265 CAL_DEVICE_ID=#{identifier} be calabash ...")
|
652
|
+
raise "Invalid device endpoint '#{server.endpoint}'"
|
653
|
+
end
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
# @!visibility private
|
658
|
+
def expect_app_installed_on_simulator(bridge)
|
659
|
+
unless bridge.app_is_installed?
|
660
|
+
raise 'App is not installed, you need to install it first.'
|
661
|
+
end
|
662
|
+
true
|
663
|
+
end
|
664
|
+
|
665
|
+
# @!visibility private
|
666
|
+
def expect_matching_sha1s(installed_app, new_app)
|
667
|
+
unless installed_app.same_sha1_as?(new_app)
|
668
|
+
logger.log('The installed application and the one under test are different.', :error)
|
669
|
+
logger.log("Installed path: #{installed_app.path}", :error)
|
670
|
+
logger.log(" New path: #{new_app.path}", :error)
|
671
|
+
logger.log("Installed SHA1: #{installed_app.sha1}", :error)
|
672
|
+
logger.log(" New SHA1: #{new_app.sha1}", :error)
|
673
|
+
raise 'The installed app is different from the app under test. You must install the new app before starting'
|
674
|
+
end
|
675
|
+
true
|
676
|
+
end
|
677
|
+
|
678
|
+
# @!visibility private
|
679
|
+
def uia_strategy_from_environment(run_loop_device)
|
680
|
+
Environment::UIA_STRATEGY || default_uia_strategy(run_loop_device)
|
681
|
+
end
|
682
|
+
|
683
|
+
# @!visibility private
|
684
|
+
# @todo Needs a bunch of work; see the argument munging in Calabash 0.x Launcher.
|
685
|
+
def merge_start_options!(application, run_loop_device, options_from_user)
|
686
|
+
strategy = uia_strategy_from_environment(run_loop_device)
|
687
|
+
|
688
|
+
default_options =
|
689
|
+
{
|
690
|
+
:app => application.path,
|
691
|
+
:bundle_id => application.identifier,
|
692
|
+
:device_target => run_loop_device.instruments_identifier,
|
693
|
+
:uia_strategy => strategy
|
694
|
+
}
|
695
|
+
@start_options = default_options.merge(options_from_user)
|
696
|
+
end
|
697
|
+
|
698
|
+
# @todo Move to run-loop!?!
|
699
|
+
# @todo Not tested locally!
|
700
|
+
def default_uia_strategy(run_loop_device)
|
701
|
+
default = :preferences
|
702
|
+
if run_loop_device.physical_device?
|
703
|
+
# `setPreferencesValueForKey` on iOS 8 devices is broken in Xcode 6
|
704
|
+
#
|
705
|
+
# rdar://18296714
|
706
|
+
# http://openradar.appspot.com/radar?id=5891145586442240
|
707
|
+
# :preferences strategy is broken on iOS 8.0
|
708
|
+
if run_loop_device.version >= RunLoop::Version.new('8.0')
|
709
|
+
default = :host
|
710
|
+
end
|
711
|
+
end
|
712
|
+
default
|
713
|
+
end
|
714
|
+
|
715
|
+
# @!visibility private
|
716
|
+
def fetch_runtime_attributes
|
717
|
+
request = request_factory('version')
|
718
|
+
body = http_client.get(request).body
|
719
|
+
begin
|
720
|
+
JSON.parse(body)
|
721
|
+
rescue TypeError, JSON::ParserError => _
|
722
|
+
raise "Could not parse response '#{body}'; the app has probably crashed"
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
726
|
+
# @!visibility private
|
727
|
+
def expect_runtime_attributes_available(method_name)
|
728
|
+
if runtime_attributes.nil?
|
729
|
+
logger.log("The method '#{method_name}' is not available to IOS::Device until", :info)
|
730
|
+
logger.log('the app has been launched with Calabash start_app.', :info)
|
731
|
+
raise "The method '#{method_name}' can only be called after the app has been launched"
|
732
|
+
end
|
733
|
+
true
|
734
|
+
end
|
735
|
+
|
736
|
+
def instruments_pid
|
737
|
+
pids = RunLoop::Instruments.new.instruments_pids
|
738
|
+
if pids
|
739
|
+
pids.first
|
740
|
+
else
|
741
|
+
nil
|
742
|
+
end
|
743
|
+
end
|
744
|
+
|
745
|
+
# Assumes the app is already running and the server can be reached.
|
746
|
+
# @todo It might make sense to cache the uia_strategy on the _server_
|
747
|
+
# to avoid having to guess.
|
748
|
+
def attach_to_run_loop(run_loop_device, uia_strategy)
|
749
|
+
if uia_strategy
|
750
|
+
strategy = uia_strategy
|
751
|
+
else
|
752
|
+
strategy = uia_strategy_from_environment(run_loop_device)
|
753
|
+
end
|
754
|
+
|
755
|
+
if strategy == :host
|
756
|
+
@run_loop = RunLoop::HostCache.default.read
|
757
|
+
@uia_strategy = :host
|
758
|
+
else
|
759
|
+
pid = instruments_pid
|
760
|
+
@run_loop = {}
|
761
|
+
@run_loop[:uia_strategy] = strategy
|
762
|
+
@run_loop[:pid] = pid
|
763
|
+
@uia_strategy = strategy
|
764
|
+
end
|
765
|
+
|
766
|
+
# populate the @runtime_attributes
|
767
|
+
wait_for_server_to_start({:timeout => 2})
|
768
|
+
{
|
769
|
+
:device => self,
|
770
|
+
:uia_strategy => strategy
|
771
|
+
}
|
772
|
+
end
|
773
|
+
|
774
|
+
def world_module
|
775
|
+
Calabash::IOS
|
776
|
+
end
|
777
|
+
end
|
778
|
+
end
|
779
|
+
end
|