iu-test-factory 0.5.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: timeout, message: message, &block)
74
+ end
75
+
76
+ end
@@ -0,0 +1,211 @@
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
+ class CheckBox
116
+ def fit(arg)
117
+ setting = TestFactory.binary_transform(arg)
118
+ self.send(setting) unless setting==nil
119
+ end
120
+ end
121
+
122
+ class Radio
123
+ def fit(arg)
124
+ setting = TestFactory.binary_transform(arg)
125
+ self.set if setting==:set
126
+ end
127
+ end
128
+
129
+ module UserEditable
130
+
131
+ # Extends Watir's methods.
132
+ # Use when the argument you are passing to a text field
133
+ # may be nil, in which case you don't
134
+ # want to do anything with the page element.
135
+ #
136
+ def fit(args)
137
+ unless args==nil
138
+ assert_exists
139
+ assert_writable
140
+
141
+ @element.clear
142
+ @element.send_keys(args)
143
+ end
144
+ end
145
+ end
146
+
147
+ class Select
148
+
149
+ # Extends Watir's methods.
150
+ # Use when the argument you are passing to a text field
151
+ # may be nil, in which case you don't
152
+ # want to do anything with the page element.
153
+ # @example
154
+ # page.select_list.fit @my_selection
155
+ #
156
+ def fit(str_or_rx)
157
+ select_by :text, str_or_rx unless str_or_rx==nil
158
+ end
159
+
160
+ # Allows you to select a specific item in a
161
+ # select list, or, if desired, it will pick an item from
162
+ # the list at random.
163
+ #
164
+ # If you pass this method the string '::random::' then
165
+ # it will select an item at random from the select
166
+ # list and, assuming what you passed it was a class instance
167
+ # variable, it will be updated to contain the
168
+ # selected value (hence the ! in the method name).
169
+ # Note that this method will be slow with large selection lists.
170
+ #
171
+ # @example
172
+ # @my_selection='::random::'
173
+ # page.select_list.pick! @my_selection
174
+ # puts @my_selection # => <Value of randomly selected item from list>
175
+ #
176
+ def pick!(item)
177
+ if item=='::random::'
178
+ item.replace(select_at_random)
179
+ else
180
+ fit item
181
+ end
182
+ end
183
+
184
+ # Same as #pick!, except it does not change the
185
+ # value of 'item'
186
+ #
187
+ def pick(item)
188
+ if item=='::random::'
189
+ select_at_random
190
+ else
191
+ fit item
192
+ end
193
+ end
194
+
195
+ private
196
+
197
+ def select_at_random
198
+ ar = options.map(&:text)
199
+ # Must break out of this method if the select list has nothing to select...
200
+ return '::random::' if ar.size==1 && (ar[0]=~/^select(.?)$/i || ar[0]=='')
201
+ sel = ar.sample
202
+ while sel=~/^select(.?)$/i || sel==''
203
+ sel = ar.sample
204
+ end
205
+ select sel
206
+ sel
207
+ end
208
+
209
+ end
210
+
211
+ end
@@ -0,0 +1,219 @@
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
+ # The PageFactory class provides a set of methods that allow the rapid creation of page element definitions--known
16
+ # colloquially as "page objects". These elements are defined using Watir syntax. Please see www.watir.com if you are
17
+ # not familiar with Watir.
18
+ #
19
+ class PageFactory
20
+
21
+ # As the PageFactory will be the superclass for all your page classes, having this initialize
22
+ # method here means it's only written once.
23
+ #
24
+ def initialize browser, visit = false
25
+ @browser = browser
26
+ goto if visit
27
+ expected_element if respond_to? :expected_element
28
+ has_expected_title? if respond_to? :has_expected_title?
29
+ end
30
+
31
+ # Catches any "missing" methods and passes them to the browser object--which means
32
+ # that Watir will take care of parsing them, so the assumption is that the method being
33
+ # passed is a valid method for the browser object.
34
+ #
35
+ def method_missing sym, *args, &block
36
+ @browser.send sym, *args, &block
37
+ end
38
+
39
+ class << self
40
+
41
+ # Define this in a page class and when you use the "visit" method to instantiate the class
42
+ # it will enter the URL in the browser's address bar.
43
+ #
44
+ def page_url url
45
+ define_method 'goto' do
46
+ @browser.goto url
47
+ end
48
+ end
49
+
50
+ # Define this in a page class and when that class is instantiated it will wait until that
51
+ # element appears on the page before continuing with the script.
52
+ # @param element_name [Symbol] The method name of the element that must be present on the page
53
+ #
54
+ def expected_element element_name, timeout=30
55
+ define_method 'expected_element' do
56
+ self.send(element_name).wait_until_present timeout: timeout
57
+ end
58
+ end
59
+
60
+ # Define this in a page class and when the class is instantiated it will verify that
61
+ # the browser's title matches the expected title. If there isn't a match, it raises an
62
+ # error and halts the script.
63
+ # @param expected_title [String] The exact text that is expected to appear in the Browser title when the page loads
64
+ #
65
+ def expected_title expected_title
66
+ define_method 'has_expected_title?' do
67
+ has_expected_title = expected_title.kind_of?(Regexp) ? expected_title =~ @browser.title : expected_title == @browser.title
68
+ raise "Expected title '#{expected_title}' instead of '#{@browser.title}'" unless has_expected_title
69
+ end
70
+ end
71
+
72
+ # The basic building block for defining and interacting with
73
+ # elements on a web page. # Use in conjunction with
74
+ # Watir to define all elements on a given page that are important to validate.
75
+ #
76
+ # Methods that take one or more parameters can be built with this as well.
77
+ #
78
+ # @example
79
+ # element(:title) { |b| b.text_field(:id=>"title-id") }
80
+ # value(:page_header) { |b| b.h3(:class=>"page_header").text }
81
+ # action(:continue) { |b| b.frm.button(:value=>"Continue").click } => Creates a #continue method that clicks the Continue button
82
+ # p_element(:select_style) { |stylename, b| b.div(:text=>/#{Regexp.escape(stylename)}/).link(:text=>"Select").click } => #select_style(stylename)
83
+ #
84
+ def element name, &block
85
+ raise "#{name} is being defined twice in #{self}!" if self.instance_methods.include?(name.to_sym)
86
+ define_method name.to_s do |*thing|
87
+ Proc.new(&block).call *thing, self
88
+ end
89
+ end
90
+ alias_method :action, :element
91
+ alias_method :value, :element
92
+ alias_method :p_element, :element
93
+ alias_method :p_action, :element
94
+ alias_method :p_value, :element
95
+
96
+ # Use this for links that are safe to define by their text string.
97
+ # This method will return two methods for interacting with the link:
98
+ # one that refers to the link itself, and one that clicks on it.
99
+ # Since it's assumed that the most common thing done with a link is to click it,
100
+ # the method for clicking it will be named according to the text of the link,
101
+ # and the method for the link itself will have "_link" appended to it. Any special
102
+ # characters are stripped from the string. Capital letters are made lower case.
103
+ # And spaces and dashes are converted to underscores.
104
+ #
105
+ # @example
106
+ # link("Click Me For Fun!") => Creates the methods #click_me_for_fun and #click_me_for_fun_link
107
+ #
108
+ # The last parameter in the method is optional. Use it when
109
+ # you need the method name to be different from the text of
110
+ # the link--for example if the link text is something unhelpful,
111
+ # like "here", or else the link text gets updated (e.g., what was
112
+ # "Log In" is now "Sign In", instead) and you don't
113
+ # want to have to go through all your data objects and step
114
+ # definitions to update them to the new method name.
115
+ #
116
+ # @example
117
+ # link("Click Me For Fun!", :click_me) => Creates the methods #click_me and #click_me_link
118
+ #
119
+ def link link_text, *alias_name
120
+ elementize(:link, link_text, *alias_name)
121
+ end
122
+
123
+ # Use this for buttons that are safe to define by their value attribute.
124
+ # This method will return two methods for interacting with the button:
125
+ # one that refers to the button itself, and one that clicks on it.
126
+ # Since it's assumed that the most common thing done with a button is to click it,
127
+ # the method for clicking it will be named according to the value of the button,
128
+ # and the method for the button itself will have "_button" appended to it. Any special
129
+ # characters are stripped from the string. Capital letters are made lower case.
130
+ # And spaces and dashes are converted to underscores.
131
+ # @param button_text [String] The contents of the button's value tag in the HTML
132
+ #
133
+ # @example
134
+ # button("Click Me For Fun!") => Creates the methods #click_me_for_fun and #click_me_for_fun_button
135
+ #
136
+ # The last parameter in the method is optional. Use it when
137
+ # you need the method name to be different from the text of
138
+ # the button--for example if the button text is unhelpful, like "Go", or else
139
+ # it changes (e.g., from "Update" to "Edit") and you don't
140
+ # want to have to go through all your data objects and step
141
+ # definitions to update them to the new method name.
142
+ #
143
+ # @example
144
+ # link("Click Me For Fun!", :click_me) => Creates the methods #click_me and #click_me_link
145
+ #
146
+ def button button_text, *alias_name
147
+ elementize(:button, button_text, *alias_name)
148
+ end
149
+
150
+ # TestFactory doesn't allow defining a method in a child class
151
+ # with the same name as one already defined in a parent class.
152
+ # The thinking here is: "Out of sight, out of mind." Meaning:
153
+ # you or a team mate might not know or have forgotten that a given
154
+ # element is already defined in a parent class, and so define it
155
+ # again. TestFactory's restriction is there to help prevent this.
156
+ #
157
+ # However, in some cases you may have a child page class with a
158
+ # special circumstance, where the parent class's version of the
159
+ # method really doesn't apply, and you want to use the same method
160
+ # name in this child class because, really, no other method name
161
+ # would fit quite as well.
162
+ #
163
+ # The #undefine method is for those rare cases. Note: If you start
164
+ # using this method a lot then you should consider that a sign
165
+ # that perhaps you're putting too many method definitions into
166
+ # parent page classes.
167
+ #
168
+ # @example
169
+ # undefine :status, :doc_id => Undefines the specified methods in the current class
170
+ #
171
+ def undefine *methods
172
+ methods.each{ |m| undef_method m }
173
+ end
174
+
175
+ def inherited klass
176
+ klass.instance_eval {
177
+
178
+ # Creates a method, #wait_for_ajax, usable in your Page Classes, that executes
179
+ # the 'jQuery.active' Javascript snippet each second until timeout.
180
+ #
181
+ # If timeout is exceeded, raises Watir::Wait::TimeoutError exception. The returned error
182
+ # message is customizable.
183
+ #
184
+ define_method 'wait_for_ajax' do |timeout=10, message|
185
+ timeout.times do
186
+ sleep 0.3
187
+ return true if @browser.execute_script('return jQuery.active').to_i == 0
188
+ sleep 0.7
189
+ end
190
+ raise Watir::Wait::TimeoutError, "Ajax calls continued beyond #{timeout} seconds. #{message}"
191
+ end
192
+
193
+ }
194
+ end
195
+
196
+ private
197
+ # A helper method that converts the passed string into snake case. See the StringFactory
198
+ # module for more info.
199
+ #
200
+ def damballa text
201
+ StringFactory.damballa(text)
202
+ end
203
+
204
+ def elementize type, text, *alias_name
205
+ hash={:link=>:text, :button=>:value}
206
+ if alias_name.empty?
207
+ el_name=damballa("#{text}_#{type}")
208
+ act_name=damballa(text)
209
+ else
210
+ el_name="#{alias_name[0]}_#{type}".to_sym
211
+ act_name=alias_name[0]
212
+ end
213
+ element(el_name) { |b| b.send(type, hash[type]=>text) }
214
+ action(act_name) { |b| b.send(type, hash[type]=>text).click }
215
+ end
216
+
217
+ end
218
+
219
+ end # PageFactory