briar 0.0.4
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.
- 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
|