frank-cucumber 0.5.1
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 +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
|
+
|