sc-frank-cucumber 1.2.1.00af28c
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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +38 -0
- data/bin/frank +6 -0
- data/bin/frank-skeleton +33 -0
- data/frank-skeleton/features/my_first.feature +12 -0
- data/frank-skeleton/features/step_definitions/launch_steps.rb +20 -0
- data/frank-skeleton/features/support/env.rb +8 -0
- data/frank-skeleton/frank_static_resources.bundle/ViewAttributeMapping.plist +63 -0
- data/frank-skeleton/frank_static_resources.bundle/ViewAttributeMappingMac.plist +99 -0
- data/frank-skeleton/frank_static_resources.bundle/images/ajax-loader.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/file.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/folder-closed.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/folder.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/loader.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/loader.png +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/minus.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/plus.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/treeview-black-line.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/treeview-black.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/treeview-default-line.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/treeview-default.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/treeview-famfamfam-line.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/treeview-famfamfam.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/treeview-gray-line.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/treeview-gray.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/treeview-red-line.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/images/treeview-red.gif +0 -0
- data/frank-skeleton/frank_static_resources.bundle/index.html +86 -0
- data/frank-skeleton/frank_static_resources.bundle/index.html.haml +76 -0
- data/frank-skeleton/frank_static_resources.bundle/js/accessible_views_view.coffee +41 -0
- data/frank-skeleton/frank_static_resources.bundle/js/accessible_views_view.js +46 -0
- data/frank-skeleton/frank_static_resources.bundle/js/controller.coffee +134 -0
- data/frank-skeleton/frank_static_resources.bundle/js/controller.js +139 -0
- data/frank-skeleton/frank_static_resources.bundle/js/details_view.coffee +42 -0
- data/frank-skeleton/frank_static_resources.bundle/js/details_view.js +51 -0
- data/frank-skeleton/frank_static_resources.bundle/js/dropdown_control.coffee +64 -0
- data/frank-skeleton/frank_static_resources.bundle/js/dropdown_control.js +73 -0
- data/frank-skeleton/frank_static_resources.bundle/js/ersatz_model.coffee +46 -0
- data/frank-skeleton/frank_static_resources.bundle/js/ersatz_model.js +60 -0
- data/frank-skeleton/frank_static_resources.bundle/js/ersatz_view.coffee +167 -0
- data/frank-skeleton/frank_static_resources.bundle/js/ersatz_view.js +205 -0
- data/frank-skeleton/frank_static_resources.bundle/js/experiment_bar_model.coffee +10 -0
- data/frank-skeleton/frank_static_resources.bundle/js/experiment_bar_model.js +17 -0
- data/frank-skeleton/frank_static_resources.bundle/js/experiment_bar_view.coffee +44 -0
- data/frank-skeleton/frank_static_resources.bundle/js/experiment_bar_view.js +63 -0
- data/frank-skeleton/frank_static_resources.bundle/js/frank.coffee +96 -0
- data/frank-skeleton/frank_static_resources.bundle/js/frank.js +146 -0
- data/frank-skeleton/frank_static_resources.bundle/js/lib/backbone.js +1431 -0
- data/frank-skeleton/frank_static_resources.bundle/js/lib/coffee-script.js +8 -0
- data/frank-skeleton/frank_static_resources.bundle/js/lib/jquery-ui.min.js +405 -0
- data/frank-skeleton/frank_static_resources.bundle/js/lib/jquery.min.js +4 -0
- data/frank-skeleton/frank_static_resources.bundle/js/lib/jquery.treeview.js +251 -0
- data/frank-skeleton/frank_static_resources.bundle/js/lib/json2.js +481 -0
- data/frank-skeleton/frank_static_resources.bundle/js/lib/raphael.js +5815 -0
- data/frank-skeleton/frank_static_resources.bundle/js/lib/require.js +2053 -0
- data/frank-skeleton/frank_static_resources.bundle/js/lib/underscore.js +1059 -0
- data/frank-skeleton/frank_static_resources.bundle/js/main.coffee +27 -0
- data/frank-skeleton/frank_static_resources.bundle/js/main.js +29 -0
- data/frank-skeleton/frank_static_resources.bundle/js/tabs_controller.coffee +13 -0
- data/frank-skeleton/frank_static_resources.bundle/js/tabs_controller.js +22 -0
- data/frank-skeleton/frank_static_resources.bundle/js/toast_controller.coffee +15 -0
- data/frank-skeleton/frank_static_resources.bundle/js/toast_controller.js +28 -0
- data/frank-skeleton/frank_static_resources.bundle/js/transform_stack.coffee +59 -0
- data/frank-skeleton/frank_static_resources.bundle/js/transform_stack.js +78 -0
- data/frank-skeleton/frank_static_resources.bundle/js/tree_view.coffee +53 -0
- data/frank-skeleton/frank_static_resources.bundle/js/tree_view.js +64 -0
- data/frank-skeleton/frank_static_resources.bundle/js/view_hier_model.coffee +37 -0
- data/frank-skeleton/frank_static_resources.bundle/js/view_hier_model.js +48 -0
- data/frank-skeleton/frank_static_resources.bundle/js/view_model.coffee +39 -0
- data/frank-skeleton/frank_static_resources.bundle/js/view_model.js +62 -0
- data/frank-skeleton/frank_static_resources.bundle/pictos/index.html +329 -0
- data/frank-skeleton/frank_static_resources.bundle/pictos/pictos-web.eot +0 -0
- data/frank-skeleton/frank_static_resources.bundle/pictos/pictos-web.svg +114 -0
- data/frank-skeleton/frank_static_resources.bundle/pictos/pictos-web.ttf +0 -0
- data/frank-skeleton/frank_static_resources.bundle/pictos/pictos-web.woff +0 -0
- data/frank-skeleton/frank_static_resources.bundle/pictos/pictos.css +20 -0
- data/frank-skeleton/frank_static_resources.bundle/pictos/pictos_base64.css +18 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/css/symbiote.css +1 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_elements.scss +28 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_header.scss +61 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_inspect_tabs_list_tabs.scss +194 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_jquery.treeview.scss +68 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_jqui.scss +2 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_layout.scss +13 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_mixins.sass +137 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_reset.scss +32 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_selector_test_toolbar.scss +81 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_solarized.scss +16 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_typography.scss +11 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_unicode.scss +3 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/_z_index.scss +2 -0
- data/frank-skeleton/frank_static_resources.bundle/stylesheets/sass/symbiote.scss +26 -0
- data/frank-skeleton/frankify.xcconfig.tt +6 -0
- data/frank-skeleton/libCocoaAsyncSocket.a +0 -0
- data/frank-skeleton/libCocoaAsyncSocketMac.a +0 -0
- data/frank-skeleton/libCocoaHTTPServer.a +0 -0
- data/frank-skeleton/libCocoaHTTPServerMac.a +0 -0
- data/frank-skeleton/libCocoaLumberjack.a +0 -0
- data/frank-skeleton/libCocoaLumberjackMac.a +0 -0
- data/frank-skeleton/libFrank.a +0 -0
- data/frank-skeleton/libFrankMac.a +0 -0
- data/frank-skeleton/libShelley.a +0 -0
- data/frank-skeleton/libShelleyMac.a +0 -0
- data/frank-skeleton/plugins/.empty_directory +0 -0
- data/lib/frank-cucumber.rb +15 -0
- data/lib/frank-cucumber/app_bundle_locator.rb +58 -0
- data/lib/frank-cucumber/bonjour.rb +73 -0
- data/lib/frank-cucumber/cli.rb +299 -0
- data/lib/frank-cucumber/color_helper.rb +13 -0
- data/lib/frank-cucumber/console.rb +28 -0
- data/lib/frank-cucumber/core_frank_steps.rb +260 -0
- data/lib/frank-cucumber/frank.xcconfig.erb +17 -0
- data/lib/frank-cucumber/frank_helper.rb +459 -0
- data/lib/frank-cucumber/frank_localize.rb +43 -0
- data/lib/frank-cucumber/frank_mac_helper.rb +120 -0
- data/lib/frank-cucumber/frankifier.rb +150 -0
- data/lib/frank-cucumber/gateway.rb +135 -0
- data/lib/frank-cucumber/gesture_helper.rb +99 -0
- data/lib/frank-cucumber/host_scripting.rb +96 -0
- data/lib/frank-cucumber/keyboard_helper.rb +69 -0
- data/lib/frank-cucumber/launcher.rb +70 -0
- data/lib/frank-cucumber/localize.yml +104 -0
- data/lib/frank-cucumber/location_helper.rb +20 -0
- data/lib/frank-cucumber/mac_launcher.rb +35 -0
- data/lib/frank-cucumber/plugins/plugin.rb +57 -0
- data/lib/frank-cucumber/rect.rb +26 -0
- data/lib/frank-cucumber/scroll_helper.rb +24 -0
- data/lib/frank-cucumber/version.rb +5 -0
- data/lib/frank-cucumber/wait_helper.rb +57 -0
- data/sc-frank-cucumber.gemspec +37 -0
- data/test/keyboard_helper_test.rb +84 -0
- data/test/launcher_test.rb +57 -0
- data/test/rect_test.rb +25 -0
- data/test/test_helper.rb +16 -0
- metadata +395 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
module Frank module Cucumber
|
2
|
+
|
3
|
+
module ColorHelper
|
4
|
+
def colorize(text, color_code)
|
5
|
+
"\e[#{color_code}m#{text}\e[0m"
|
6
|
+
end
|
7
|
+
|
8
|
+
def red(text); colorize(text, 31); end
|
9
|
+
def green(text); colorize(text, 32); end
|
10
|
+
def blue(text); colorize(text, 34); end
|
11
|
+
end
|
12
|
+
|
13
|
+
end end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'frank-cucumber/frank_helper'
|
2
|
+
require 'frank-cucumber/frank_mac_helper'
|
3
|
+
require 'frank-cucumber/launcher'
|
4
|
+
|
5
|
+
module Frank
|
6
|
+
class Console
|
7
|
+
include Frank::Cucumber::FrankHelper
|
8
|
+
include Frank::Cucumber::FrankMacHelper
|
9
|
+
include Frank::Cucumber::Launcher
|
10
|
+
|
11
|
+
def check_for_running_app
|
12
|
+
print 'connecting to app...'
|
13
|
+
begin
|
14
|
+
Timeout::timeout(5) do
|
15
|
+
until frankly_ping
|
16
|
+
print '.'
|
17
|
+
sleep 0.2
|
18
|
+
end
|
19
|
+
end
|
20
|
+
rescue Timeout::Error
|
21
|
+
puts ' failed to connect.'
|
22
|
+
return false
|
23
|
+
end
|
24
|
+
puts ' connected'
|
25
|
+
return true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
WAIT_TIMEOUT = ENV['WAIT_TIMEOUT'].to_i || 240
|
2
|
+
|
3
|
+
require 'rspec/expectations'
|
4
|
+
|
5
|
+
# -- See -- #
|
6
|
+
Then /^I wait to see "([^\"]*)"$/ do |expected_mark|
|
7
|
+
quote = get_selector_quote(expected_mark)
|
8
|
+
wait_until(:message => "waited to see view marked #{quote}#{expected_mark}#{quote}"){
|
9
|
+
view_with_mark_exists( expected_mark )
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
Then /^I wait to not see "([^\"]*)"$/ do |expected_mark|
|
14
|
+
sleep 3 # ugh, this should be removed but I'm worried it'll break existing tests
|
15
|
+
quote = get_selector_quote(expected_mark)
|
16
|
+
wait_until(:message => "waited to not see view marked #{quote}#{expected_mark}#{quote}"){
|
17
|
+
!view_with_mark_exists( expected_mark )
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
Then /^I should see a navigation bar titled "([^\"]*)"$/ do |expected_mark|
|
22
|
+
quote = get_selector_quote(expected_mark)
|
23
|
+
check_element_exists( "navigationItemView marked:#{quote}#{expected_mark}#{quote}" )
|
24
|
+
end
|
25
|
+
|
26
|
+
Then /^I wait to see a navigation bar titled "([^\"]*)"$/ do |expected_mark|
|
27
|
+
quote = get_selector_quote(expected_mark)
|
28
|
+
wait_until( :timeout => 30, :message => "waited to see a navigation bar titled #{quote}#{expected_mark}#{quote}" ) {
|
29
|
+
element_exists( "navigationItemView marked:#{quote}#{expected_mark}#{quote}" )
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
Then /^I wait to not see a navigation bar titled "([^\"]*)"$/ do |expected_mark|
|
34
|
+
quote = get_selector_quote(expected_mark)
|
35
|
+
wait_until( :timeout => 30, :message => "waited to not see a navigation bar titled #{quote}#{expected_mark}#{quote}" ) {
|
36
|
+
!element_exists( "navigationItemView marked:#{quote}#{expected_mark}#{quote}" )
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
Then /^I should see a "([^\"]*)" button$/ do |expected_mark|
|
41
|
+
quote = get_selector_quote(expected_mark)
|
42
|
+
check_element_exists("button marked:#{quote}#{expected_mark}#{quote}")
|
43
|
+
end
|
44
|
+
|
45
|
+
Then /^I should see "([^\"]*)"$/ do |expected_mark|
|
46
|
+
check_view_with_mark_exists(expected_mark)
|
47
|
+
end
|
48
|
+
|
49
|
+
Then /^I should not see "([^\"]*)"$/ do |expected_mark|
|
50
|
+
quote = get_selector_quote(expected_mark)
|
51
|
+
check_element_does_not_exist_or_is_not_visible("view marked:#{quote}#{expected_mark}#{quote}")
|
52
|
+
end
|
53
|
+
|
54
|
+
Then /I should see the following:/ do |table|
|
55
|
+
values = frankly_map( 'view', 'FEX_accessibilityLabel' )
|
56
|
+
table.raw.each do |expected_mark|
|
57
|
+
values.should include( expected_mark.first )
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Then /I should not see the following:/ do |table|
|
62
|
+
values = frankly_map( 'view', 'FEX_accessibilityLabel' )
|
63
|
+
table.raw.each do |expected_mark|
|
64
|
+
values.should_not include( expected_mark.first )
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
Then /^I should see an alert view titled "([^\"]*)"$/ do |expected_mark|
|
69
|
+
values = frankly_map( 'alertView', 'title')
|
70
|
+
values.should include(expected_mark)
|
71
|
+
end
|
72
|
+
|
73
|
+
Then /^I should see an alert view with the message "([^\"]*)"$/ do |expected_mark|
|
74
|
+
values = frankly_map( 'alertView', 'message')
|
75
|
+
values.should include(expected_mark)
|
76
|
+
end
|
77
|
+
|
78
|
+
Then /^I should not see an alert view$/ do
|
79
|
+
check_element_does_not_exist( 'alertView' )
|
80
|
+
end
|
81
|
+
|
82
|
+
Then /^I should see an element of class "([^\"]*)" with name "([^\"]*)" with the following labels: "([^\"]*)"$/ do |className, classLabel, listOfLabels|
|
83
|
+
arrayOfLabels = listOfLabels.split(',');
|
84
|
+
arrayOfLabels.each do |label|
|
85
|
+
quote = get_selector_quote(label)
|
86
|
+
check_element_exists("view marked:'#{classLabel}' parent view:'#{className}' descendant view marked:#{quote}#{label}#{quote}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
Then /^I should see an element of class "([^\"]*)" with name "([^\"]*)" with a "([^\"]*)" button$/ do |className, classLabel, buttonName|
|
91
|
+
quote = get_selector_quote(buttonName)
|
92
|
+
check_element_exists("view marked:'#{classLabel}' parent view:'#{className}' descendant button marked:#{quote}#{buttonName}#{quote}")
|
93
|
+
end
|
94
|
+
|
95
|
+
Then /^I should not see a hidden button marked "([^\"]*)"$/ do |expected_mark|
|
96
|
+
quote = get_selector_quote(expected_mark)
|
97
|
+
element_is_not_hidden("button marked:#{quote}#{expected_mark}#{quote}").should be_false
|
98
|
+
end
|
99
|
+
|
100
|
+
Then /^I should see a nonhidden button marked "([^\"]*)"$/ do |expected_mark|
|
101
|
+
quote = get_selector_quote(expected_mark)
|
102
|
+
element_is_not_hidden("button marked:#{quote}#{expected_mark}#{quote}").should be_true
|
103
|
+
end
|
104
|
+
|
105
|
+
Then /^I should see an element of class "([^\"]*)"$/ do |className|
|
106
|
+
element_is_not_hidden("view:'#{className}'").should be_true
|
107
|
+
end
|
108
|
+
|
109
|
+
Then /^I should not see an element of class "([^\"]*)"$/ do |className|
|
110
|
+
selector = "view:'#{className}'"
|
111
|
+
element_exists_and_is_not_hidden = element_exists( selector ) && element_is_not_hidden(selector)
|
112
|
+
element_exists_and_is_not_hidden.should be_false
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
# -- Rotate -- #
|
117
|
+
Given /^the device is in (a )?(landscape|portrait) orientation$/ do |_,orientation|
|
118
|
+
frankly_set_orientation orientation
|
119
|
+
end
|
120
|
+
|
121
|
+
When /^I simulate a memory warning$/ do
|
122
|
+
simulate_memory_warning
|
123
|
+
end
|
124
|
+
|
125
|
+
Then /^I rotate to the "([^\"]*)"$/ do |direction|
|
126
|
+
if direction == "right"
|
127
|
+
rotate_simulator_right
|
128
|
+
elsif direction == "left"
|
129
|
+
rotate_simulator_left
|
130
|
+
else
|
131
|
+
raise %Q|Rotation direction specified ("#{direction}") is invalid. Please specify right or left.|
|
132
|
+
end
|
133
|
+
sleep 1
|
134
|
+
end
|
135
|
+
|
136
|
+
# -- touch -- #
|
137
|
+
# generic views
|
138
|
+
When /^I touch "([^\"]*)"$/ do |mark|
|
139
|
+
quote = get_selector_quote(mark)
|
140
|
+
selector = "view marked:#{quote}#{mark}#{quote} first"
|
141
|
+
if element_exists(selector)
|
142
|
+
touch( selector )
|
143
|
+
else
|
144
|
+
raise "Could not touch [#{mark}], it does not exist."
|
145
|
+
end
|
146
|
+
sleep 1
|
147
|
+
end
|
148
|
+
|
149
|
+
When /^I touch "([^\"]*)" if exists$/ do |mark|
|
150
|
+
sleep 1
|
151
|
+
quote = get_selector_quote(mark)
|
152
|
+
selector = "view marked:#{quote}#{mark}#{quote} first"
|
153
|
+
if element_exists(selector)
|
154
|
+
touch(selector)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# table cells
|
159
|
+
When /^I touch the first table cell$/ do
|
160
|
+
touch("tableViewCell first")
|
161
|
+
end
|
162
|
+
|
163
|
+
When /^I touch the table cell marked "([^\"]*)"$/ do |mark|
|
164
|
+
quote = get_selector_quote(mark)
|
165
|
+
touch("tableViewCell marked:#{quote}#{mark}#{quote}")
|
166
|
+
end
|
167
|
+
|
168
|
+
When /^I touch the (\d*)(?:st|nd|rd|th)? table cell$/ do |ordinal|
|
169
|
+
ordinal = ordinal.to_i - 1
|
170
|
+
touch("tableViewCell index:#{ordinal}")
|
171
|
+
end
|
172
|
+
|
173
|
+
Then /I touch the following:/ do |table|
|
174
|
+
values = frankly_map( 'view', 'FEX_accessibilityLabel' )
|
175
|
+
table.raw.each do |expected_mark|
|
176
|
+
quote = get_selector_quote(expected_mark)
|
177
|
+
touch( "view marked:#{quote}#{expected_mark}#{quote}" )
|
178
|
+
sleep 2
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# buttons
|
183
|
+
When /^I touch the button marked "([^\"]*)"$/ do |mark|
|
184
|
+
quote = get_selector_quote(mark)
|
185
|
+
touch( "button marked:#{quote}#{mark}#{quote}" )
|
186
|
+
end
|
187
|
+
|
188
|
+
# action sheets
|
189
|
+
When /^I touch the "([^\"]*)" action sheet button$/ do |mark|
|
190
|
+
quote = get_selector_quote(mark)
|
191
|
+
touch( "actionSheet threePartButton marked:#{quote}#{mark}#{quote}" )
|
192
|
+
end
|
193
|
+
|
194
|
+
When /^I touch the (\d*)(?:st|nd|rd|th)? action sheet button$/ do |ordinal|
|
195
|
+
ordinal = ordinal.to_i
|
196
|
+
touch( "actionSheet threePartButton tag:#{ordinal}" )
|
197
|
+
end
|
198
|
+
|
199
|
+
# alert views
|
200
|
+
When /^I touch the "([^\"]*)" alert view button$/ do |mark|
|
201
|
+
quote = get_selector_quote(mark)
|
202
|
+
touch( "alertView threePartButton marked:#{quote}#{mark}#{quote}" )
|
203
|
+
end
|
204
|
+
|
205
|
+
When /^I touch the (\d*)(?:st|nd|rd|th)? alert view button$/ do |ordinal|
|
206
|
+
ordinal = ordinal.to_i
|
207
|
+
touch( "alertView threePartButton tag:#{ordinal}" )
|
208
|
+
end
|
209
|
+
|
210
|
+
# -- switch -- #
|
211
|
+
|
212
|
+
When /^I flip switch "([^\"]*)"$/ do |mark|
|
213
|
+
quote = get_selector_quote(mark)
|
214
|
+
touch("view:'UISwitch' marked:#{quote}#{mark}#{quote}")
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
Then /^switch "([^\"]*)" should be (on|off)$/ do |mark,expected_state|
|
219
|
+
expected_state = expected_state == 'on'
|
220
|
+
|
221
|
+
quote = get_selector_quote(mark)
|
222
|
+
selector = "view:'UISwitch' marked:#{quote}#{mark}#{quote}"
|
223
|
+
switch_states = frankly_map( selector, "isOn" )
|
224
|
+
|
225
|
+
switch_states.each do |switch_state|
|
226
|
+
switch_state.should == expected_state
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# -- misc -- #
|
231
|
+
|
232
|
+
When /^I wait for ([\d\.]+) second(?:s)?$/ do |num_seconds|
|
233
|
+
num_seconds = num_seconds.to_f
|
234
|
+
sleep num_seconds
|
235
|
+
end
|
236
|
+
|
237
|
+
Then /^a pop\-over menu is displayed with the following:$/ do |table|
|
238
|
+
sleep 1
|
239
|
+
table.raw.each do |expected_mark|
|
240
|
+
quote = get_selector_quote(expected_mark)
|
241
|
+
check_element_exists "actionSheet view marked:#{quote}#{expected_mark}#{quote}"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
Then /^I navigate back$/ do
|
246
|
+
touch( "navigationItemButtonView" )
|
247
|
+
wait_for_nothing_to_be_animating
|
248
|
+
end
|
249
|
+
|
250
|
+
When /^I dump the DOM$/ do
|
251
|
+
dom = frankly_dump
|
252
|
+
end
|
253
|
+
|
254
|
+
When /^I quit the simulator/ do
|
255
|
+
quit_simulator
|
256
|
+
end
|
257
|
+
|
258
|
+
When /^I reset the simulator/ do
|
259
|
+
simulator_reset_data
|
260
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#include "frankify.xcconfig"
|
2
|
+
<% plugins.each do |plugin| %>
|
3
|
+
#include "plugins/<%=plugin.name%>/<%=plugin.xcconfig%>"
|
4
|
+
<% end %>
|
5
|
+
INSTALL_PATH = /./
|
6
|
+
|
7
|
+
<% plugin_names = plugins.map {|plugin| plugin.name } %>
|
8
|
+
|
9
|
+
FRANK_LDFLAGS_iphoneos = $(FRANK_CORE_LDFLAGS) <%= plugin_names.map {|name| "$(#{name.upcase}_LDFLAGS)"}.join(' ') %>
|
10
|
+
|
11
|
+
FRANK_LDFLAGS_macosx = $(FRANK_CORE_MAC_LDFLAGS) <%= plugin_names.map {|name| "$(#{name.upcase}_MAC_LDFLAGS)"}.join(' ') %>
|
12
|
+
|
13
|
+
FRANK_LDFLAGS_iphonesimulator = $(FRANK_LDFLAGS_iphoneos)
|
14
|
+
|
15
|
+
FRANK_LDFLAGS = $(FRANK_LDFLAGS_$(PLATFORM_NAME))
|
16
|
+
|
17
|
+
FRANK_GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS = $(FRANK_CORE_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS) <%= plugin_names.map {|name| "$(#{name.upcase}_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPS)"}.join(' ') %>
|
@@ -0,0 +1,459 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'frank-cucumber/gateway'
|
3
|
+
require 'frank-cucumber/host_scripting'
|
4
|
+
require 'frank-cucumber/wait_helper'
|
5
|
+
require 'frank-cucumber/keyboard_helper'
|
6
|
+
require 'frank-cucumber/scroll_helper'
|
7
|
+
require 'frank-cucumber/gesture_helper'
|
8
|
+
require 'frank-cucumber/location_helper'
|
9
|
+
require 'frank-cucumber/bonjour'
|
10
|
+
require 'frank-cucumber/rect.rb'
|
11
|
+
|
12
|
+
module Frank module Cucumber
|
13
|
+
|
14
|
+
# FrankHelper provides a core set of helper functions for use when interacting with Frank.
|
15
|
+
#
|
16
|
+
# == Most helpful methods
|
17
|
+
# * {#touch}
|
18
|
+
# * {#wait_for_element_to_exist}
|
19
|
+
# * {#wait_for_element_to_exist_and_then_touch_it}
|
20
|
+
# * {#wait_for_nothing_to_be_animating}
|
21
|
+
# * {#app_exec}
|
22
|
+
#
|
23
|
+
# == Configuring the Frank driver
|
24
|
+
# There are some class-level facilities which configure how all Frank interactions work. For example you can specify which selector engine to use
|
25
|
+
# with {FrankHelper.selector_engine}. You can specify the base url which the native app's Frank server is listening on with {FrankHelper.server_base_url}.
|
26
|
+
#
|
27
|
+
# Two common use cases are covered more conveniently with {FrankHelper.use_shelley_from_now_on} and {FrankHelper.test_on_physical_device_via_bonjour}.
|
28
|
+
module FrankHelper
|
29
|
+
include WaitHelper
|
30
|
+
include KeyboardHelper
|
31
|
+
include ScrollHelper
|
32
|
+
include GestureHelper
|
33
|
+
include HostScripting
|
34
|
+
include LocationHelper
|
35
|
+
|
36
|
+
# @!attribute [rw] selector_engine
|
37
|
+
class << self
|
38
|
+
# @return [String] the selector engine we tell Frank to use when interpreting view selectors.
|
39
|
+
attr_accessor :selector_engine
|
40
|
+
# @return [String] the base url which the Frank server is running on. All Frank commands will be sent to that server.
|
41
|
+
attr_accessor :server_base_url
|
42
|
+
|
43
|
+
# After calling this method all subsequent commands will ask Frank to use the Shelley selector engine to interpret view selectors.
|
44
|
+
def use_shelley_from_now_on
|
45
|
+
@selector_engine = 'shelley_compat'
|
46
|
+
end
|
47
|
+
|
48
|
+
# Use Bonjour to search for a running Frank server. The server found will be the recipient for all subsequent Frank commands.
|
49
|
+
# @raise a generic exception if no Frank server could be found via Bonjour
|
50
|
+
def test_on_physical_device_via_bonjour
|
51
|
+
@server_base_url = Bonjour.new.lookup_frank_base_uri
|
52
|
+
raise 'could not detect running Frank server' unless @server_base_url
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Get the correct quote for the selector
|
57
|
+
def get_selector_quote(selector)
|
58
|
+
if selector.index("'") == nil
|
59
|
+
return "'"
|
60
|
+
else
|
61
|
+
return '"'
|
62
|
+
end
|
63
|
+
|
64
|
+
# Specify ip address to run on
|
65
|
+
def test_on_physical_device_with_ip(ip_address)
|
66
|
+
@server_base_url = ip_address
|
67
|
+
raise 'IP Address is incorrect' unless @server_base_url.match(%r{\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b})
|
68
|
+
puts "Running on Frank server #{@server_base_url}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#@api private
|
73
|
+
#@return [:String] convient shorthand for {Frank::Cucumber::FrankHelper.selector_engine}, defaulting to 'uiquery'
|
74
|
+
def selector_engine
|
75
|
+
Frank::Cucumber::FrankHelper.selector_engine || 'uiquery' # default to UIQuery for backwards compatibility
|
76
|
+
end
|
77
|
+
|
78
|
+
#@api private
|
79
|
+
#@return [:String] convient shorthand for {Frank::Cucumber::FrankHelper.server_base_url}
|
80
|
+
def base_server_url
|
81
|
+
Frank::Cucumber::FrankHelper.server_base_url
|
82
|
+
end
|
83
|
+
|
84
|
+
# Ask Frank to touch all views matching the specified selector. There may be views in the view heirarchy which match the selector but
|
85
|
+
# which Frank cannot or will not touch - for example views which are outside the current viewport. You can discover which of the matching
|
86
|
+
# views were actually touched by inspecting the Array which is returned.
|
87
|
+
#
|
88
|
+
# @param [String] selector a view selector.
|
89
|
+
# @return [Array<Boolean>] an array indicating for each view which matched the selector whether it was touched or not.
|
90
|
+
# @raise an expection if no views matched the selector
|
91
|
+
# @raise an expection if no views which matched the selector could be touched
|
92
|
+
def touch( selector )
|
93
|
+
touch_successes = frankly_map( selector, 'touch' )
|
94
|
+
raise "could not find anything matching [#{selector}] to touch" if touch_successes.empty?
|
95
|
+
raise "some views could not be touched (probably because they are not within the current viewport)" if touch_successes.include?(false)
|
96
|
+
touch_successes
|
97
|
+
end
|
98
|
+
|
99
|
+
# Fill in text in a text field.
|
100
|
+
#
|
101
|
+
# @param [String] the placeholder text for the desired text field
|
102
|
+
# @param [Hash{Symbol => String}] a hash with a :with key and a string of text to fill in
|
103
|
+
# @raise an exception if the :with key DSL syntax is missing
|
104
|
+
# @raise an exception if a text field with the given placeholder text could not be found
|
105
|
+
def fill_in( placeholder_field_name, options={} )
|
106
|
+
raise "Must pass a hash containing the key :with" unless (options.is_a?(Hash) && options.has_key?(:with))
|
107
|
+
text_to_type = options[:with]
|
108
|
+
|
109
|
+
quote = get_selector_quote(placeholder_field_name)
|
110
|
+
text_fields_modified = frankly_map( "textField placeholder:#{quote}#{placeholder_field_name}#{quote}", "setText:", text_to_type )
|
111
|
+
raise "could not find text fields with placeholder #{quote}#{placeholder_field_name}#{quote}" if text_fields_modified.empty?
|
112
|
+
#TODO raise warning if text_fields_modified.count > 1
|
113
|
+
end
|
114
|
+
|
115
|
+
# Indicate whether there are any views in the current view heirarchy which match the specified selector.
|
116
|
+
# @param [String] selector a view selector.
|
117
|
+
# @return [Boolean]
|
118
|
+
# @see #check_element_exists
|
119
|
+
def element_exists( selector )
|
120
|
+
matches = frankly_map( selector, 'FEX_accessibilityLabel' )
|
121
|
+
# TODO: raise warning if matches.count > 1
|
122
|
+
!matches.empty?
|
123
|
+
end
|
124
|
+
|
125
|
+
# Assert whether there are any views in the current view heirarchy which match the specified selector.
|
126
|
+
# @param [String] selector a view selector.
|
127
|
+
# @raise an rspec exception if the assertion fails
|
128
|
+
# @see #element_exists, #check_element_does_not_exist
|
129
|
+
def check_element_exists( selector )
|
130
|
+
element_exists( selector ).should be_true
|
131
|
+
end
|
132
|
+
|
133
|
+
def check_element_exists_and_is_visible( selector )
|
134
|
+
element_is_not_hidden( selector ).should be_true
|
135
|
+
end
|
136
|
+
|
137
|
+
# Assert whether there are no views in the current view heirarchy which match the specified selector.
|
138
|
+
# @param [String] selector a view selector.
|
139
|
+
# @raise an rspec exception if the assertion fails
|
140
|
+
# @see #element_exists, #check_element_exists
|
141
|
+
def check_element_does_not_exist( selector )
|
142
|
+
element_exists( selector ).should be_false
|
143
|
+
end
|
144
|
+
|
145
|
+
def check_element_does_not_exist_or_is_not_visible( selector )
|
146
|
+
element_is_not_hidden( selector ).should be_false
|
147
|
+
end
|
148
|
+
|
149
|
+
# Indicate whether there are any views in the current view heirarchy which contain the specified accessibility label.
|
150
|
+
# @param [String] expected_mark the expected accessibility label
|
151
|
+
# @return [Boolean]
|
152
|
+
# @see #check_view_with_mark_exists
|
153
|
+
def view_with_mark_exists(expected_mark)
|
154
|
+
quote = get_selector_quote(expected_mark)
|
155
|
+
element_exists( "view marked:#{quote}#{expected_mark}#{quote}" )
|
156
|
+
end
|
157
|
+
|
158
|
+
# Assert whether there are any views in the current view heirarchy which contain the specified accessibility label.
|
159
|
+
# @param [String] expected_mark the expected accessibility label
|
160
|
+
# @raise an rspec exception if the assertion fails
|
161
|
+
# @see #view_with_mark_exists
|
162
|
+
def check_view_with_mark_exists(expected_mark)
|
163
|
+
quote = get_selector_quote(expected_mark)
|
164
|
+
check_element_exists( "view marked:#{quote}#{expected_mark}#{quote}" )
|
165
|
+
end
|
166
|
+
|
167
|
+
# Assert whether there are no views in the current view heirarchy which contain the specified accessibility label.
|
168
|
+
# @param [String] expected_mark the expected accessibility label
|
169
|
+
# @raise an rspec exception if the assertion fails
|
170
|
+
# @see #view_with_mark_exists, #check_view_with_mark_exists
|
171
|
+
def check_view_with_mark_does_not_exist(expected_mark)
|
172
|
+
quote = get_selector_quote(expected_mark)
|
173
|
+
check_element_does_not_exist( "view marked:#{quote}#{expected_mark}#{quote}" )
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
# Waits for any of the specified selectors to match a view.
|
178
|
+
#
|
179
|
+
# Checks each selector in turn within a {http://sauceio.com/index.php/2011/04/how-to-lose-races-and-win-at-selenium/ spin assert} loop and yields the first one which is found to exist in the view heirarchy.
|
180
|
+
# Raises an exception if no views could be found to match any of the provided selectors within {WaitHelper::TIMEOUT} seconds.
|
181
|
+
#
|
182
|
+
# @see WaitHelper#wait_until
|
183
|
+
def wait_for_element_to_exist(*selectors,&block)
|
184
|
+
wait_until(:message => "Waited for element matching any of #{selectors.join(', ')} to exist") do
|
185
|
+
at_least_one_exists = false
|
186
|
+
selectors.each do |selector|
|
187
|
+
if element_exists( selector )
|
188
|
+
at_least_one_exists = true
|
189
|
+
block.call(selector) if block
|
190
|
+
end
|
191
|
+
end
|
192
|
+
at_least_one_exists
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Waits for the specified selector to not match any views.
|
197
|
+
#
|
198
|
+
# Uses {WaitHelper#wait_until} to check for any matching views within a {http://sauceio.com/index.php/2011/04/how-to-lose-races-and-win-at-selenium/ spin assert} loop.
|
199
|
+
# Returns as soon as no views match the specified selector.
|
200
|
+
# Raises an exception if there continued to be at least one view which matched the selector by the time {WaitHelper::TIMEOUT} seconds passed.
|
201
|
+
#
|
202
|
+
# @see check_element_does_not_exist
|
203
|
+
# @see wait_for_element_to_not_exist
|
204
|
+
def wait_for_element_to_not_exist(selector)
|
205
|
+
wait_until(:message => "Waited for element #{selector} to not exist") do
|
206
|
+
!element_exists(selector)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Waits for a view to exist and then send a touch command to that view.
|
211
|
+
#
|
212
|
+
# @param selectors takes one or more selectors to use to search for a view. The first selector which is found to matches a view is the selector
|
213
|
+
# which is then used to send a touch command.
|
214
|
+
#
|
215
|
+
# Raises an exception if no views could be found to match any of the provided selectors within {WaitHelper::TIMEOUT} seconds.
|
216
|
+
def wait_for_element_to_exist_and_then_touch_it(*selectors)
|
217
|
+
wait_for_element_to_exist(*selectors) do |sel|
|
218
|
+
touch(sel)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# Waits for there to be no views which report an isAnimated property of true.
|
223
|
+
#
|
224
|
+
# @param timeout [Number] number of seconds to wait for nothing to be animating before timeout out. Defaults to {WaitHelper::TIMEOUT}
|
225
|
+
#
|
226
|
+
# Raises an exception if there were still views animating after {timeout} seconds.
|
227
|
+
def wait_for_nothing_to_be_animating( timeout = false )
|
228
|
+
wait_until :timeout => timeout do
|
229
|
+
!element_exists('view isAnimating')
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
# Checks that the specified selector matches at least one view, and that at least one of the matched
|
235
|
+
# views has an isHidden property set to false
|
236
|
+
#
|
237
|
+
# a better name for this method would be element_exists_and_is_not_hidden
|
238
|
+
def element_is_not_hidden(selector)
|
239
|
+
matches = frankly_map( selector, 'FEX_isVisible' )
|
240
|
+
matches.delete(false)
|
241
|
+
!matches.empty?
|
242
|
+
end
|
243
|
+
|
244
|
+
def accessibility_frame(selector)
|
245
|
+
frames = frankly_map( selector, 'FEX_accessibilityFrame' )
|
246
|
+
raise "the supplied selector [#{selector}] did not match any views" if frames.empty?
|
247
|
+
raise "the supplied selector [#{selector}] matched more than one views (#{frames.count} views matched)" if frames.count > 1
|
248
|
+
Rect.from_api_repr( frames.first )
|
249
|
+
end
|
250
|
+
|
251
|
+
def drag_with_initial_delay(args)
|
252
|
+
from, to = args.values_at(:from,:to)
|
253
|
+
raise ArgumentError.new('must specify a :from parameter') if from.nil?
|
254
|
+
raise ArgumentError.new('must specify a :to parameter') if to.nil?
|
255
|
+
|
256
|
+
dest_frame = accessibility_frame(to)
|
257
|
+
|
258
|
+
if is_mac
|
259
|
+
from_frame = accessibility_frame(from)
|
260
|
+
|
261
|
+
frankly_map( from, 'FEX_mouseDownX:y:', from_frame.center.x, from_frame.center.y )
|
262
|
+
|
263
|
+
sleep 0.3
|
264
|
+
|
265
|
+
frankly_map( from, 'FEX_dragToX:y:', dest_frame.center.x, dest_frame.center.y )
|
266
|
+
|
267
|
+
sleep 0.3
|
268
|
+
|
269
|
+
frankly_map( from, 'FEX_mouseUpX:y:', dest_frame.center.x, dest_frame.center.y )
|
270
|
+
|
271
|
+
else
|
272
|
+
|
273
|
+
frankly_map( from, 'FEX_dragWithInitialDelayToX:y:', dest_frame.center.x, dest_frame.center.y )
|
274
|
+
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
# Ask Frank to invoke the specified method on the app delegate of the iOS application under automation.
|
281
|
+
# @param method_sig [String] the method signature
|
282
|
+
# @param method_args the method arguments
|
283
|
+
#
|
284
|
+
# @example
|
285
|
+
# # the same as calling
|
286
|
+
# # [[[UIApplication sharedApplication] appDelegate] setServiceBaseUrl:@"http://example.com/my_api" withPort:8080]
|
287
|
+
# # from your native app
|
288
|
+
# app_exec( "setServiceBaseUrl:withPort:", "http://example.com/my_api", 8080 )
|
289
|
+
#
|
290
|
+
#
|
291
|
+
def app_exec(method_sig, *method_args)
|
292
|
+
operation_map = Gateway.build_operation_map(method_sig.to_s, method_args)
|
293
|
+
|
294
|
+
res = frank_server.send_post(
|
295
|
+
'app_exec',
|
296
|
+
:operation => operation_map
|
297
|
+
)
|
298
|
+
|
299
|
+
return Gateway.evaluate_frankly_response( res, "app_exec #{method_sig}" )
|
300
|
+
end
|
301
|
+
|
302
|
+
# Ask Frank to execute an arbitrary Objective-C method on each view which matches the specified selector.
|
303
|
+
#
|
304
|
+
# @return [Array] an array with an element for each view matched by the selector, each element in the array gives the return value from invoking the specified method on that view.
|
305
|
+
def frankly_map( selector, method_name, *method_args )
|
306
|
+
operation_map = Gateway.build_operation_map(method_name.to_s, method_args)
|
307
|
+
res = frank_server.send_post(
|
308
|
+
'map',
|
309
|
+
:query => selector,
|
310
|
+
:operation => operation_map,
|
311
|
+
:selector_engine => selector_engine
|
312
|
+
)
|
313
|
+
|
314
|
+
return Gateway.evaluate_frankly_response( res, "frankly_map #{selector} #{method_name}" )
|
315
|
+
end
|
316
|
+
|
317
|
+
# print a JSON-formatted dump of the current view heirarchy to stdout
|
318
|
+
def frankly_dump
|
319
|
+
res = frank_server.send_get( 'dump' )
|
320
|
+
puts JSON.pretty_generate(JSON.parse(res)) rescue puts res #dumping a super-deep DOM causes errors
|
321
|
+
end
|
322
|
+
|
323
|
+
# grab a screenshot of the application under automation and save it to the specified file.
|
324
|
+
#
|
325
|
+
# @param filename [String] where to save the screenshot image file
|
326
|
+
# @param subframe describes which section of the screen to grab. If unspecified then the entire screen will be captured. #TODO document what format this parameter takes.
|
327
|
+
# @param allwindows [Boolean] If true then all UIWindows in the current UIScreen will be included in the screenshot. If false then only the main window will be captured.
|
328
|
+
def frankly_screenshot(filename, subframe=nil, allwindows=true)
|
329
|
+
path = 'screenshot'
|
330
|
+
path += '/allwindows' if allwindows
|
331
|
+
path += "/frame/" + URI.escape(subframe) if (subframe != nil)
|
332
|
+
|
333
|
+
data = frank_server.send_get( path )
|
334
|
+
|
335
|
+
open(filename, "wb") do |file|
|
336
|
+
file.write(data)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
# @return [Boolean] true if the device running the application currently in a portrait orientation
|
341
|
+
# @note wil return false if the device is in a flat or unknown orientation. Sometimes the iOS simulator will report this state when first launched.
|
342
|
+
def frankly_oriented_portrait?
|
343
|
+
'portrait' == frankly_current_orientation
|
344
|
+
end
|
345
|
+
|
346
|
+
# @return [Boolean] true if the device running the application currently in a landscape orientation
|
347
|
+
# @note wil return false if the device is in a flat or unknown orientation. Sometimes the iOS simulator will report this state when first launched.
|
348
|
+
def frankly_oriented_landscape?
|
349
|
+
'landscape' == frankly_current_orientation
|
350
|
+
end
|
351
|
+
|
352
|
+
# @return [String] the orientation of the device running the application under automation.
|
353
|
+
# @note this is a low-level API. In most cases you should use {frankly_oriented_portrait} or {frankly_oriented_landscape} instead.
|
354
|
+
def frankly_current_orientation
|
355
|
+
res = frank_server.send_get( 'orientation' )
|
356
|
+
orientation = JSON.parse( res )['orientation']
|
357
|
+
puts "orientation reported as '#{orientation}'" if $DEBUG
|
358
|
+
orientation
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
# set the device orientation
|
363
|
+
# @param orientation can be 'landscape','landscape_left','landscape_right','portrait', or 'portrait_upside_down'
|
364
|
+
def frankly_set_orientation(orientation)
|
365
|
+
orientation = orientation.to_s
|
366
|
+
orientation = 'landscape_left' if orientation == 'landscape'
|
367
|
+
res = frank_server.send_post( 'orientation', orientation )
|
368
|
+
return Gateway.evaluate_frankly_response( res, "set_orientation #{orientation}" )
|
369
|
+
end
|
370
|
+
|
371
|
+
# @return [Boolean] Does the device running the application have accessibility enabled.
|
372
|
+
# If accessibility is not enabled then a lot of Frank functionality will not work.
|
373
|
+
def frankly_is_accessibility_enabled
|
374
|
+
res = frank_server.send_get( 'accessibility_check' )
|
375
|
+
JSON.parse( res )['accessibility_enabled'] == 'true'
|
376
|
+
end
|
377
|
+
|
378
|
+
# wait for the application under automation to be ready to receive automation commands.
|
379
|
+
#
|
380
|
+
# Has some basic heuristics to cope with cases where the Frank server is intermittently available when first launching.
|
381
|
+
#
|
382
|
+
# @raise [Timeout::TimeoutError] if nothing is ready within 20 seconds
|
383
|
+
# @raise generic error if the device hosting the application does not appear to have accessibility enabled.
|
384
|
+
def wait_for_frank_to_come_up
|
385
|
+
num_consec_successes = 0
|
386
|
+
num_consec_failures = 0
|
387
|
+
Timeout.timeout(20) do
|
388
|
+
while num_consec_successes <= 6
|
389
|
+
if frankly_ping
|
390
|
+
num_consec_failures = 0
|
391
|
+
num_consec_successes += 1
|
392
|
+
else
|
393
|
+
num_consec_successes = 0
|
394
|
+
num_consec_failures += 1
|
395
|
+
if num_consec_failures >= 5 # don't show small timing errors
|
396
|
+
print (num_consec_failures == 5 ) ? "\n" : "\r"
|
397
|
+
print "PING FAILED" + "!"*num_consec_failures
|
398
|
+
end
|
399
|
+
end
|
400
|
+
STDOUT.flush
|
401
|
+
sleep 0.2
|
402
|
+
end
|
403
|
+
|
404
|
+
if num_consec_successes < 6
|
405
|
+
print (num_consec_successes == 1 ) ? "\n" : "\r"
|
406
|
+
print "FRANK!".slice(0,num_consec_successes)
|
407
|
+
STDOUT.flush
|
408
|
+
puts ''
|
409
|
+
end
|
410
|
+
|
411
|
+
if num_consec_failures >= 5
|
412
|
+
puts ''
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
unless frankly_is_accessibility_enabled
|
417
|
+
raise "ACCESSIBILITY DOES NOT APPEAR TO BE ENABLED ON YOUR SIMULATOR. Hit the home button, go to settings, select Accessibility, and turn the inspector on."
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
# @return [String] the name of the device currently running the application
|
422
|
+
# @note this is a low-level API. In most cases you should use {is_iphone}, {is_ipad} or {is_mac} instead.
|
423
|
+
def frankly_device_name
|
424
|
+
res = frank_server.send_get( 'device' )
|
425
|
+
device = JSON.parse( res )['device']
|
426
|
+
puts "device reported as '#{device}'" if $DEBUG
|
427
|
+
device
|
428
|
+
end
|
429
|
+
|
430
|
+
# @return [Boolean] is the device running the application an iPhone.
|
431
|
+
def is_iphone
|
432
|
+
return frankly_device_name == "iphone"
|
433
|
+
end
|
434
|
+
|
435
|
+
# @return [Boolean] is the device running the application an iPhone.
|
436
|
+
def is_ipad
|
437
|
+
return frankly_device_name == "ipad"
|
438
|
+
end
|
439
|
+
|
440
|
+
# @return [Boolean] is the device running the application a Mac.
|
441
|
+
def is_mac
|
442
|
+
return frankly_device_name == "mac"
|
443
|
+
end
|
444
|
+
|
445
|
+
# Check whether Frank is able to communicate with the application under automation
|
446
|
+
def frankly_ping
|
447
|
+
frank_server.ping
|
448
|
+
end
|
449
|
+
|
450
|
+
#@api private
|
451
|
+
#@return [Frank::Cucumber::Gateway] a gateway for sending Frank commands to the application under automation
|
452
|
+
def frank_server
|
453
|
+
@_frank_server ||= Frank::Cucumber::Gateway.new( base_server_url )
|
454
|
+
end
|
455
|
+
|
456
|
+
end
|
457
|
+
|
458
|
+
|
459
|
+
end end
|