frank-cucumber 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/bin/frank-skeleton +19 -0
- data/example/features/example.feature +5 -0
- data/example/features/step_definitions/example_steps.rb +19 -0
- data/example/features/support/env.rb +1 -0
- data/features-skeleton/my_first.feature +12 -0
- data/features-skeleton/support/env.rb +1 -0
- data/frank-cucumber.gemspec +23 -0
- data/lib/frank-cucumber.rb +9 -0
- data/lib/frank-cucumber/color_helper.rb +13 -0
- data/lib/frank-cucumber/core_frank_steps.rb +300 -0
- data/lib/frank-cucumber/frank_helper.rb +234 -0
- data/lib/frank-cucumber/version.rb +5 -0
- metadata +114 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/bin/frank-skeleton
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
features_dir = File.join( FileUtils.pwd, "features" )
|
6
|
+
|
7
|
+
if File.exists?( features_dir )
|
8
|
+
puts "A features directory already exists. I won't overwrite it. Giving up."
|
9
|
+
exit 1
|
10
|
+
else
|
11
|
+
puts "I'm about to create a subdirectory called features which will contain all your Cucumber tests. Please hit return to confirm that's what you want."
|
12
|
+
exit 2 unless STDIN.gets.chomp == ''
|
13
|
+
end
|
14
|
+
|
15
|
+
source_dir = File.join( File.dirname(__FILE__), '..', 'features-skeleton' )
|
16
|
+
|
17
|
+
FileUtils.cp_r( source_dir, features_dir )
|
18
|
+
|
19
|
+
puts "features subdirectory created. Try executing 'cucumber' from the current directory to see how you new cucumber setup runs."
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Given /^I wait (\d+) seconds$/ do |seconds|
|
2
|
+
seconds = seconds.to_i
|
3
|
+
sleep seconds
|
4
|
+
end
|
5
|
+
|
6
|
+
Then /^the test fails$/ do
|
7
|
+
(2+2).should == 5
|
8
|
+
end
|
9
|
+
|
10
|
+
Then /^the test passes!$/ do
|
11
|
+
(2+2).should == 4
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
Then /^I see colors!$/ do
|
16
|
+
announce red "RED"
|
17
|
+
announce blue "BLUE"
|
18
|
+
announce green "GREEEN!"
|
19
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'frank-cucumber'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Feature:
|
2
|
+
As an iOS developer
|
3
|
+
I want to have a sample feature file
|
4
|
+
So I can see what my next step is in the wonderful world of Frank/Cucumber testing
|
5
|
+
|
6
|
+
Scenario:
|
7
|
+
Rotating the simulator for demonstration purposes
|
8
|
+
Given I launch the app
|
9
|
+
Given the device is in landscape orientation
|
10
|
+
Given the device is in portrait orientation
|
11
|
+
Given the device is in landscape orientation
|
12
|
+
Given the device is in portrait orientation
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'frank-cucumber'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "frank-cucumber/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "frank-cucumber"
|
7
|
+
s.version = Frank::Cucumber::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Pete Hodgson","Derek Longmuir"]
|
10
|
+
s.email = ["gems@thepete.net"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/frank-cucumber"
|
12
|
+
s.summary = %q{Use cucumber to test native iOS apps via Frank}
|
13
|
+
s.description = %q{Use cucumber to test native iOS apps via Frank}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_dependency( "cucumber" )
|
21
|
+
s.add_dependency( "rspec", [">=2.0"] )
|
22
|
+
s.add_dependency( "json" ) # TODO: figure out how to be more permissive as to which JSON gems we allow
|
23
|
+
end
|
@@ -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,300 @@
|
|
1
|
+
WAIT_TIMEOUT = 240
|
2
|
+
|
3
|
+
require 'rspec/expectations'
|
4
|
+
|
5
|
+
# -- See -- #
|
6
|
+
Then /^I wait to see "([^\"]*)"$/ do |expected_mark|
|
7
|
+
Timeout::timeout(WAIT_TIMEOUT) do
|
8
|
+
until view_with_mark_exists( expected_mark )
|
9
|
+
sleep 0.1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Then /^I wait to not see "([^\"]*)"$/ do |expected_mark|
|
15
|
+
sleep 3
|
16
|
+
Timeout::timeout(WAIT_TIMEOUT) do
|
17
|
+
while element_exists( "view marked:'#{expected_mark}'" )
|
18
|
+
sleep 0.1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Then /^I wait to see a navigation bar titled "([^\"]*)"$/ do |expected_mark|
|
24
|
+
Timeout::timeout(30) do
|
25
|
+
values = frankly_map( 'navigationItemView', 'accessibilityLabel' )
|
26
|
+
until values.include(expected_mark)
|
27
|
+
values = frankly_map( 'navigationItemView', 'accessibilityLabel' )
|
28
|
+
sleep 0.1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Then /^I wait to not see a navigation bar titled "([^\"]*)"$/ do |expected_mark|
|
34
|
+
Timeout::timeout(30) do
|
35
|
+
values = frankly_map( 'navigationItemView', 'accessibilityLabel' )
|
36
|
+
while values.include(expected_mark)
|
37
|
+
values = frankly_map( 'navigationItemView', 'accessibilityLabel' )
|
38
|
+
sleep 0.1
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Then /^I should see a "([^\"]*)" button$/ do |expected_mark|
|
44
|
+
check_element_exists("button marked:'#{expected_mark}'")
|
45
|
+
end
|
46
|
+
|
47
|
+
Then /^I should see "([^\"]*)"$/ do |expected_mark|
|
48
|
+
check_element_exists("view marked:'#{expected_mark}'")
|
49
|
+
end
|
50
|
+
|
51
|
+
Then /^I should not see "([^\"]*)"$/ do |expected_mark|
|
52
|
+
check_element_does_not_exist("view marked:'#{expected_mark}'")
|
53
|
+
end
|
54
|
+
|
55
|
+
Then /I should see the following:/ do |table|
|
56
|
+
values = frankly_map( 'view', 'accessibilityLabel' )
|
57
|
+
table.raw.each do |expected_mark|
|
58
|
+
values.should include( expected_mark.first )
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Then /I should not see the following:/ do |table|
|
63
|
+
values = frankly_map( 'view', 'accessibilityLabel' )
|
64
|
+
table.raw.each do |expected_mark|
|
65
|
+
values.should_not include( expected_mark.first )
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
Then /^I should see a navigation bar titled "([^\"]*)"$/ do |expected_mark|
|
70
|
+
values = frankly_map( 'navigationItemView', 'accessibilityLabel' )
|
71
|
+
values.should include(expected_mark)
|
72
|
+
end
|
73
|
+
|
74
|
+
Then /^I should see an alert view titled "([^\"]*)"$/ do |expected_mark|
|
75
|
+
values = frankly_map( 'alertView', 'message')
|
76
|
+
puts values
|
77
|
+
values.should include(expected_mark)
|
78
|
+
end
|
79
|
+
|
80
|
+
Then /^I should not see an alert view$/ do
|
81
|
+
check_element_does_not_exist( 'alertView' )
|
82
|
+
end
|
83
|
+
|
84
|
+
Then /^I should see an element of class "([^\"]*)" with name "([^\"]*)" with the following labels: "([^\"]*)"$/ do |className, classLabel, listOfLabels|
|
85
|
+
arrayOfLabels = listOfLabels.split(',');
|
86
|
+
arrayOfLabels.each do |label|
|
87
|
+
check_element_exists("view marked:'#{classLabel}' parent view:'#{className}' descendant view marked:'#{label}'")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
Then /^I should see an element of class "([^\"]*)" with name "([^\"]*)" with a "([^\"]*)" button$/ do |className, classLabel, buttonName|
|
92
|
+
check_element_exists("view marked:'#{classLabel}' parent view:'#{className}' descendant button marked:'#{buttonName}'")
|
93
|
+
end
|
94
|
+
|
95
|
+
Then /^I should not see a hidden button marked "([^\"]*)"$/ do |expected_mark|
|
96
|
+
element_is_not_hidden("button marked:'#{expected_mark}'").should be_false
|
97
|
+
end
|
98
|
+
|
99
|
+
Then /^I should see a nonhidden button marked "([^\"]*)"$/ do |expected_mark|
|
100
|
+
element_is_not_hidden("button marked:'#{expected_mark}'").should be_true
|
101
|
+
end
|
102
|
+
|
103
|
+
Then /^I should see an element of class "([^\"]*)"$/ do |className|
|
104
|
+
element_is_not_hidden("view:'#{className}'")
|
105
|
+
end
|
106
|
+
|
107
|
+
Then /^I should not see an element of class "([^\"]*)"$/ do |className|
|
108
|
+
selector = "view:'#{className}'"
|
109
|
+
element_exists_and_is_not_hidden = element_exists( selector ) && element_is_not_hidden(selector)
|
110
|
+
element_exists_and_is_not_hidden.should be_false
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
# -- Type/Fill in -- #
|
115
|
+
|
116
|
+
When /^I type "([^\"]*)" into the "([^\"]*)" text field$/ do |text_to_type, field_name|
|
117
|
+
text_fields_modified = frankly_map( "textField placeholder:'#{field_name}'", "setText:", text_to_type )
|
118
|
+
raise "could not find text fields with placeholder '#{field_name}'" if text_fields_modified.empty?
|
119
|
+
#TODO raise warning if text_fields_modified.count > 1
|
120
|
+
end
|
121
|
+
|
122
|
+
# alias
|
123
|
+
When /^I fill in "([^\"]*)" with "([^\"]*)"$/ do |text_field, text_to_type|
|
124
|
+
When %Q|I type "#{text_to_type}" into the "#{text_field}" text field|
|
125
|
+
end
|
126
|
+
|
127
|
+
When /^I fill in text fields as follows:$/ do |table|
|
128
|
+
table.hashes.each do |row|
|
129
|
+
When %Q|I type "#{row['text']}" into the "#{row['field']}" text field|
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# -- Rotate -- #
|
134
|
+
Given /^the device is in (a )?landscape orientation$/ do |ignored|
|
135
|
+
# for some reason the simulator sometimes starts of reporting its orientation as 'flat'. Workaround for this is to rotate the device then wait a bit
|
136
|
+
if 'flat' == frankly_current_orientation
|
137
|
+
rotate_simulator_right
|
138
|
+
sleep 1
|
139
|
+
end
|
140
|
+
|
141
|
+
unless frankly_oriented_landscape?
|
142
|
+
rotate_simulator_left
|
143
|
+
sleep 1
|
144
|
+
raise "expected orientation to be landscape after rotating left, but it is #{frankly_current_orientation}" unless frankly_oriented_landscape?
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
Given /^the device is in (a )?portrait orientation$/ do |ignored|
|
149
|
+
# for some reason the simulator sometimes starts of reporting its orientation as 'flat'. Workaround for this is to rotate the device then wait a bit
|
150
|
+
if 'flat' == frankly_current_orientation
|
151
|
+
rotate_simulator_right
|
152
|
+
sleep 1
|
153
|
+
end
|
154
|
+
|
155
|
+
unless frankly_oriented_portrait?
|
156
|
+
rotate_simulator_left
|
157
|
+
sleep 1
|
158
|
+
raise "Expected orientation to be portrait after rotating left, but it is #{frankly_current_orientation}" unless frankly_oriented_portrait?
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
When /^I simulate a memory warning$/ do
|
163
|
+
simulate_memory_warning
|
164
|
+
end
|
165
|
+
|
166
|
+
Then /^I rotate to the "([^\"]*)"$/ do |direction|
|
167
|
+
if direction == "right"
|
168
|
+
rotate_simulator_right
|
169
|
+
elsif direction == "left"
|
170
|
+
rotate_simulator_left
|
171
|
+
else
|
172
|
+
raise %Q|Rotation direction specified ("#{direction}") is invalid. Please specify right or left.|
|
173
|
+
end
|
174
|
+
sleep 1
|
175
|
+
end
|
176
|
+
|
177
|
+
# -- touch -- #
|
178
|
+
When /^I touch "([^\"]*)"$/ do |mark|
|
179
|
+
selector = "view marked:'#{mark}' first"
|
180
|
+
if element_exists(selector)
|
181
|
+
touch( selector )
|
182
|
+
else
|
183
|
+
raise "Could not touch [#{mark}], it does not exist."
|
184
|
+
end
|
185
|
+
sleep 1
|
186
|
+
end
|
187
|
+
|
188
|
+
When /^I touch "([^\"]*)" if exists$/ do |mark|
|
189
|
+
sleep 1
|
190
|
+
selector = "view marked:'#{mark}' first"
|
191
|
+
if element_exists(selector)
|
192
|
+
touch(selector)
|
193
|
+
sleep 1
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
When /^I touch the first table cell$/ do
|
198
|
+
touch("tableViewCell first")
|
199
|
+
end
|
200
|
+
|
201
|
+
When /^I touch the table cell marked "([^\"]*)"$/ do |mark|
|
202
|
+
touch("tableViewCell marked:'#{mark}'")
|
203
|
+
end
|
204
|
+
|
205
|
+
When /^I touch the (\d*)(?:st|nd|rd|th)? table cell$/ do |ordinal|
|
206
|
+
ordinal = ordinal.to_i - 1
|
207
|
+
touch("tableViewCell index:#{ordinal}")
|
208
|
+
end
|
209
|
+
|
210
|
+
Then /I touch the following:/ do |table|
|
211
|
+
values = frankly_map( 'view', 'accessibilityLabel' )
|
212
|
+
table.raw.each do |expected_mark|
|
213
|
+
touch( "view marked:'#{expected_mark}'" )
|
214
|
+
sleep 2
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
When /^I touch the button marked "([^\"]*)"$/ do |mark|
|
219
|
+
touch( "button marked:'#{mark}'" )
|
220
|
+
end
|
221
|
+
|
222
|
+
When /^I touch the "([^\"]*)" action sheet button$/ do |mark|
|
223
|
+
touch( "actionSheet threePartButton marked:'#{mark}'" )
|
224
|
+
end
|
225
|
+
|
226
|
+
When /^I touch the (\d*)(?:st|nd|rd|th)? action sheet button$/ do |ordinal|
|
227
|
+
ordinal = ordinal.to_i
|
228
|
+
touch( "actionSheet threePartButton tag:#{ordinal}" )
|
229
|
+
end
|
230
|
+
|
231
|
+
When /^I touch the (\d*)(?:st|nd|rd|th)? alert view button$/ do |ordinal|
|
232
|
+
ordinal = ordinal.to_i
|
233
|
+
touch( "alertView threePartButton tag:#{ordinal}" )
|
234
|
+
end
|
235
|
+
|
236
|
+
# -- switch -- #
|
237
|
+
|
238
|
+
When /^I flip switch "([^\"]*)" on$/ do |mark|
|
239
|
+
selector = "view:'UISwitch' marked:'#{mark}'"
|
240
|
+
views_switched = frankly_map( selector, 'setOn:animated:', true, true )
|
241
|
+
raise "could not find anything matching [#{uiquery}] to switch" if views_switched.empty?
|
242
|
+
end
|
243
|
+
|
244
|
+
When /^I flip switch "([^\"]*)" off$/ do |mark|
|
245
|
+
selector = "view:'UISwitch' marked:'#{mark}'"
|
246
|
+
views_switched = frankly_map( selector, 'setOn:animated:', false, true )
|
247
|
+
raise "could not find anything matching [#{uiquery}] to switch" if views_switched.empty?
|
248
|
+
end
|
249
|
+
|
250
|
+
When /^I flip switch "([^\"]*)"$/ do |mark|
|
251
|
+
touch("view:'UISwitch' marked:'#{mark}'")
|
252
|
+
end
|
253
|
+
|
254
|
+
Then /^switch "([^\"]*)" should be on$/ do |mark|
|
255
|
+
# switch_states = frankly_map( "view:'Switch' marked:'#{mark}'", "isOn" )
|
256
|
+
switch_states = frankly_map( "view accesibilityLabel:'#{mark}'", "isOn" )
|
257
|
+
puts "test #{switch_states.inspect}"
|
258
|
+
|
259
|
+
if switch_states == 0
|
260
|
+
puts "Switch #{mark} is ON"
|
261
|
+
else
|
262
|
+
puts "Switch #{mark} is OFF, flim switch ON"
|
263
|
+
Then %Q|I flip switch "#{mark}"|
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
Then /^switch "([^\"]*)" should be off$/ do |mark|
|
268
|
+
switch_states = frankly_map( "view:'UISwitch' marked:'#{mark}'", "isOn" )
|
269
|
+
puts "test #{switch_states.inspect}"
|
270
|
+
|
271
|
+
if switch_states == 0
|
272
|
+
puts "Switch #{mark} is ON, flip switch OFF"
|
273
|
+
Then %Q|I flip switch "#{mark}"|
|
274
|
+
else
|
275
|
+
puts "Switch #{mark} is OFF"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
# -- misc -- #
|
281
|
+
|
282
|
+
When /^I wait for ([\d\.]+) second(?:s)?$/ do |num_seconds|
|
283
|
+
num_seconds = num_seconds.to_f
|
284
|
+
sleep num_seconds
|
285
|
+
end
|
286
|
+
|
287
|
+
Then /^a pop\-over menu is displayed with the following:$/ do |table|
|
288
|
+
sleep 1
|
289
|
+
table.raw.each do |expected_mark|
|
290
|
+
check_element_exists "actionSheet view marked:'#{expected_mark}'"
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
Then /^I navigate back$/ do
|
295
|
+
touch( "navigationItemButtonView" )
|
296
|
+
end
|
297
|
+
|
298
|
+
When /^I dump the DOM$/ do
|
299
|
+
dom = frankly_dump
|
300
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Frank module Cucumber
|
5
|
+
|
6
|
+
module FrankHelper
|
7
|
+
|
8
|
+
def touch( uiquery )
|
9
|
+
views_touched = frankly_map( uiquery, 'touch' )
|
10
|
+
raise "could not find anything matching [#{uiquery}] to touch" if views_touched.empty?
|
11
|
+
#TODO raise warning if views_touched.count > 1
|
12
|
+
end
|
13
|
+
|
14
|
+
def element_exists( query )
|
15
|
+
matches = frankly_map( query, 'accessibilityLabel' )
|
16
|
+
# TODO: raise warning if matches.count > 1
|
17
|
+
!matches.empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
def check_element_exists( query )
|
21
|
+
#puts "checking #{query} exists..."
|
22
|
+
element_exists( query ).should be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
def check_element_does_not_exist( query )
|
26
|
+
#puts "checking #{query} does not exist..."
|
27
|
+
element_exists( query ).should be_false
|
28
|
+
end
|
29
|
+
|
30
|
+
def view_with_mark_exists(expected_mark)
|
31
|
+
element_exists( "view marked:'#{expected_mark}'" )
|
32
|
+
end
|
33
|
+
|
34
|
+
def check_view_with_mark_exists(expected_mark)
|
35
|
+
check_element_exists( "view marked:'#{expected_mark}'" )
|
36
|
+
end
|
37
|
+
|
38
|
+
# a better name would be element_exists_and_is_not_hidden
|
39
|
+
def element_is_not_hidden(query)
|
40
|
+
matches = frankly_map( query, 'isHidden' )
|
41
|
+
matches.delete(true)
|
42
|
+
!matches.empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
def app_exec(method_name, *method_args)
|
46
|
+
operation_map = {
|
47
|
+
:method_name => method_name,
|
48
|
+
:arguments => method_args
|
49
|
+
}
|
50
|
+
|
51
|
+
before = Time.now
|
52
|
+
res = post_to_uispec_server( 'app_exec', :operation => operation_map )
|
53
|
+
logger.debug( "MAP applying #{method_name} with args:( #{method_args.inspect} ) to 'Application Delegate' took #{Time.now - before} seconds" )
|
54
|
+
|
55
|
+
res = JSON.parse( res )
|
56
|
+
if res['outcome'] != 'SUCCESS'
|
57
|
+
raise "app_exec #{method_name} failed because: #{res['reason']}\n#{res['details']}"
|
58
|
+
end
|
59
|
+
|
60
|
+
res['results']
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def frankly_map( query, method_name, *method_args )
|
65
|
+
operation_map = {
|
66
|
+
:method_name => method_name,
|
67
|
+
:arguments => method_args
|
68
|
+
}
|
69
|
+
res = post_to_uispec_server( 'map', :query => query, :operation => operation_map )
|
70
|
+
res = JSON.parse( res )
|
71
|
+
if res['outcome'] != 'SUCCESS'
|
72
|
+
raise "frankly_map #{query} #{method_name} failed because: #{res['reason']}\n#{res['details']}"
|
73
|
+
end
|
74
|
+
|
75
|
+
res['results']
|
76
|
+
end
|
77
|
+
|
78
|
+
def frankly_dump
|
79
|
+
res = get_to_uispec_server( 'dump' )
|
80
|
+
puts JSON.pretty_generate(JSON.parse(res)) rescue puts res #dumping a super-deep DOM causes errors
|
81
|
+
end
|
82
|
+
|
83
|
+
def frankly_oriented_portrait?
|
84
|
+
'portrait' == frankly_current_orientation
|
85
|
+
end
|
86
|
+
|
87
|
+
def frankly_oriented_landscape?
|
88
|
+
'landscape' == frankly_current_orientation
|
89
|
+
end
|
90
|
+
|
91
|
+
def frankly_current_orientation
|
92
|
+
res = get_to_uispec_server( 'orientation' )
|
93
|
+
JSON.parse( res )['orientation']
|
94
|
+
end
|
95
|
+
|
96
|
+
def wait_for_frank_to_come_up
|
97
|
+
num_consec_successes = 0
|
98
|
+
num_consec_failures = 0
|
99
|
+
Timeout.timeout(60) do
|
100
|
+
while num_consec_successes <= 6
|
101
|
+
if frankly_ping
|
102
|
+
num_consec_failures = 0
|
103
|
+
num_consec_successes += 1
|
104
|
+
print (num_consec_successes == 1 ) ? "\n" : "\r"
|
105
|
+
print "FRANK!".slice(0,num_consec_successes)
|
106
|
+
else
|
107
|
+
num_consec_successes = 0
|
108
|
+
num_consec_failures += 1
|
109
|
+
print (num_consec_failures == 1 ) ? "\n" : "\r"
|
110
|
+
print "PING FAILED" + "!"*num_consec_failures
|
111
|
+
end
|
112
|
+
STDOUT.flush
|
113
|
+
sleep 0.2
|
114
|
+
end
|
115
|
+
puts ''
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def frankly_ping
|
120
|
+
get_to_uispec_server('')
|
121
|
+
return true
|
122
|
+
rescue Errno::ECONNREFUSED
|
123
|
+
return false
|
124
|
+
rescue EOFError
|
125
|
+
return false
|
126
|
+
end
|
127
|
+
|
128
|
+
#taken from Ian Dee's Encumber
|
129
|
+
def post_to_uispec_server( verb, command_hash )
|
130
|
+
url = frank_url_for( verb )
|
131
|
+
req = Net::HTTP::Post.new url.path
|
132
|
+
req.body = command_hash.to_json
|
133
|
+
|
134
|
+
make_http_request( url, req )
|
135
|
+
end
|
136
|
+
|
137
|
+
def get_to_uispec_server( verb )
|
138
|
+
url = frank_url_for( verb )
|
139
|
+
req = Net::HTTP::Get.new url.path
|
140
|
+
make_http_request( url, req )
|
141
|
+
end
|
142
|
+
|
143
|
+
def frank_url_for( verb )
|
144
|
+
url = URI.parse "http://localhost:37265/"
|
145
|
+
url.path = '/'+verb
|
146
|
+
url
|
147
|
+
end
|
148
|
+
|
149
|
+
def make_http_request( url, req )
|
150
|
+
http = Net::HTTP.new(url.host, url.port)
|
151
|
+
|
152
|
+
res = http.start do |sess|
|
153
|
+
sess.request req
|
154
|
+
end
|
155
|
+
|
156
|
+
res.body
|
157
|
+
end
|
158
|
+
|
159
|
+
def start_recording
|
160
|
+
%x{osascript<<APPLESCRIPT
|
161
|
+
tell application "QuickTime Player"
|
162
|
+
set sr to new screen recording
|
163
|
+
tell sr to start
|
164
|
+
end tell
|
165
|
+
APPLESCRIPT}
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
def stop_recording
|
170
|
+
%x{osascript<<APPLESCRIPT
|
171
|
+
tell application "QuickTime Player"
|
172
|
+
set sr to (document 1)
|
173
|
+
tell sr to stop
|
174
|
+
end tell
|
175
|
+
APPLESCRIPT}
|
176
|
+
end
|
177
|
+
|
178
|
+
def launch_app_in_simulator
|
179
|
+
%x{osascript<<APPLESCRIPT
|
180
|
+
application "iPhone Simulator" quit
|
181
|
+
tell application "Xcode"
|
182
|
+
set myprojectdocument to active project document
|
183
|
+
set myproject to project of myprojectdocument
|
184
|
+
tell myproject
|
185
|
+
launch
|
186
|
+
end tell
|
187
|
+
end tell
|
188
|
+
application "iPhone Simulator" activate
|
189
|
+
APPLESCRIPT}
|
190
|
+
sleep 5 # TODO: replace this with polling for the frank server
|
191
|
+
end
|
192
|
+
|
193
|
+
#Note this needs to have "Enable access for assistive devices"
|
194
|
+
#chcked in the Universal Access system preferences
|
195
|
+
def simulator_hardware_menu_press( menu_label )
|
196
|
+
%x{osascript<<APPLESCRIPT
|
197
|
+
activate application "iPhone Simulator"
|
198
|
+
tell application "System Events"
|
199
|
+
click menu item "#{menu_label}" of menu "Hardware" of menu bar of process "iPhone Simulator"
|
200
|
+
end tell
|
201
|
+
APPLESCRIPT}
|
202
|
+
end
|
203
|
+
|
204
|
+
def press_home_on_simulator
|
205
|
+
simulator_hardware_menu_press "Home"
|
206
|
+
end
|
207
|
+
|
208
|
+
def rotate_simulator_left
|
209
|
+
simulator_hardware_menu_press "Rotate Left"
|
210
|
+
end
|
211
|
+
|
212
|
+
def rotate_simulator_right
|
213
|
+
simulator_hardware_menu_press "Rotate Right"
|
214
|
+
end
|
215
|
+
|
216
|
+
def shake_simulator
|
217
|
+
simulator_hardware_menu_press "Shake Gesture"
|
218
|
+
end
|
219
|
+
|
220
|
+
def simulate_memory_warning
|
221
|
+
simulator_hardware_menu_press "Simulate Memory Warning"
|
222
|
+
end
|
223
|
+
|
224
|
+
def toggle_call_status_bar
|
225
|
+
simulator_hardware_menu_press "Toggle In-Call Status Bar"
|
226
|
+
end
|
227
|
+
|
228
|
+
def simulate_hardware_keyboard
|
229
|
+
simulator_hardware_menu_press "Simulate Hardware Keyboard"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
end end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: frank-cucumber
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 5
|
8
|
+
- 1
|
9
|
+
version: 0.5.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Pete Hodgson
|
13
|
+
- Derek Longmuir
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-15 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: cucumber
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rspec
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 2
|
42
|
+
- 0
|
43
|
+
version: "2.0"
|
44
|
+
type: :runtime
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: json
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
type: :runtime
|
57
|
+
version_requirements: *id003
|
58
|
+
description: Use cucumber to test native iOS apps via Frank
|
59
|
+
email:
|
60
|
+
- gems@thepete.net
|
61
|
+
executables:
|
62
|
+
- frank-skeleton
|
63
|
+
extensions: []
|
64
|
+
|
65
|
+
extra_rdoc_files: []
|
66
|
+
|
67
|
+
files:
|
68
|
+
- .gitignore
|
69
|
+
- Gemfile
|
70
|
+
- Rakefile
|
71
|
+
- bin/frank-skeleton
|
72
|
+
- example/features/example.feature
|
73
|
+
- example/features/step_definitions/example_steps.rb
|
74
|
+
- example/features/support/env.rb
|
75
|
+
- features-skeleton/my_first.feature
|
76
|
+
- features-skeleton/support/env.rb
|
77
|
+
- frank-cucumber.gemspec
|
78
|
+
- lib/frank-cucumber.rb
|
79
|
+
- lib/frank-cucumber/color_helper.rb
|
80
|
+
- lib/frank-cucumber/core_frank_steps.rb
|
81
|
+
- lib/frank-cucumber/frank_helper.rb
|
82
|
+
- lib/frank-cucumber/version.rb
|
83
|
+
has_rdoc: true
|
84
|
+
homepage: http://rubygems.org/gems/frank-cucumber
|
85
|
+
licenses: []
|
86
|
+
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
segments:
|
104
|
+
- 0
|
105
|
+
version: "0"
|
106
|
+
requirements: []
|
107
|
+
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 1.3.6
|
110
|
+
signing_key:
|
111
|
+
specification_version: 3
|
112
|
+
summary: Use cucumber to test native iOS apps via Frank
|
113
|
+
test_files: []
|
114
|
+
|