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,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
|