kuali-test-factory 0.5.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/COPYING +68 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +33 -0
- data/LICENSE +68 -0
- data/README.md +241 -0
- data/lib/test-factory.rb +18 -0
- data/lib/test-factory/collections_factory.rb +67 -0
- data/lib/test-factory/core_ext.rb +133 -0
- data/lib/test-factory/data_factory.rb +334 -0
- data/lib/test-factory/date_factory.rb +158 -0
- data/lib/test-factory/foundry.rb +76 -0
- data/lib/test-factory/gem_ext.rb +224 -0
- data/lib/test-factory/page_factory.rb +238 -0
- data/lib/test-factory/string_factory.rb +167 -0
- data/lib/test-factory/test_factory.rb +16 -0
- data/test-factory.gemspec +13 -0
- metadata +78 -0
@@ -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
|