briar 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +29 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +36 -0
- data/README.md +25 -0
- data/Rakefile +12 -0
- data/briar.gemspec +26 -0
- data/dotirbc_briar_additions +31 -0
- data/features/step_definitions/alerts_and_sheets/action_sheet_steps.rb +8 -0
- data/features/step_definitions/alerts_and_sheets/alert_view_steps.rb +25 -0
- data/features/step_definitions/bars/navbar_steps.rb +95 -0
- data/features/step_definitions/bars/tabbar_steps.rb +32 -0
- data/features/step_definitions/bars/toolbar_steps.rb +17 -0
- data/features/step_definitions/briar_core_steps.rb +9 -0
- data/features/step_definitions/control/button_steps.rb +57 -0
- data/features/step_definitions/control/segmented_control_steps.rb +85 -0
- data/features/step_definitions/control/slider_steps.rb +9 -0
- data/features/step_definitions/email_steps.rb +59 -0
- data/features/step_definitions/image_view_steps.rb +9 -0
- data/features/step_definitions/keyboard_steps.rb +21 -0
- data/features/step_definitions/label_steps.rb +21 -0
- data/features/step_definitions/picker/date_picker_steps.rb +216 -0
- data/features/step_definitions/picker/picker_steps.rb +58 -0
- data/features/step_definitions/scroll_view_steps.rb +20 -0
- data/features/step_definitions/table_steps.rb +186 -0
- data/features/step_definitions/text_field_steps.rb +37 -0
- data/features/step_definitions/text_view_steps.rb +44 -0
- data/features/support/env.rb +0 -0
- data/features/support/hooks.rb +0 -0
- data/features/support/launch.rb +0 -0
- data/lib/briar.rb +72 -0
- data/lib/briar/alerts_and_sheets/alert_view.rb +16 -0
- data/lib/briar/bars/navbar.rb +104 -0
- data/lib/briar/bars/tabbar.rb +38 -0
- data/lib/briar/bars/toolbar.rb +36 -0
- data/lib/briar/briar_core.rb +56 -0
- data/lib/briar/briar_steps.rb +26 -0
- data/lib/briar/control/button.rb +47 -0
- data/lib/briar/control/segmented_control.rb +22 -0
- data/lib/briar/control/slider.rb +34 -0
- data/lib/briar/cucumber.rb +49 -0
- data/lib/briar/email.rb +58 -0
- data/lib/briar/gestalt.rb +80 -0
- data/lib/briar/image_view.rb +27 -0
- data/lib/briar/keyboard.rb +104 -0
- data/lib/briar/label.rb +34 -0
- data/lib/briar/picker/date_picker.rb +598 -0
- data/lib/briar/picker/picker.rb +32 -0
- data/lib/briar/picker/picker_shared.rb +34 -0
- data/lib/briar/scroll_view.rb +10 -0
- data/lib/briar/table.rb +237 -0
- data/lib/briar/text_field.rb +57 -0
- data/lib/briar/text_view.rb +21 -0
- data/lib/briar/version.rb +3 -0
- data/spec/briar/gestalt_spec.rb +62 -0
- data/spec/spec_helper.rb +17 -0
- metadata +164 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'calabash-cucumber'
|
2
|
+
|
3
|
+
module Briar
|
4
|
+
module ImageView
|
5
|
+
def image_view_exists? name
|
6
|
+
query_str = "imageView marked:'#{name}'"
|
7
|
+
exists = !query(query_str).empty?
|
8
|
+
if exists
|
9
|
+
alpha = query(query_str, :alpha).first.to_i
|
10
|
+
hidden = query(query_str, :isHidden).first.to_i
|
11
|
+
alpha > 0 and hidden == 0
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def should_see_image_view name
|
16
|
+
unless image_view_exists? name
|
17
|
+
screenshot_and_raise "i should see image view with id '#{name}'"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def should_not_see_image_view name
|
22
|
+
if image_view_exists? name
|
23
|
+
screenshot_and_raise "i should not see an image view with id #{name}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require "calabash-cucumber"
|
2
|
+
|
3
|
+
module Briar
|
4
|
+
module Keyboard
|
5
|
+
UITextAutocapitalizationTypeNone = 0
|
6
|
+
UITextAutocapitalizationTypeWords = 1
|
7
|
+
UITextAutocapitalizationTypeSentences = 2
|
8
|
+
UITextAutocapitalizationTypeAllCharacters = 3
|
9
|
+
|
10
|
+
UITextAutocorrectionTypeNo = 1
|
11
|
+
UITextAutocorrectionTypeYes = 2
|
12
|
+
|
13
|
+
UITextSpellCheckingTypeNo = 1
|
14
|
+
UITextSpellCheckingTypeYes = 2
|
15
|
+
|
16
|
+
|
17
|
+
def should_see_keyboard
|
18
|
+
res = element_exists("keyboardAutomatic")
|
19
|
+
unless res
|
20
|
+
screenshot_and_raise "Expected keyboard to be visible."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def should_not_see_keyboard
|
25
|
+
res = element_exists("keyboardAutomatic")
|
26
|
+
if res
|
27
|
+
screenshot_and_raise "Expected keyboard to not be visible."
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# is it possible to find what view the keyboard is responding to?
|
32
|
+
def autocapitalization_type ()
|
33
|
+
if !query("textView index:0").empty?
|
34
|
+
query("textView index:0", :autocapitalizationType).first.to_i
|
35
|
+
elsif !query("textField index:0").empty?
|
36
|
+
query("textField index:0", :autocapitalizationType).first.to_i
|
37
|
+
else
|
38
|
+
screenshot_and_raise "could not find a text view or text field"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_autocapitalization (type)
|
43
|
+
if !query("textView index:0").empty?
|
44
|
+
query("textView index:0", [{setAutocapitalizationType:type}])
|
45
|
+
elsif !query("textField index:0").empty?
|
46
|
+
query("textField index:0", [{setAutocapitalizationType:type}])
|
47
|
+
else
|
48
|
+
screenshot_and_raise "could not find a text view or text field"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def turn_autocapitalization_off
|
53
|
+
set_autocapitalization UITextAutocapitalizationTypeNone
|
54
|
+
end
|
55
|
+
|
56
|
+
def set_autocorrect (type)
|
57
|
+
if !query("textView index:0").empty?
|
58
|
+
query("textView index:0", [{setAutocorrectionType:type}])
|
59
|
+
elsif !query("textField index:0").empty?
|
60
|
+
query("textField index:0", [{setAutocorrectionType:type}])
|
61
|
+
else
|
62
|
+
screenshot_and_raise "could not find a text view or text field"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def turn_autocorrect_off
|
67
|
+
set_autocorrect UITextAutocorrectionTypeNo
|
68
|
+
end
|
69
|
+
|
70
|
+
def turn_spell_correct_off
|
71
|
+
if !query("textView index:0").empty?
|
72
|
+
query("textView index:0", [{setSpellCheckingType:UITextSpellCheckingTypeNo}])
|
73
|
+
elsif !query("textField index:0").empty?
|
74
|
+
query("textField index:0", [{setSpellCheckingType:UITextSpellCheckingTypeNo}])
|
75
|
+
else
|
76
|
+
screenshot_and_raise "could not find a text view or text field"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
#def is_capitalize_none (cap_type)
|
81
|
+
# cap_type == UITextAutocapitalizationTypeNone
|
82
|
+
#end
|
83
|
+
#
|
84
|
+
#def is_capitalize_words (cap_type)
|
85
|
+
# cap_type == UITextAutocapitalizationTypeWords
|
86
|
+
#end
|
87
|
+
#
|
88
|
+
#def is_capitalize_sentences (cap_type)
|
89
|
+
# cap_type == UITextAutocapitalizationTypeSentences
|
90
|
+
#end
|
91
|
+
#
|
92
|
+
#def is_capitalize_all (cap_type)
|
93
|
+
# cap_type == UITextAutocapitalizationTypeAllCharacters
|
94
|
+
#end
|
95
|
+
#
|
96
|
+
#def is_autocorrect_on (state)
|
97
|
+
# state == UITextAutocorrectionTypeYes
|
98
|
+
#end
|
99
|
+
#
|
100
|
+
#def is_autocorrect_off (state)
|
101
|
+
# state == UITextAutocorrectionTypeNo
|
102
|
+
#end
|
103
|
+
end
|
104
|
+
end
|
data/lib/briar/label.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "calabash-cucumber"
|
2
|
+
|
3
|
+
module Briar
|
4
|
+
module Label
|
5
|
+
def label_exists? (name)
|
6
|
+
!query("label marked:'#{name}'").empty?
|
7
|
+
end
|
8
|
+
|
9
|
+
def should_see_label (name)
|
10
|
+
res = label_exists? (name)
|
11
|
+
unless res
|
12
|
+
screenshot_and_raise "i could not find label with access id #{name}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def label_exists_with_text? (name, text)
|
17
|
+
actual = query("label marked:'#{name}'", :text).first
|
18
|
+
actual.eql? text
|
19
|
+
end
|
20
|
+
|
21
|
+
def should_see_label_with_text (name, text)
|
22
|
+
unless label_exists_with_text?(name, text)
|
23
|
+
actual = query("label marked:'#{name}'", :text).first
|
24
|
+
screenshot_and_raise "i expected to see '#{text}' in label named '#{name}' but found '#{actual}'"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def should_not_see_label_with_text (name, text)
|
29
|
+
if label_exists_with_text?(name, text)
|
30
|
+
screenshot_and_raise "i expected that i would not see '#{text}' in label named '#{name}'"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,598 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'calabash-cucumber'
|
3
|
+
|
4
|
+
module Briar
|
5
|
+
module Picker
|
6
|
+
module Date
|
7
|
+
include Briar::Picker_Shared
|
8
|
+
=begin
|
9
|
+
TODO use minute interval modes 5,10,15 etc. to test if date is reachable
|
10
|
+
TODO use the max/min dates to determine if a date is reachable
|
11
|
+
TODO when manually moving the picker wheels - speed things up by figuring out which direction to scroll the hour/minutes
|
12
|
+
=end
|
13
|
+
|
14
|
+
|
15
|
+
=begin
|
16
|
+
|
17
|
+
requires: picker_common_step.rb
|
18
|
+
|
19
|
+
examples
|
20
|
+
|
21
|
+
Then I should see that the date picker is in time mode
|
22
|
+
Then I should see that the time on the picker is now
|
23
|
+
|
24
|
+
Then I change the time on the picker to "1:45 PM"
|
25
|
+
Then I change the time on the picker to "13:45"
|
26
|
+
|
27
|
+
Then I change the date on the picker to "Sat Nov 17"
|
28
|
+
Then I change the date on the picker to "Sat 17 Nov"
|
29
|
+
|
30
|
+
Then I change the picker date to "Sat Nov 17" and the time to "0:00"
|
31
|
+
Then I change the picker date to "Sat 17 Nov" and the time to "12:00 AM"
|
32
|
+
|
33
|
+
Then I change the picker to 2 days ago at "9:30 PM"
|
34
|
+
Then I should see that the "checkin" row has the time I just entered in the "status" label
|
35
|
+
|
36
|
+
|
37
|
+
this file provides 2 ways to manipulate a date picker:
|
38
|
+
|
39
|
+
1. AUTOMATIC <== setting the date directly using a UIDatePicker category method
|
40
|
+
2. MANUAL <== setting the date by manipulating the picker wheels
|
41
|
+
|
42
|
+
there are pros and cons for each approach.
|
43
|
+
|
44
|
+
AUTOMATIC pros
|
45
|
+
1. the date selection happens almost instantly
|
46
|
+
2. there is very little parsing of date and time strings <== fewer errors
|
47
|
+
3. it is accomplished in a small number of lines of code <== fewer errors
|
48
|
+
|
49
|
+
AUTOMATIC cons
|
50
|
+
1. it does not really simulate the user interaction with the picker wheels.
|
51
|
+
|
52
|
+
this is actually very hard to do because there are cases where changing
|
53
|
+
one column will cause another column to change. for example: when in 12h
|
54
|
+
mode, if the user rotates the hour to 12, then the period column will change.
|
55
|
+
this change cannot be detected on the calabash side so either it has to be
|
56
|
+
guarded against (don't rotate past 12) or the AM/PM must be changed last.
|
57
|
+
|
58
|
+
2. requires a category on UIDatePicker <== pollutes the application environment
|
59
|
+
3. uses the query language to make changes to application state <== doesn't seem kosher
|
60
|
+
|
61
|
+
in my mind this is a little like the keyboard_enter_text because that method
|
62
|
+
enters text in a way that no user can (i.e. so fast)
|
63
|
+
|
64
|
+
MANUAL pros
|
65
|
+
1. it directly simulates what a user does
|
66
|
+
|
67
|
+
|
68
|
+
MANUAL cons
|
69
|
+
1. it is very slow <== long tests are a drag
|
70
|
+
2. there is a lot of string <==> date parsing <== more errors
|
71
|
+
3. lots of special case handling <== more errors
|
72
|
+
|
73
|
+
|
74
|
+
to use the automatic mode, include this category in your CALABASH target
|
75
|
+
|
76
|
+
=== BEGIN ===
|
77
|
+
@interface UIDatePicker (CALABASH_ADDITIONS)
|
78
|
+
- (NSString *) hasCalabashAdditions:(NSString *) aSuccessIndicator;
|
79
|
+
- (BOOL) setDateWithString:(NSString *)aString
|
80
|
+
format:(NSString *) aFormat
|
81
|
+
animated:(BOOL) aAnimated;
|
82
|
+
@end
|
83
|
+
|
84
|
+
|
85
|
+
@implementation UIDatePicker (CALABASH_ADDITIONS)
|
86
|
+
- (NSString *) hasCalabashAdditions:(NSString *) aSuccessIndicator {
|
87
|
+
return aSuccessIndicator;
|
88
|
+
}
|
89
|
+
|
90
|
+
- (BOOL) setDateWithString:(NSString *)aString
|
91
|
+
format:(NSString *) aFormat
|
92
|
+
animated:(BOOL) aAnimated {
|
93
|
+
NSDateFormatter *df = [[NSDateFormatter alloc] init];
|
94
|
+
[df setDateFormat:aFormat];
|
95
|
+
NSDate *date = [df dateFromString:aString];
|
96
|
+
if (date == nil) return NO;
|
97
|
+
[self setDate:date animated:aAnimated];
|
98
|
+
return YES;
|
99
|
+
}
|
100
|
+
@end
|
101
|
+
=== END ===
|
102
|
+
|
103
|
+
=end
|
104
|
+
|
105
|
+
# 0.2 is too fast because the picker does not pause at the date long enough for
|
106
|
+
# the date to change. 0.3 seems to work, but 0.4 is best i think.
|
107
|
+
PICKER_STEP_PAUSE = 0.4.to_f
|
108
|
+
PICKER_AM = "AM"
|
109
|
+
PICKER_PM = "PM"
|
110
|
+
|
111
|
+
# most locales and situations prefer _not_ to have leading zeros on hours in 24h
|
112
|
+
# see usage below to find out when and if the zeros are stripped
|
113
|
+
PICKER_24H_TIME_FMT = '%H:%M'
|
114
|
+
PICKER_12H_TIME_FMT = '%l:%M %p'
|
115
|
+
PICKER_ISO8601_TIME_FMT = '%H:%M'
|
116
|
+
|
117
|
+
PICKER_REMOVE_LEADING_ZERO_REGEX = /\A^0/
|
118
|
+
|
119
|
+
# 24h locales - Fri 16 Nov - 24h locales
|
120
|
+
PICKER_24H_DATE_FMT = '%a %e %b'
|
121
|
+
# common format for US Fri Nov 16
|
122
|
+
PICKER_12H_DATE_FMT = '%a %b %e'
|
123
|
+
|
124
|
+
# our canonical format for testing if two dates are the same
|
125
|
+
PICKER_ISO8601_DATE_FMT = '%Y-%m-%d'
|
126
|
+
PICKER_ISO8601_DATE_TIME_FMT = '%Y-%m-%d %H:%M'
|
127
|
+
|
128
|
+
# when we are using the date picker category, this is the format of the string
|
129
|
+
# we will send to setDateWithString:animated:
|
130
|
+
#
|
131
|
+
# ex. 2012_11_18_16_45
|
132
|
+
PICKER__RUBY___SET_PICKER_DATE__DATE_AND_TIME_FMT = '%Y_%m_%d_%H_%M_%z'
|
133
|
+
PICKER__OBJC___SET_PICKER_DATE__DATE_AND_TIME_FMT = 'yyyy_MM_dd_HH_mm_Z'
|
134
|
+
|
135
|
+
# iOS 5
|
136
|
+
PICKER_VIEW_CLASS_IOS5 = "datePickerView"
|
137
|
+
PICKER_VIEW_CLASS_IOS6 = "view:'_UIDatePickerView'"
|
138
|
+
|
139
|
+
# testing for existence
|
140
|
+
def should_see_date_picker (picker_id)
|
141
|
+
res = !query("datePicker marked:'#{picker_id}'").empty?
|
142
|
+
unless res
|
143
|
+
screenshot_and_raise "could not find date picker #{picker_id}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# getting dates from the picker
|
148
|
+
|
149
|
+
def picker_date_time
|
150
|
+
res = query("datePicker", :date)
|
151
|
+
screenshot_and_raise "expected to see a date picker" if res.empty?
|
152
|
+
DateTime.parse(res.first)
|
153
|
+
end
|
154
|
+
|
155
|
+
# appledoc ==> The property is an NSDate object or nil (the default), which
|
156
|
+
# means no maximum date.
|
157
|
+
def picker_maximum_date_time
|
158
|
+
res = query("datePicker", :maximumDate)
|
159
|
+
screenshot_and_raise "expected to see a date picker" if res.empty?
|
160
|
+
res.first != nil ? DateTime.parse(res.first) : nil
|
161
|
+
end
|
162
|
+
|
163
|
+
# appledoc ==> The property is an NSDate object or nil (the default), which
|
164
|
+
# means no minimum date.
|
165
|
+
def picker_minimum_date_time
|
166
|
+
res = query("datePicker", :minimumDate)
|
167
|
+
screenshot_and_raise "expected to see a date picker" if res.empty?
|
168
|
+
res.first != nil ? DateTime.parse(res.first) : nil
|
169
|
+
end
|
170
|
+
|
171
|
+
# automatic date setting
|
172
|
+
|
173
|
+
# checking to see if the picker is visible and has the calabash category
|
174
|
+
# additions
|
175
|
+
def picker_has_calabash_additions
|
176
|
+
success_value = "1"
|
177
|
+
res = query("datePicker", [{hasCalabashAdditions:success_value}])
|
178
|
+
screenshot_and_raise "picker is not visible" if res.empty?
|
179
|
+
res.first.eql? success_value
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
def date_time_or_time_str_is_in_24h (date_time_or_time_str)
|
184
|
+
date_time_or_time_str[-1, 1].scan(/^[a-zA-Z]/).empty?
|
185
|
+
end
|
186
|
+
|
187
|
+
def date_str_is_in_24h (date_str)
|
188
|
+
!date_str[-1, 1].scan(/^[a-zA-Z]/).empty?
|
189
|
+
end
|
190
|
+
|
191
|
+
def date_str_to_send_to_picker_from_time_str (time_str)
|
192
|
+
time_in_24h = date_time_or_time_str_is_in_24h time_str
|
193
|
+
time_fmt = time_in_24h ? PICKER_24H_TIME_FMT : PICKER_12H_TIME_FMT
|
194
|
+
date_fmt = time_in_24h ? PICKER_24H_DATE_FMT : PICKER_12H_DATE_FMT
|
195
|
+
date_str = picker_date_time.strftime(date_fmt).squeeze(" ").strip
|
196
|
+
|
197
|
+
date_time_str = "#{date_str} #{time_str}"
|
198
|
+
date_time_fmt = "#{date_fmt} #{time_fmt}"
|
199
|
+
|
200
|
+
date_time = DateTime.strptime(date_time_str, date_time_fmt)
|
201
|
+
date_time.strftime(PICKER__RUBY___SET_PICKER_DATE__DATE_AND_TIME_FMT).squeeze(" ").strip
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
def date_str_to_send_to_picker_from_date_str (date_str)
|
206
|
+
date_in_24h = date_str_is_in_24h (date_str)
|
207
|
+
time_fmt = date_in_24h ? PICKER_24H_TIME_FMT : PICKER_12H_TIME_FMT
|
208
|
+
date_fmt = date_in_24h ? PICKER_24H_DATE_FMT : PICKER_12H_DATE_FMT
|
209
|
+
time_str = picker_date_time.strftime(time_fmt).squeeze(" ").strip
|
210
|
+
date_time_str = "#{date_str} #{time_str}"
|
211
|
+
date_time_fmt = "#{date_fmt} #{time_fmt}"
|
212
|
+
date_time = DateTime.strptime(date_time_str, date_time_fmt)
|
213
|
+
date_time.strftime(PICKER__RUBY___SET_PICKER_DATE__DATE_AND_TIME_FMT).squeeze(" ").strip
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
def set_picker_date_with_date_time_str (date_time_str, animated=1)
|
218
|
+
query("datePicker", [{respondsToSelector:"minuteInterval"}])
|
219
|
+
|
220
|
+
|
221
|
+
res = query("datePicker", [{setDateWithString:date_time_str},
|
222
|
+
{format:"#{PICKER__OBJC___SET_PICKER_DATE__DATE_AND_TIME_FMT}"},
|
223
|
+
{animated:animated.to_i}])
|
224
|
+
screenshot_and_raise "could not find a date picker to query" if res.empty?
|
225
|
+
if res.first.to_i == 0
|
226
|
+
screenshot_and_raise "could not set the picker date with '#{date_time_str}' and '#{PICKER__RUBY___SET_PICKER_DATE__DATE_AND_TIME_FMT}'"
|
227
|
+
end
|
228
|
+
|
229
|
+
# REQUIRED
|
230
|
+
sleep(PICKER_STEP_PAUSE)
|
231
|
+
|
232
|
+
# the query does not create a UIControlEventValueChanged event, so we have to
|
233
|
+
# to a touch event
|
234
|
+
|
235
|
+
# if the picker is in time mode, then we dont need to worry about min/max
|
236
|
+
# if the picker is date or date time mode, i think the first column is
|
237
|
+
# always scrollable up _and_ it sends an event even if the date is beyond
|
238
|
+
# the maximum date
|
239
|
+
|
240
|
+
#picker_max_date = picker_maximum_date_time
|
241
|
+
#picker_min_date = picker_minimum_date_time
|
242
|
+
#target_date = DateTime.strptime(date_time_str, PICKER__RUBY___SET_PICKER_DATE__DATE_AND_TIME_FMT)
|
243
|
+
|
244
|
+
#column_one_index = picker_current_index_for_column 0
|
245
|
+
#query("pickerTableView index:0", [{selectRow:column_one_index}, {animated:1}, {notify:1}])
|
246
|
+
|
247
|
+
picker_scroll_down_on_column 0
|
248
|
+
sleep(PICKER_STEP_PAUSE)
|
249
|
+
picker_scroll_up_on_column 0
|
250
|
+
sleep(PICKER_STEP_PAUSE)
|
251
|
+
#if (target_date + 1) > picker_max_date
|
252
|
+
# picker_scroll_down_on_column 0
|
253
|
+
# sleep(PICKER_STEP_PAUSE)
|
254
|
+
# picker_scroll_up_on_column 0
|
255
|
+
#elsif (target_date - 1) < picker_min_date
|
256
|
+
# picker_scroll_up_on_column 0
|
257
|
+
# sleep(PICKER_STEP_PAUSE)
|
258
|
+
# picker_scroll_down_on_column 0
|
259
|
+
#else
|
260
|
+
# screenshot_and_raise "could not figure out which way to rotate the day column to trigger an event"
|
261
|
+
#end
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
# apple docs
|
266
|
+
# You can use this property to set the interval displayed by the minutes wheel
|
267
|
+
# (for example, 15 minutes). The interval value must be evenly divided into 60;
|
268
|
+
# if it is not, the default value is used. The default and minimum values are 1;
|
269
|
+
# the maximum value is 30.
|
270
|
+
def picker_minute_interval
|
271
|
+
screenshot_and_raise "there is no minute in date mode" if picker_is_in_date_mode
|
272
|
+
screenshot_and_raise "nyi" if picker_is_in_countdown_mode
|
273
|
+
res = query("datePicker", :minuteInterval)
|
274
|
+
screenshot_and_raise "expected to see a date picker" if res.empty?
|
275
|
+
@picker_minute_interval = res.first
|
276
|
+
end
|
277
|
+
|
278
|
+
def time_hash_by_add_minutes_until_at_closest_interval (time_str, interval=picker_minute_interval())
|
279
|
+
screenshot_and_raise "interval '#{interval}' is not on (0, 59) which is not allowed" unless (0..59).member?(interval)
|
280
|
+
time = Time.parse(time_str)
|
281
|
+
# normalize to zero
|
282
|
+
time = time - time.sec
|
283
|
+
minute = time.min
|
284
|
+
count = 0
|
285
|
+
unless (minute % interval) == 0
|
286
|
+
begin
|
287
|
+
minute = (minute > 59) ? 0 : minute + 1
|
288
|
+
count = count + 1
|
289
|
+
end
|
290
|
+
end while ((minute % interval) != 0)
|
291
|
+
time = time + (count * 60)
|
292
|
+
|
293
|
+
{:h12 => time.strftime(PICKER_12H_TIME_FMT).squeeze(" ").strip,
|
294
|
+
:h24 => time.strftime(PICKER_24H_TIME_FMT).squeeze(" ").strip.sub(PICKER_REMOVE_LEADING_ZERO_REGEX,""),
|
295
|
+
:time => time}
|
296
|
+
end
|
297
|
+
|
298
|
+
# picker modes
|
299
|
+
|
300
|
+
UIDatePickerModeTime = 0
|
301
|
+
UIDatePickerModeDate = 1
|
302
|
+
UIDatePickerModeDateAndTime = 2
|
303
|
+
UIDatePickerModeCountDownTimer = 3
|
304
|
+
|
305
|
+
def picker_mode
|
306
|
+
res = query("datePicker", :datePickerMode)
|
307
|
+
screenshot_and_raise "expected to see a date picker" if res.empty?
|
308
|
+
res.first
|
309
|
+
end
|
310
|
+
|
311
|
+
def picker_is_in_time_mode
|
312
|
+
picker_mode == UIDatePickerModeTime
|
313
|
+
end
|
314
|
+
|
315
|
+
def picker_is_in_date_mode
|
316
|
+
picker_mode == UIDatePickerModeDate
|
317
|
+
end
|
318
|
+
|
319
|
+
def picker_is_in_date_and_time_mode
|
320
|
+
picker_mode == UIDatePickerModeDateAndTime
|
321
|
+
end
|
322
|
+
|
323
|
+
def picker_is_in_countdown_mode
|
324
|
+
picker_mode == UIDatePickerModeCountDownTimer
|
325
|
+
end
|
326
|
+
|
327
|
+
# columns for different modes
|
328
|
+
|
329
|
+
def picker_column_for_hour
|
330
|
+
screenshot_and_raise "there is no hour column in date mode" if picker_is_in_date_mode
|
331
|
+
screenshot_and_raise "nyi" if picker_is_in_countdown_mode
|
332
|
+
picker_is_in_time_mode ? 0 : 1
|
333
|
+
end
|
334
|
+
|
335
|
+
def picker_column_for_minute
|
336
|
+
screenshot_and_raise "there is no minute column in date mode" if picker_is_in_date_mode
|
337
|
+
screenshot_and_raise "nyi" if picker_is_in_countdown_mode
|
338
|
+
picker_is_in_time_mode ? 1 : 2
|
339
|
+
end
|
340
|
+
|
341
|
+
def picker_column_for_period
|
342
|
+
screenshot_and_raise "there is no period column in date mode" if picker_is_in_date_mode
|
343
|
+
screenshot_and_raise "nyi" if picker_is_in_countdown_mode
|
344
|
+
picker_is_in_time_mode ? 2 : 3
|
345
|
+
end
|
346
|
+
|
347
|
+
# 12h or 24h locale
|
348
|
+
|
349
|
+
def picker_is_in_12h_locale
|
350
|
+
screenshot_and_raise "12h/24h mode is not applicable to this mode" if picker_is_in_date_mode or picker_is_in_countdown_mode
|
351
|
+
column = picker_column_for_period
|
352
|
+
!query("pickerTableView index:#{column}").empty?
|
353
|
+
end
|
354
|
+
|
355
|
+
def picker_is_in_24h_locale
|
356
|
+
!picker_is_in_12h_locale
|
357
|
+
end
|
358
|
+
|
359
|
+
# dealing with the period (aka meridian) column
|
360
|
+
|
361
|
+
# this will cause problems with localizations - for example:
|
362
|
+
# pt (lisbon) - a.m./p.m.
|
363
|
+
# de - nach/vor
|
364
|
+
def picker_period
|
365
|
+
screenshot_and_raise "picker is not in 12h mode" if picker_is_in_24h_locale
|
366
|
+
date = picker_date_time
|
367
|
+
date_str = date.strftime(PICKER_12H_TIME_FMT)
|
368
|
+
tokens = date_str.split(" ")
|
369
|
+
tokens.last
|
370
|
+
end
|
371
|
+
|
372
|
+
def picker_period_is_am?
|
373
|
+
picker_period.eql?("AM")
|
374
|
+
end
|
375
|
+
|
376
|
+
def picker_period_is_pm?
|
377
|
+
picker_period.eql?("PM")
|
378
|
+
end
|
379
|
+
|
380
|
+
# weekday, month, day, etc
|
381
|
+
|
382
|
+
def picker_weekday
|
383
|
+
screenshot_and_raise "weekday is not applicable to this mode" if picker_is_in_time_mode or picker_is_in_countdown_mode
|
384
|
+
res = query("datePickerWeekMonthDayView", :weekdayLabel, :text)[2]
|
385
|
+
# need to guard against Today showing
|
386
|
+
res == nil ? Date.today.strftime('%a') : res
|
387
|
+
end
|
388
|
+
|
389
|
+
def picker_month_day
|
390
|
+
screenshot_and_raise "month/day is not applicable to this mode" if picker_is_in_time_mode or picker_is_in_countdown_mode
|
391
|
+
res = query("datePickerWeekMonthDayView", :dateLabel, :text)[2]
|
392
|
+
picker_iso = picker_date_time.strftime(PICKER_ISO8601_DATE_FMT).squeeze(" ").strip
|
393
|
+
today = Date.today
|
394
|
+
today_iso = today.strftime(PICKER_ISO8601_DATE_FMT).squeeze(" ").strip
|
395
|
+
fmt = picker_is_in_24h_locale ? '%e %b' : '%b %e'
|
396
|
+
(picker_iso.eql? today_iso) ? today.strftime(fmt) : res
|
397
|
+
end
|
398
|
+
|
399
|
+
def picker_date_str
|
400
|
+
"#{picker_weekday} #{picker_month_day}".strip.squeeze(" ")
|
401
|
+
end
|
402
|
+
|
403
|
+
def picker_weekday_month_day_is (weekday_month_day)
|
404
|
+
weekday_month_day.eql? picker_date_str
|
405
|
+
end
|
406
|
+
|
407
|
+
# dealing with time
|
408
|
+
|
409
|
+
def picker_hour_24h
|
410
|
+
screenshot_and_raise "hour is not applicable to this mode" if picker_is_in_countdown_mode or picker_is_in_date_mode
|
411
|
+
# query always returns as 24h
|
412
|
+
res_ios5 = query(PICKER_VIEW_CLASS_IOS5, :hour).first
|
413
|
+
res_ios6 = query(PICKER_VIEW_CLASS_IOS6, :hour).first
|
414
|
+
return res_ios5 != nil ? res_ios5 : res_ios6
|
415
|
+
#query("datePickerView", :hour).first
|
416
|
+
end
|
417
|
+
|
418
|
+
def picker_hour_24h_str
|
419
|
+
screenshot_and_raise "hour is not applicable to this mode" if picker_is_in_countdown_mode or picker_is_in_date_mode
|
420
|
+
"%02d" % picker_hour_24h
|
421
|
+
end
|
422
|
+
|
423
|
+
def picker_hour_12h
|
424
|
+
screenshot_and_raise "hour is not applicable to this mode" if picker_is_in_countdown_mode or picker_is_in_date_mode
|
425
|
+
hour_24h = picker_hour_24h
|
426
|
+
return 12 if hour_24h == 0
|
427
|
+
hour_24h > 12 ? hour_24h - 12 : hour_24h
|
428
|
+
end
|
429
|
+
|
430
|
+
def picker_hour_12h_str
|
431
|
+
screenshot_and_raise "hour is not applicable to this mode" if picker_is_in_countdown_mode or picker_is_in_date_mode
|
432
|
+
"#{picker_hour_12h}"
|
433
|
+
end
|
434
|
+
|
435
|
+
def picker_hour_24h_is (target_hour)
|
436
|
+
target_hour == picker_hour_24h
|
437
|
+
end
|
438
|
+
|
439
|
+
def picker_hour_12h_is (target_hour)
|
440
|
+
target_hour == picker_hour_12h
|
441
|
+
end
|
442
|
+
|
443
|
+
def picker_hour_is (target_hour)
|
444
|
+
picker_is_in_24h_locale ? picker_hour_24h_is(target_hour) : picker_hour_12h_is(target_hour)
|
445
|
+
end
|
446
|
+
|
447
|
+
def picker_minute
|
448
|
+
screenshot_and_raise "hour is not applicable to this mode" if picker_is_in_countdown_mode or picker_is_in_date_mode
|
449
|
+
res_ios5 = query(PICKER_VIEW_CLASS_IOS5, :minute).first
|
450
|
+
res_ios6 = query(PICKER_VIEW_CLASS_IOS6, :minute).first
|
451
|
+
return res_ios5 != nil ? res_ios5 : res_ios6
|
452
|
+
#query("datePickerView", :minute).first
|
453
|
+
end
|
454
|
+
|
455
|
+
def picker_minute_str
|
456
|
+
screenshot_and_raise "hour is not applicable to this mode" if picker_is_in_countdown_mode or picker_is_in_date_mode
|
457
|
+
"%02d" % picker_minute
|
458
|
+
#"%02d" % query("datePickerView", :minute).first
|
459
|
+
end
|
460
|
+
|
461
|
+
def picker_minute_is (target_minute)
|
462
|
+
target_minute == picker_minute
|
463
|
+
end
|
464
|
+
|
465
|
+
def picker_time_24h_str
|
466
|
+
screenshot_and_raise "the time is not applicable for this mode" if picker_is_in_date_mode or picker_is_in_countdown_mode
|
467
|
+
"#{picker_hour_24h_str}:#{picker_minute_str}".strip.sub(PICKER_REMOVE_LEADING_ZERO_REGEX, "")
|
468
|
+
end
|
469
|
+
|
470
|
+
def picker_time_12h_str
|
471
|
+
screenshot_and_raise "the time is not applicable for this mode" if picker_is_in_date_mode or picker_is_in_countdown_mode
|
472
|
+
"#{picker_hour_12h_str}:#{picker_minute_str} #{picker_period}"
|
473
|
+
end
|
474
|
+
|
475
|
+
def picker_time_str
|
476
|
+
screenshot_and_raise "the time is not applicable for this mode" if picker_is_in_date_mode or picker_is_in_countdown_mode
|
477
|
+
picker_is_in_24h_locale ? picker_time_24h_str : picker_time_12h_str
|
478
|
+
end
|
479
|
+
|
480
|
+
def picker_time_for_other_locale
|
481
|
+
time_str = picker_is_in_24h_locale ? picker_time_24h_str : picker_time_12h_str
|
482
|
+
fmt_str = picker_is_in_24h_locale ? PICKER_12H_TIME_FMT : PICKER_24H_TIME_FMT
|
483
|
+
Time.parse(time_str).strftime(fmt_str).squeeze(" ").strip.sub(PICKER_REMOVE_LEADING_ZERO_REGEX,"")
|
484
|
+
end
|
485
|
+
|
486
|
+
# date and time
|
487
|
+
|
488
|
+
def picker_date_and_time_str_24h
|
489
|
+
screenshot_and_raise "the time is not applicable for this mode" if picker_is_in_date_mode or picker_is_in_countdown_mode
|
490
|
+
"#{picker_date_str} #{picker_time_24h_str}"
|
491
|
+
end
|
492
|
+
|
493
|
+
def picker_date_and_time_str_12h
|
494
|
+
screenshot_and_raise "the time is not applicable for this mode" if picker_is_in_date_mode or picker_is_in_countdown_mode
|
495
|
+
"#{picker_date_str} #{picker_time_12h_str}"
|
496
|
+
end
|
497
|
+
|
498
|
+
def picker_date_and_time_str
|
499
|
+
"#{picker_date_str} #{picker_time_str}"
|
500
|
+
end
|
501
|
+
|
502
|
+
def picker_date_and_time_str_for_other_locale
|
503
|
+
"#{picker_date_str} #{picker_time_for_other_locale}"
|
504
|
+
end
|
505
|
+
|
506
|
+
# useful
|
507
|
+
|
508
|
+
def now_times_map
|
509
|
+
now = Time.new
|
510
|
+
{:h12 => now.strftime(PICKER_12H_TIME_FMT).squeeze(" ").strip,
|
511
|
+
:h24 => now.strftime(PICKER_24H_TIME_FMT).squeeze(" ").strip.sub(PICKER_REMOVE_LEADING_ZERO_REGEX,""),
|
512
|
+
:time => now}
|
513
|
+
end
|
514
|
+
|
515
|
+
def now_time_24h_locale
|
516
|
+
now_times_map[:h24]
|
517
|
+
end
|
518
|
+
|
519
|
+
def now_time_12h_locale
|
520
|
+
now_times_map[:h12]
|
521
|
+
end
|
522
|
+
|
523
|
+
# scrolling picker
|
524
|
+
|
525
|
+
def picker_scroll_to_hour (target_hour_int_24h_notation)
|
526
|
+
screenshot_and_raise "nyi" if picker_is_in_countdown_mode
|
527
|
+
column = picker_column_for_hour
|
528
|
+
limit = 25
|
529
|
+
count = 0
|
530
|
+
unless picker_hour_24h_is target_hour_int_24h_notation
|
531
|
+
begin
|
532
|
+
picker_scroll_up_on_column(column)
|
533
|
+
sleep(PICKER_STEP_PAUSE)
|
534
|
+
count = count + 1
|
535
|
+
end while (not picker_hour_24h_is target_hour_int_24h_notation) and count < limit
|
536
|
+
end
|
537
|
+
unless picker_hour_24h_is target_hour_int_24h_notation
|
538
|
+
screenshot_and_raise "scrolled '#{limit}' but could not change hour to '#{target_hour_int_24h_notation}'"
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
def picker_scroll_to_minute (target_minute_int)
|
543
|
+
screenshot_and_raise "nyi" if picker_is_in_countdown_mode
|
544
|
+
column = picker_column_for_minute
|
545
|
+
limit = 61
|
546
|
+
count = 0
|
547
|
+
unless picker_minute_is target_minute_int
|
548
|
+
begin
|
549
|
+
picker_scroll_up_on_column(column)
|
550
|
+
sleep(PICKER_STEP_PAUSE)
|
551
|
+
count = count + 1
|
552
|
+
end while (not picker_minute_is target_minute_int) and count < limit
|
553
|
+
end
|
554
|
+
unless picker_minute_is target_minute_int
|
555
|
+
screenshot_and_raise "scrolled '#{limit}' but could not change minute to '#{target_minute_int}'"
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
def picker_scroll_to_period(target_period_str)
|
560
|
+
screenshot_and_raise "nyi" if picker_is_in_countdown_mode
|
561
|
+
screenshot_and_raise "period is not applicable to 24h locale" if picker_is_in_24h_locale
|
562
|
+
column = picker_column_for_period
|
563
|
+
limit = 3
|
564
|
+
count = 0
|
565
|
+
unless picker_period.eql? target_period_str
|
566
|
+
begin
|
567
|
+
if picker_period_is_am?
|
568
|
+
scroll_to_row("pickerTableView index:#{column}", 1)
|
569
|
+
else
|
570
|
+
scroll_to_row("pickerTableView index:#{column}", 0)
|
571
|
+
end
|
572
|
+
sleep(PICKER_STEP_PAUSE)
|
573
|
+
count = count + 1
|
574
|
+
end while (not picker_period.eql? target_period_str) and count < limit
|
575
|
+
end
|
576
|
+
unless picker_period.eql? target_period_str
|
577
|
+
screenshot_and_raise "scrolled '#{limit}' but could not change period to '#{target_period_str}'"
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
|
582
|
+
# utility
|
583
|
+
|
584
|
+
def time_strings_are_equivalent (a, b)
|
585
|
+
a_iso_str = Time.parse(a).strftime(PICKER_ISO8601_TIME_FMT)
|
586
|
+
b_iso_str = Time.parse(b).strftime(PICKER_ISO8601_TIME_FMT)
|
587
|
+
a_iso_str.eql? b_iso_str
|
588
|
+
end
|
589
|
+
|
590
|
+
def date_time_strings_are_equivalent (a, b)
|
591
|
+
a_iso_str = Date.parse(a).strftime(PICKER_ISO8601_DATE_TIME_FMT)
|
592
|
+
b_iso_str = Date.parse(b).strftime(PICKER_ISO8601_DATE_TIME_FMT)
|
593
|
+
a_iso_str.eql? b_iso_str
|
594
|
+
end
|
595
|
+
|
596
|
+
end
|
597
|
+
end
|
598
|
+
end
|