iu-test-factory 0.5.4.2

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: 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