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.
Files changed (57) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +29 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +36 -0
  5. data/README.md +25 -0
  6. data/Rakefile +12 -0
  7. data/briar.gemspec +26 -0
  8. data/dotirbc_briar_additions +31 -0
  9. data/features/step_definitions/alerts_and_sheets/action_sheet_steps.rb +8 -0
  10. data/features/step_definitions/alerts_and_sheets/alert_view_steps.rb +25 -0
  11. data/features/step_definitions/bars/navbar_steps.rb +95 -0
  12. data/features/step_definitions/bars/tabbar_steps.rb +32 -0
  13. data/features/step_definitions/bars/toolbar_steps.rb +17 -0
  14. data/features/step_definitions/briar_core_steps.rb +9 -0
  15. data/features/step_definitions/control/button_steps.rb +57 -0
  16. data/features/step_definitions/control/segmented_control_steps.rb +85 -0
  17. data/features/step_definitions/control/slider_steps.rb +9 -0
  18. data/features/step_definitions/email_steps.rb +59 -0
  19. data/features/step_definitions/image_view_steps.rb +9 -0
  20. data/features/step_definitions/keyboard_steps.rb +21 -0
  21. data/features/step_definitions/label_steps.rb +21 -0
  22. data/features/step_definitions/picker/date_picker_steps.rb +216 -0
  23. data/features/step_definitions/picker/picker_steps.rb +58 -0
  24. data/features/step_definitions/scroll_view_steps.rb +20 -0
  25. data/features/step_definitions/table_steps.rb +186 -0
  26. data/features/step_definitions/text_field_steps.rb +37 -0
  27. data/features/step_definitions/text_view_steps.rb +44 -0
  28. data/features/support/env.rb +0 -0
  29. data/features/support/hooks.rb +0 -0
  30. data/features/support/launch.rb +0 -0
  31. data/lib/briar.rb +72 -0
  32. data/lib/briar/alerts_and_sheets/alert_view.rb +16 -0
  33. data/lib/briar/bars/navbar.rb +104 -0
  34. data/lib/briar/bars/tabbar.rb +38 -0
  35. data/lib/briar/bars/toolbar.rb +36 -0
  36. data/lib/briar/briar_core.rb +56 -0
  37. data/lib/briar/briar_steps.rb +26 -0
  38. data/lib/briar/control/button.rb +47 -0
  39. data/lib/briar/control/segmented_control.rb +22 -0
  40. data/lib/briar/control/slider.rb +34 -0
  41. data/lib/briar/cucumber.rb +49 -0
  42. data/lib/briar/email.rb +58 -0
  43. data/lib/briar/gestalt.rb +80 -0
  44. data/lib/briar/image_view.rb +27 -0
  45. data/lib/briar/keyboard.rb +104 -0
  46. data/lib/briar/label.rb +34 -0
  47. data/lib/briar/picker/date_picker.rb +598 -0
  48. data/lib/briar/picker/picker.rb +32 -0
  49. data/lib/briar/picker/picker_shared.rb +34 -0
  50. data/lib/briar/scroll_view.rb +10 -0
  51. data/lib/briar/table.rb +237 -0
  52. data/lib/briar/text_field.rb +57 -0
  53. data/lib/briar/text_view.rb +21 -0
  54. data/lib/briar/version.rb +3 -0
  55. data/spec/briar/gestalt_spec.rb +62 -0
  56. data/spec/spec_helper.rb +17 -0
  57. 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
@@ -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