kuali-test-factory 0.5.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,158 @@
1
+ # Copyright 2012-2014 The rSmart Group, Inc.
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Some date and time helper functions....
16
+ module DateFactory
17
+
18
+ MONTHS = %w{JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC}
19
+
20
+ # Takes a time object and returns a hash containing
21
+ # various parts of the relevant date.
22
+ # @param time_object [Time] the moment you want to convert
23
+ # @returns [Hash] a hash object containing various parts of the date/time you passed to the method
24
+ #
25
+ def date_factory(time_object)
26
+ {
27
+ sakai: make_date(time_object),
28
+ sakai_rounded: make_date(time_object).gsub!(/:\d+/, ":#{Time.at(time_object.to_i/(5*60)*(5*60)).strftime("%M")}"), # Date with time rounded to nearest 5-minute mark.
29
+ short_date: time_object.strftime("%b %-d, %Y"), # => "Oct 18, 2013"
30
+ samigo: time_object.strftime("%m/%d/%Y %I:%M:%S %p"), # => "10/30/2012 07:02:05 AM"
31
+ MON: time_object.strftime("%^b"), # => "DEC"
32
+ Mon: time_object.strftime("%b"), # => "Jan"
33
+ Month: time_object.strftime("%B"), # => "February"
34
+ month_int: time_object.month, # => 3
35
+ day_of_month: time_object.day, # => 17 Note this is not zero-padded
36
+ weekday: time_object.strftime("%A"), # => "Monday"
37
+ wkdy: time_object.strftime("%a"), # => "Tue"
38
+ year: time_object.year, # => 2013
39
+ hour: time_object.strftime("%I").to_i, # => "07" Zero-padded, 12-hour clock
40
+ minute: time_object.strftime("%M"), # => "02" Zero-padded
41
+ minute_rounded: (Time.at(time_object.to_i/(5*60)*(5*60))).strftime("%M"), # => "05" Zero-padded, rounded to 5-minute increments
42
+ meridian: time_object.strftime("%P"), # => "pm"
43
+ MERIDIAN: time_object.strftime("%p"), # => "AM"
44
+ date_w_slashes: time_object.strftime("%m/%d/%Y"), # => 02/08/2013
45
+ custom: time_object # => Allows creation of a custom date string using the passed time value.
46
+ }
47
+ end
48
+
49
+ def an_hour_ago
50
+ date_factory(Time.now - 3600)
51
+ end
52
+ alias last_hour an_hour_ago
53
+
54
+ def right_now
55
+ date_factory(Time.now)
56
+ end
57
+
58
+ def in_an_hour
59
+ date_factory(Time.now + 3600)
60
+ end
61
+ alias next_hour in_an_hour
62
+
63
+ def last_year
64
+ date_factory(Time.now - (3600*24*365))
65
+ end
66
+ alias a_year_ago last_year
67
+
68
+ # Returns a randomly selected date/time from
69
+ # within the last year.
70
+ def in_the_last_year
71
+ date_factory(Time.random(:year_range=>1))
72
+ end
73
+
74
+ def last_month
75
+ index = MONTHS.index(current_month)
76
+ return MONTHS[index-1]
77
+ end
78
+
79
+ def hours_ago(hours)
80
+ date_factory(Time.now - hours*3600)
81
+ end
82
+
83
+ def hours_from_now(hours)
84
+ date_factory(Time.now + hours*3600)
85
+ end
86
+
87
+ # Takes an integer representing
88
+ # the count of minutes as the parameter, and
89
+ # returns the date_factory hash for the
90
+ # resulting Time value.
91
+ #
92
+ def minutes_ago(mins)
93
+ date_factory(Time.now - mins*60)
94
+ end
95
+
96
+ def minutes_from_now(mins)
97
+ date_factory(Time.now + mins*60)
98
+ end
99
+
100
+ # Returns the current month as an
101
+ # upper-case 3-letter string.
102
+ # example: "JUL"
103
+ #
104
+ def current_month
105
+ Time.now.strftime("%^b")
106
+ end
107
+
108
+ def next_month
109
+ index = MONTHS.index(current_month)
110
+ if index < 11
111
+ return MONTHS[index+1]
112
+ else
113
+ return MONTHS[0]
114
+ end
115
+ end
116
+
117
+ def in_a_year
118
+ date_factory(Time.now + (3600*24*365))
119
+ end
120
+ alias next_year in_a_year
121
+
122
+ def yesterday
123
+ date_factory(Time.now - (3600*24))
124
+ end
125
+
126
+ def tomorrow
127
+ date_factory(Time.now + (3600*24))
128
+ end
129
+
130
+ def in_a_week
131
+ date_factory(Time.now + (3600*24*7))
132
+ end
133
+ alias next_week in_a_week
134
+
135
+ def a_week_ago
136
+ date_factory(Time.now - (3600*24*7))
137
+ end
138
+
139
+ def next_monday
140
+ date_factory(Time.at(Time.now+(8-Time.now.wday)*24*3600))
141
+ end
142
+
143
+ # Formats a date string Sakai-style.
144
+ # Useful for verifying creation dates and such.
145
+ #
146
+ # @param time_object [Time] the moment that you want converted to the string
147
+ # @returns [String] a date formatted to look like this: Jun 8, 2012 12:02 pm
148
+ #
149
+ def make_date(time_object)
150
+ month = time_object.strftime("%b ")
151
+ day = time_object.strftime("%-d")
152
+ year = time_object.strftime(", %Y ")
153
+ mins = time_object.strftime(":%M %P")
154
+ hour = time_object.strftime("%l").to_i
155
+ return month + day + year + hour.to_s + mins
156
+ end
157
+
158
+ end
@@ -0,0 +1,76 @@
1
+ # Copyright 2012-2014 The rSmart Group, Inc.
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # This module provides methods that instantiate the page and data classes.
16
+ module Foundry
17
+
18
+ # Using the page_url defined in the provided page_class,
19
+ # this method will enter that url into the browser's address bar, then run the block of
20
+ # code that you specify.
21
+ # @param page_class [Class] the name of the page class that you want to instantiate
22
+ # @param &block [C] this is the block of code that you want to run while on the given page
23
+ #
24
+ def visit page_class, &block
25
+ on page_class, true, &block
26
+ end
27
+
28
+ # Instantiates the supplied page class, then runs the supplied block of code. Use this
29
+ # method when you are already on the site page you want to interact with.
30
+ # @param page_class [Class] the name of the page class that you want to instantiate
31
+ # @param visit [TrueClass, FalseClass] Essentially you will never have to specify this explicitly
32
+ # @param &block [C] this is the block of code that you want to run while on the given page
33
+ #
34
+ def on page_class, visit=false, &block
35
+ $current_page = page_class.new @browser, visit
36
+ block.call $current_page if block
37
+ $current_page
38
+ end
39
+ alias_method :on_page, :on
40
+
41
+ # Use this for making a data object in your test steps
42
+ #
43
+ # @param data_object_class [Class] The name of the class you want to use to build a data object for testing
44
+ # @param opts [Hash] The list of attributes you want to give to your data object
45
+ #
46
+ def make data_object_class, opts={}
47
+ data_object_class.new @browser, opts
48
+ end
49
+
50
+ # An extension of the #make method that simplifies and improves
51
+ # the readability of your "create" step definitions by
52
+ # combining the make with the create. Of course, this
53
+ # requires that your data object classes properly follow the design
54
+ # pattern and have a #create method available.
55
+ #
56
+ def create data_object_class, opts={}
57
+ data_object = make data_object_class, opts
58
+ data_object.create
59
+ data_object
60
+ end
61
+
62
+ # A helper method that takes a block of code and waits until it resolves to true.
63
+ # Useful when you need to wait for something to be on a page that's a little more
64
+ # involved than a simple element (for those, you should use the #expected_element
65
+ # method found in the PageFactory class)
66
+ # @param timeout [Fixnum] Defaults to 30 seconds
67
+ # @param message [String] The text thrown if the timeout is reached
68
+ #
69
+ # @example
70
+ # page.wait_until { |b| b.processing_message=="Done" }
71
+ #
72
+ def wait_until(timeout=30, message=nil, &block)
73
+ Object::Watir::Wait.until(timeout, message, &block)
74
+ end
75
+
76
+ end
@@ -0,0 +1,224 @@
1
+ # Copyright 2012-2014 The rSmart Group, Inc.
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # We are extending Watir's element methods here with the #fit method,
16
+ # which can be used with text fields, select lists, radio buttons,
17
+ # and checkboxes.
18
+ #
19
+ # The purpose of +#fit+ is to allow the creation, in your Data Object classes,
20
+ # of a minimal number of +#edit+ methods (ideally only one) written as
21
+ # concisely as possible.
22
+ #
23
+ # Without the +#fit+ method, you would either have to write separate edit
24
+ # methods for every possible field you want to edit, or else your
25
+ # edit method would have to contain lots of repetitive conditional code
26
+ # to prevent making inadvertent updates to those fields that don't need it.
27
+ #
28
+ # Proper use of the +#fit+ method requires following a particular coding
29
+ # pattern, however:
30
+ #
31
+ # * In your Page Classes, define your text field, select list, radio button, and
32
+ # checkbox elements directly. Do not define +#select+, +#set+ and/or +#clear+
33
+ # actions there.
34
+ # * Your data object's instance variables for radio buttons and checkboxes, when
35
+ # not +nil+, should have the values of +:set+ or +:clear+. If they *need* to be
36
+ # something else, then define a Hash transform method to easily convert the
37
+ # custom values back to +:set+ or +:clear+, then pass that transform to the +#fit+ method.
38
+ # * Always remember to end your +#edit+ methods with the +#set_options()+
39
+ # method (a.k.a. +#update_options+), from the DataFactory module. It
40
+ # automatically takes care of updating your data object's instance variables
41
+ # with any new values.
42
+ #
43
+ # ==Example
44
+ #
45
+ # Let's take a look at how the proper use of +#fit+ in your code can significantly
46
+ # clean things up, using a checkbox field for our example. Remember that +#fit+
47
+ # works with radio buttons, text fields, and select lists, too.
48
+ #
49
+ # First, here's some code written without using +#fit+, and using
50
+ # actions for the checkbox page objects, and a Data Object
51
+ # instance variable, +@option+, that is either "YES" or "NO"...
52
+ #
53
+ # class MyPage < BasePage
54
+ # # ...
55
+ # action(:check_checkbox) { |b| b.checkbox(id: "checkbox").set }
56
+ # action(:clear_checkbox) { |b| b.checkbox(id: "checkbox").clear }
57
+ # # ...
58
+ # end
59
+ #
60
+ # class DataObject
61
+ # # ...
62
+ # def edit opts={}
63
+ # # ...
64
+ # if opts[:option] != @option
65
+ # on MyPage do |page|
66
+ # if opts[:option] == "NO"
67
+ # page.clear_checkbox
68
+ # else
69
+ # page.check_checkbox
70
+ # end
71
+ # end
72
+ # @option = opts[:option]
73
+ # end
74
+ # # ...
75
+ # end
76
+ # # ...
77
+ # end
78
+ #
79
+ # That's just nasty! Your Page Class has two element definitions that are nearly identical.
80
+ # And the nested conditional in the Data Object's #edit method hurts the eyes!
81
+ #
82
+ # Now, let's take that same code, but this time use the +#fit+ method. We'll assume that
83
+ # the data object's +@option+ instance variable will be +:set+, +:clear+, or +nil+, and
84
+ # end the +#edit+ with the DataFactory's +#set_options+ helper method...
85
+ #
86
+ # class MyPage < BasePage
87
+ # # ...
88
+ # element(:checkbox) { |b| b.checkbox(id: "checkbox") }
89
+ # # ...
90
+ # end
91
+ #
92
+ # class DataObject
93
+ # # ...
94
+ # def edit opts={}
95
+ # # ...
96
+ # on MyPage do |page|
97
+ # # ...
98
+ # page.checkbox.fit opts[:option]
99
+ # # ...
100
+ # end
101
+ # # ...
102
+ # update_options opts
103
+ # end
104
+ # # ...
105
+ # end
106
+ #
107
+ # Much cleaner!
108
+ #
109
+ # The +#fit+ method is designed to allow for other values than just +:set+ or +:clear+. It will support
110
+ # 'Yes', 'No', 'on', 'off' (and it's case insensitive), +true+, and +false+, as well. This way you don't have to worry so much
111
+ # about making methods that transform from one type to another.
112
+ #
113
+ module Watir
114
+
115
+ module Container
116
+ def frm
117
+ case
118
+ when div(id: 'embedded').exists?
119
+ iframe(id: /easyXDM_default\d+_provider/).iframe(id: 'iframeportlet')
120
+ when div(id: 'Uif-ViewContentWrapper').exists?
121
+ iframe(class: 'uif-iFrame uif-boxLayoutVerticalItem pull-left clearfix')
122
+ else
123
+ self
124
+ end
125
+ end
126
+ end
127
+
128
+ class CheckBox
129
+ def fit(arg)
130
+ setting = TestFactory.binary_transform(arg)
131
+ self.send(setting) unless setting==nil
132
+ end
133
+ end
134
+
135
+ class Radio
136
+ def fit(arg)
137
+ setting = TestFactory.binary_transform(arg)
138
+ self.set if setting==:set
139
+ end
140
+ end
141
+
142
+ module UserEditable
143
+
144
+ # Extends Watir's methods.
145
+ # Use when the argument you are passing to a text field
146
+ # may be nil, in which case you don't
147
+ # want to do anything with the page element.
148
+ #
149
+ def fit(args)
150
+ unless args==nil
151
+ assert_exists
152
+ assert_writable
153
+
154
+ @element.clear
155
+ @element.send_keys(args)
156
+ end
157
+ end
158
+ end
159
+
160
+ class Select
161
+
162
+ # Extends Watir's methods.
163
+ # Use when the argument you are passing to a text field
164
+ # may be nil, in which case you don't
165
+ # want to do anything with the page element.
166
+ # @example
167
+ # page.select_list.fit @my_selection
168
+ #
169
+ def fit(str_or_rx)
170
+ select_by :text, str_or_rx unless str_or_rx==nil
171
+ end
172
+
173
+ # Allows you to select a specific item in a
174
+ # select list, or, if desired, it will pick an item from
175
+ # the list at random.
176
+ #
177
+ # If you pass this method the string '::random::' then
178
+ # it will select an item at random from the select
179
+ # list and, assuming what you passed it was a class instance
180
+ # variable, it will be updated to contain the
181
+ # selected value (hence the ! in the method name).
182
+ # Note that this method will be slow with large selection lists.
183
+ #
184
+ # @example
185
+ # @my_selection='::random::'
186
+ # page.select_list.pick! @my_selection
187
+ # puts @my_selection # => <Value of randomly selected item from list>
188
+ #
189
+ def pick!(item)
190
+ if item=='::random::'
191
+ item.replace(select_at_random)
192
+ else
193
+ fit item
194
+ end
195
+ end
196
+
197
+ # Same as #pick!, except it does not change the
198
+ # value of 'item'
199
+ #
200
+ def pick(item)
201
+ if item=='::random::'
202
+ select_at_random
203
+ else
204
+ fit item
205
+ end
206
+ end
207
+
208
+ private
209
+
210
+ def select_at_random
211
+ ar = options.map(&:text)
212
+ # Must break out of this method if the select list has nothing to select...
213
+ return '::random::' if ar.size==1 && (ar[0]=~/^select(.?)$/i || ar[0]=='')
214
+ sel = ar.sample
215
+ while sel=~/^select(.?)$/i || sel==''
216
+ sel = ar.sample
217
+ end
218
+ select sel
219
+ sel
220
+ end
221
+
222
+ end
223
+
224
+ end