aslakhellesoy-webrat 0.3.2.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.
Files changed (66) hide show
  1. data/History.txt +193 -0
  2. data/MIT-LICENSE.txt +19 -0
  3. data/README.rdoc +81 -0
  4. data/Rakefile +104 -0
  5. data/install.rb +1 -0
  6. data/lib/webrat.rb +34 -0
  7. data/lib/webrat/core.rb +14 -0
  8. data/lib/webrat/core/configuration.rb +44 -0
  9. data/lib/webrat/core/elements/area.rb +31 -0
  10. data/lib/webrat/core/elements/element.rb +33 -0
  11. data/lib/webrat/core/elements/field.rb +386 -0
  12. data/lib/webrat/core/elements/form.rb +103 -0
  13. data/lib/webrat/core/elements/label.rb +31 -0
  14. data/lib/webrat/core/elements/link.rb +90 -0
  15. data/lib/webrat/core/elements/select_option.rb +35 -0
  16. data/lib/webrat/core/locators.rb +20 -0
  17. data/lib/webrat/core/locators/area_locator.rb +38 -0
  18. data/lib/webrat/core/locators/button_locator.rb +54 -0
  19. data/lib/webrat/core/locators/field_by_id_locator.rb +37 -0
  20. data/lib/webrat/core/locators/field_labeled_locator.rb +50 -0
  21. data/lib/webrat/core/locators/field_locator.rb +25 -0
  22. data/lib/webrat/core/locators/field_named_locator.rb +41 -0
  23. data/lib/webrat/core/locators/form_locator.rb +19 -0
  24. data/lib/webrat/core/locators/label_locator.rb +34 -0
  25. data/lib/webrat/core/locators/link_locator.rb +66 -0
  26. data/lib/webrat/core/locators/locator.rb +20 -0
  27. data/lib/webrat/core/locators/select_option_locator.rb +59 -0
  28. data/lib/webrat/core/logging.rb +21 -0
  29. data/lib/webrat/core/matchers.rb +4 -0
  30. data/lib/webrat/core/matchers/have_content.rb +55 -0
  31. data/lib/webrat/core/matchers/have_selector.rb +37 -0
  32. data/lib/webrat/core/matchers/have_tag.rb +57 -0
  33. data/lib/webrat/core/matchers/have_xpath.rb +83 -0
  34. data/lib/webrat/core/methods.rb +60 -0
  35. data/lib/webrat/core/mime.rb +29 -0
  36. data/lib/webrat/core/save_and_open_page.rb +50 -0
  37. data/lib/webrat/core/scope.rb +330 -0
  38. data/lib/webrat/core/session.rb +218 -0
  39. data/lib/webrat/core/xml.rb +115 -0
  40. data/lib/webrat/core/xml/hpricot.rb +19 -0
  41. data/lib/webrat/core/xml/nokogiri.rb +76 -0
  42. data/lib/webrat/core/xml/rexml.rb +24 -0
  43. data/lib/webrat/core_extensions/blank.rb +58 -0
  44. data/lib/webrat/core_extensions/deprecate.rb +8 -0
  45. data/lib/webrat/core_extensions/detect_mapped.rb +12 -0
  46. data/lib/webrat/core_extensions/hash_with_indifferent_access.rb +131 -0
  47. data/lib/webrat/core_extensions/meta_class.rb +6 -0
  48. data/lib/webrat/core_extensions/nil_to_param.rb +5 -0
  49. data/lib/webrat/mechanize.rb +43 -0
  50. data/lib/webrat/merb.rb +77 -0
  51. data/lib/webrat/rack.rb +26 -0
  52. data/lib/webrat/rails.rb +96 -0
  53. data/lib/webrat/rails/redirect_actions.rb +18 -0
  54. data/lib/webrat/rspec-rails.rb +13 -0
  55. data/lib/webrat/selenium.rb +107 -0
  56. data/lib/webrat/selenium/location_strategy_javascript/button.js +12 -0
  57. data/lib/webrat/selenium/location_strategy_javascript/label.js +16 -0
  58. data/lib/webrat/selenium/location_strategy_javascript/webrat.js +5 -0
  59. data/lib/webrat/selenium/location_strategy_javascript/webratlink.js +9 -0
  60. data/lib/webrat/selenium/location_strategy_javascript/webratlinkwithin.js +15 -0
  61. data/lib/webrat/selenium/location_strategy_javascript/webratselectwithoption.js +5 -0
  62. data/lib/webrat/selenium/selenium_extensions.js +6 -0
  63. data/lib/webrat/selenium/selenium_session.rb +230 -0
  64. data/lib/webrat/sinatra.rb +21 -0
  65. data/vendor/selenium-server.jar +0 -0
  66. metadata +136 -0
@@ -0,0 +1,83 @@
1
+ require "webrat/core/xml/nokogiri"
2
+ require "webrat/core/xml/rexml"
3
+
4
+ module Webrat
5
+ module Matchers
6
+
7
+ class HaveXpath #:nodoc:
8
+ def initialize(expected, &block)
9
+ @expected = expected
10
+ @block = block
11
+ end
12
+
13
+ def matches?(stringlike)
14
+ if Webrat.configuration.parse_with_nokogiri?
15
+ matches_nokogiri?(stringlike)
16
+ else
17
+ matches_rexml?(stringlike)
18
+ end
19
+ end
20
+
21
+ def matches_rexml?(stringlike)
22
+ if REXML::Node === stringlike || Array === stringlike
23
+ @query = query.map { |q| q.gsub(%r'//', './') }
24
+ else
25
+ @query = query
26
+ end
27
+
28
+ @document = Webrat.rexml_document(stringlike)
29
+
30
+ matched = @query.map do |q|
31
+ if @document.is_a?(Array)
32
+ @document.map { |d| REXML::XPath.match(d, q) }
33
+ else
34
+ REXML::XPath.match(@document, q)
35
+ end
36
+ end.flatten.compact
37
+
38
+ matched.any? && (!@block || @block.call(matched))
39
+ end
40
+
41
+ def matches_nokogiri?(stringlike)
42
+ if Nokogiri::XML::NodeSet === stringlike
43
+ @query = query.map { |q| q.gsub(%r'//', './') }
44
+ else
45
+ @query = query
46
+ end
47
+
48
+ @document = Webrat::XML.document(stringlike)
49
+ matched = @document.xpath(*@query)
50
+ matched.any? && (!@block || @block.call(matched))
51
+ end
52
+
53
+ def query
54
+ [@expected].flatten.compact
55
+ end
56
+
57
+ # ==== Returns
58
+ # String:: The failure message.
59
+ def failure_message
60
+ "expected following text to match xpath #{@expected}:\n#{@document}"
61
+ end
62
+
63
+ # ==== Returns
64
+ # String:: The failure message to be displayed in negative matches.
65
+ def negative_failure_message
66
+ "expected following text to not match xpath #{@expected}:\n#{@document}"
67
+ end
68
+ end
69
+
70
+ # Matches HTML content against an XPath query
71
+ #
72
+ # ==== Parameters
73
+ # expected<String>:: The XPath query to look for.
74
+ #
75
+ # ==== Returns
76
+ # HaveXpath:: A new have xpath matcher.
77
+ def have_xpath(expected, &block)
78
+ HaveXpath.new(expected, &block)
79
+ end
80
+ alias_method :match_xpath, :have_xpath
81
+
82
+ end
83
+ end
@@ -0,0 +1,60 @@
1
+ module Webrat
2
+ module Methods #:nodoc:
3
+
4
+ def self.delegate_to_session(*meths)
5
+ meths.each do |meth|
6
+ self.class_eval <<-RUBY
7
+ def #{meth}(*args, &blk)
8
+ webrat_session.#{meth}(*args, &blk)
9
+ end
10
+ RUBY
11
+ end
12
+ end
13
+
14
+ def webrat
15
+ webrat_session
16
+ end
17
+
18
+ def webrat_session
19
+ @_webrat_session ||= ::Webrat.session_class.new(self)
20
+ end
21
+
22
+ # all of these methods delegate to the @session, which should
23
+ # be created transparently.
24
+ #
25
+ # Note that when using Webrat, #request also uses @session, so
26
+ # that #request and webrat native functions behave interchangably
27
+
28
+ delegate_to_session \
29
+ :visits, :visit,
30
+ :within,
31
+ :header, :http_accept, :basic_auth,
32
+ :save_and_open_page,
33
+ :fills_in, :fill_in,
34
+ :checks, :check,
35
+ :unchecks, :uncheck,
36
+ :chooses, :choose,
37
+ :selects, :select,
38
+ :attaches_file, :attach_file,
39
+ :current_page,
40
+ :current_url,
41
+ :clicks_link, :click_link,
42
+ :clicks_area, :click_area,
43
+ :clicks_button, :click_button,
44
+ :reload, :reloads,
45
+ :clicks_link_within, :click_link_within,
46
+ :field_labeled,
47
+ :select_option,
48
+ :set_hidden_field, :submit_form,
49
+ :request_page, :current_dom,
50
+ :selects_date, :selects_time, :selects_datetime,
51
+ :select_date, :select_time, :select_datetime,
52
+ :field_by_xpath,
53
+ :field_with_id,
54
+ :selenium,
55
+ :simulate, :automate
56
+
57
+
58
+
59
+ end
60
+ end
@@ -0,0 +1,29 @@
1
+ module Webrat #:nodoc:
2
+ module MIME #:nodoc:
3
+
4
+ def self.mime_type(string_or_symbol) #:nodoc:
5
+ if string_or_symbol.is_a?(String)
6
+ string_or_symbol
7
+ else
8
+ case string_or_symbol
9
+ when :text then "text/plain"
10
+ when :html then "text/html"
11
+ when :js then "text/javascript"
12
+ when :css then "text/css"
13
+ when :ics then "text/calendar"
14
+ when :csv then "text/csv"
15
+ when :xml then "application/xml"
16
+ when :rss then "application/rss+xml"
17
+ when :atom then "application/atom+xml"
18
+ when :yaml then "application/x-yaml"
19
+ when :multipart_form then "multipart/form-data"
20
+ when :url_encoded_form then "application/x-www-form-urlencoded"
21
+ when :json then "application/json"
22
+ else
23
+ raise ArgumentError.new("Invalid Mime type: #{string_or_symbol.inspect}")
24
+ end
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,50 @@
1
+ module Webrat
2
+ module SaveAndOpenPage
3
+ # Saves the page out to RAILS_ROOT/tmp/ and opens it in the default
4
+ # web browser if on OS X. Useful for debugging.
5
+ #
6
+ # Example:
7
+ # save_and_open_page
8
+ def save_and_open_page
9
+ return unless File.exist?(saved_page_dir)
10
+
11
+ filename = "#{saved_page_dir}/webrat-#{Time.now.to_i}.html"
12
+
13
+ File.open(filename, "w") do |f|
14
+ f.write rewrite_css_and_image_references(response_body)
15
+ end
16
+
17
+ open_in_browser(filename)
18
+ end
19
+
20
+ def open_in_browser(path) # :nodoc
21
+ platform = ruby_platform
22
+ if platform =~ /cygwin/ || platform =~ /win32/
23
+ `rundll32 url.dll,FileProtocolHandler #{path.gsub("/", "\\\\")}`
24
+ elsif platform =~ /darwin/
25
+ `open #{path}`
26
+ end
27
+ end
28
+
29
+ def rewrite_css_and_image_references(response_html) # :nodoc:
30
+ return response_html unless doc_root
31
+ response_html.gsub(/"\/(stylesheets|images)/, doc_root + '/\1')
32
+ end
33
+
34
+ def saved_page_dir #:nodoc:
35
+ File.expand_path(".")
36
+ end
37
+
38
+ def doc_root #:nodoc:
39
+ nil
40
+ end
41
+
42
+ private
43
+
44
+ # accessor for testing
45
+ def ruby_platform
46
+ RUBY_PLATFORM
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,330 @@
1
+ require "webrat/core/elements/form"
2
+ require "webrat/core/locators"
3
+ require "webrat/core_extensions/deprecate"
4
+
5
+ module Webrat
6
+ # An HTML element (link, button, field, etc.) that Webrat expected was not found on the page
7
+ class NotFoundError < WebratError
8
+ end
9
+
10
+ class Scope
11
+ include Logging
12
+ include Locators
13
+
14
+ def self.from_page(session, response, response_body) #:nodoc:
15
+ new(session) do
16
+ @response = response
17
+ @response_body = response_body
18
+ end
19
+ end
20
+
21
+ def self.from_scope(session, scope, selector) #:nodoc:
22
+ new(session) do
23
+ @scope = scope
24
+ @selector = selector
25
+ end
26
+ end
27
+
28
+ attr_reader :session
29
+
30
+ def initialize(session, &block) #:nodoc:
31
+ @session = session
32
+ instance_eval(&block) if block_given?
33
+ end
34
+
35
+ # Verifies an input field or textarea exists on the current page, and stores a value for
36
+ # it which will be sent when the form is submitted.
37
+ #
38
+ # Examples:
39
+ # fill_in "Email", :with => "user@example.com"
40
+ # fill_in "user[email]", :with => "user@example.com"
41
+ #
42
+ # The field value is required, and must be specified in <tt>options[:with]</tt>.
43
+ # <tt>field</tt> can be either the value of a name attribute (i.e. <tt>user[email]</tt>)
44
+ # or the text inside a <tt><label></tt> element that points at the <tt><input></tt> field.
45
+ def fill_in(field_locator, options = {})
46
+ field = locate_field(field_locator, TextField, TextareaField, PasswordField)
47
+ field.raise_error_if_disabled
48
+ field.set(options[:with])
49
+ end
50
+
51
+ webrat_deprecate :fills_in, :fill_in
52
+
53
+ def set_hidden_field(field_locator, options = {})
54
+ field = locate_field(field_locator, HiddenField)
55
+ field.set(options[:to])
56
+ end
57
+
58
+ # Verifies that an input checkbox exists on the current page and marks it
59
+ # as checked, so that the value will be submitted with the form.
60
+ #
61
+ # Example:
62
+ # check 'Remember Me'
63
+ def check(field_locator)
64
+ locate_field(field_locator, CheckboxField).check
65
+ end
66
+
67
+ webrat_deprecate :checks, :check
68
+
69
+ # Verifies that an input checkbox exists on the current page and marks it
70
+ # as unchecked, so that the value will not be submitted with the form.
71
+ #
72
+ # Example:
73
+ # uncheck 'Remember Me'
74
+ def uncheck(field_locator)
75
+ locate_field(field_locator, CheckboxField).uncheck
76
+ end
77
+
78
+ webrat_deprecate :unchecks, :uncheck
79
+
80
+ # Verifies that an input radio button exists on the current page and marks it
81
+ # as checked, so that the value will be submitted with the form.
82
+ #
83
+ # Example:
84
+ # choose 'First Option'
85
+ def choose(field_locator)
86
+ locate_field(field_locator, RadioField).choose
87
+ end
88
+
89
+ webrat_deprecate :chooses, :choose
90
+
91
+ # Verifies that a an option element exists on the current page with the specified
92
+ # text. You can optionally restrict the search to a specific select list by
93
+ # assigning <tt>options[:from]</tt> the value of the select list's name or
94
+ # a label. Stores the option's value to be sent when the form is submitted.
95
+ #
96
+ # Examples:
97
+ # select "January"
98
+ # select "February", :from => "event_month"
99
+ # select "February", :from => "Event Month"
100
+ def select(option_text, options = {})
101
+ select_option(option_text, options[:from]).choose
102
+ end
103
+
104
+ webrat_deprecate :selects, :select
105
+
106
+ DATE_TIME_SUFFIXES = {
107
+ :year => '1i',
108
+ :month => '2i',
109
+ :day => '3i',
110
+ :hour => '4i',
111
+ :minute => '5i'
112
+ }
113
+
114
+ # Verifies that date elements (year, month, day) exist on the current page
115
+ # with the specified values. You can optionally restrict the search to a specific
116
+ # date's elements by assigning <tt>options[:from]</tt> the value of the date's
117
+ # label. Selects all the date elements with date provided. The date provided may
118
+ # be a string or a Date/Time object.
119
+ #
120
+ # Rail's convention is used for detecting the date elements. All elements
121
+ # are assumed to have a shared prefix. You may also specify the prefix
122
+ # by assigning <tt>options[:id_prefix]</tt>.
123
+ #
124
+ # Examples:
125
+ # select_date "January 23, 2004"
126
+ # select_date "April 26, 1982", :from => "Birthday"
127
+ # select_date Date.parse("December 25, 2000"), :from => "Event"
128
+ # select_date "April 26, 1982", :id_prefix => 'birthday'
129
+ def select_date(date_to_select, options ={})
130
+ date = date_to_select.is_a?(Date) || date_to_select.is_a?(Time) ?
131
+ date_to_select : Date.parse(date_to_select)
132
+
133
+ id_prefix = locate_id_prefix(options) do
134
+ year_field = FieldByIdLocator.new(@session, dom, /(.*?)_#{DATE_TIME_SUFFIXES[:year]}$/).locate
135
+ raise NotFoundError.new("No date fields were found") unless year_field && year_field.id =~ /(.*?)_1i/
136
+ $1
137
+ end
138
+
139
+ select date.year, :from => "#{id_prefix}_#{DATE_TIME_SUFFIXES[:year]}"
140
+ select date.strftime('%B'), :from => "#{id_prefix}_#{DATE_TIME_SUFFIXES[:month]}"
141
+ select date.day, :from => "#{id_prefix}_#{DATE_TIME_SUFFIXES[:day]}"
142
+ end
143
+
144
+ webrat_deprecate :selects_date, :select_date
145
+
146
+ # Verifies that time elements (hour, minute) exist on the current page
147
+ # with the specified values. You can optionally restrict the search to a specific
148
+ # time's elements by assigning <tt>options[:from]</tt> the value of the time's
149
+ # label. Selects all the time elements with date provided. The time provided may
150
+ # be a string or a Time object.
151
+ #
152
+ # Rail's convention is used for detecting the time elements. All elements are
153
+ # assumed to have a shared prefix. You may specify the prefix by assigning
154
+ # <tt>options[:id_prefix]</tt>.
155
+ #
156
+ # Note: Just like Rails' time_select helper this assumes the form is using
157
+ # 24 hour select boxes, and not 12 hours with AM/PM.
158
+ #
159
+ # Examples:
160
+ # select_time "9:30"
161
+ # select_date "3:30PM", :from => "Party Time"
162
+ # select_date Time.parse("10:00PM"), :from => "Event"
163
+ # select_date "10:30AM", :id_prefix => 'meeting'
164
+ def select_time(time_to_select, options ={})
165
+ time = time_to_select.is_a?(Time) ? time_to_select : Time.parse(time_to_select)
166
+
167
+ id_prefix = locate_id_prefix(options) do
168
+ hour_field = FieldByIdLocator.new(@session, dom, /(.*?)_#{DATE_TIME_SUFFIXES[:hour]}$/).locate
169
+ raise NotFoundError.new("No time fields were found") unless hour_field && hour_field.id =~ /(.*?)_4i/
170
+ $1
171
+ end
172
+
173
+ select time.hour.to_s.rjust(2,'0'), :from => "#{id_prefix}_#{DATE_TIME_SUFFIXES[:hour]}"
174
+ select time.min.to_s.rjust(2,'0'), :from => "#{id_prefix}_#{DATE_TIME_SUFFIXES[:minute]}"
175
+ end
176
+
177
+ webrat_deprecate :selects_time, :select_time
178
+
179
+ # Verifies and selects all the date and time elements on the current page.
180
+ # See #select_time and #select_date for more details and available options.
181
+ #
182
+ # Examples:
183
+ # select_datetime "January 23, 2004 10:30AM"
184
+ # select_datetime "April 26, 1982 7:00PM", :from => "Birthday"
185
+ # select_datetime Time.parse("December 25, 2000 15:30"), :from => "Event"
186
+ # select_datetime "April 26, 1982 5:50PM", :id_prefix => 'birthday'
187
+ def select_datetime(time_to_select, options ={})
188
+ time = time_to_select.is_a?(Time) ? time_to_select : Time.parse(time_to_select)
189
+
190
+ options[:id_prefix] ||= (options[:from] ? FieldByIdLocator.new(@session, dom, options[:from]).locate : nil)
191
+
192
+ select_date time, options
193
+ select_time time, options
194
+ end
195
+
196
+ webrat_deprecate :selects_datetime, :select_datetime
197
+
198
+ # Verifies that an input file field exists on the current page and sets
199
+ # its value to the given +file+, so that the file will be uploaded
200
+ # along with the form. An optional <tt>content_type</tt> may be given.
201
+ #
202
+ # Example:
203
+ # attaches_file "Resume", "/path/to/the/resume.txt"
204
+ # attaches_file "Photo", "/path/to/the/image.png", "image/png"
205
+ def attach_file(field_locator, path, content_type = nil)
206
+ locate_field(field_locator, FileField).set(path, content_type)
207
+ end
208
+
209
+ webrat_deprecate :attaches_file, :attach_file
210
+
211
+ def click_area(area_name)
212
+ find_area(area_name).click
213
+ end
214
+
215
+ webrat_deprecate :clicks_area, :click_area
216
+
217
+ # Issues a request for the URL pointed to by a link on the current page,
218
+ # follows any redirects, and verifies the final page load was successful.
219
+ #
220
+ # click_link has very basic support for detecting Rails-generated
221
+ # JavaScript onclick handlers for PUT, POST and DELETE links, as well as
222
+ # CSRF authenticity tokens if they are present.
223
+ #
224
+ # Javascript imitation can be disabled by passing the option :javascript => false
225
+ #
226
+ # Passing a :method in the options hash overrides the HTTP method used
227
+ # for making the link request
228
+ #
229
+ # It will try to find links by (in order of precedence):
230
+ # innerHTML, with simple &nbsp; handling
231
+ # title
232
+ # id
233
+ #
234
+ # innerHTML and title are matchable by text subtring or Regexp
235
+ # id is matchable by full text equality or Regexp
236
+ #
237
+ # Example:
238
+ # click_link "Sign up"
239
+ # click_link "Sign up", :javascript => false
240
+ # click_link "Sign up", :method => :put
241
+ def click_link(text_or_title_or_id, options = {})
242
+ find_link(text_or_title_or_id).click(options)
243
+ end
244
+
245
+ webrat_deprecate :clicks_link, :click_link
246
+
247
+ # Verifies that a submit button exists for the form, then submits the form, follows
248
+ # any redirects, and verifies the final page was successful.
249
+ #
250
+ # Example:
251
+ # click_button "Login"
252
+ # click_button
253
+ #
254
+ # The URL and HTTP method for the form submission are automatically read from the
255
+ # <tt>action</tt> and <tt>method</tt> attributes of the <tt><form></tt> element.
256
+ def click_button(value = nil)
257
+ find_button(value).click
258
+ end
259
+
260
+ webrat_deprecate :clicks_button, :click_button
261
+
262
+ def submit_form(id)
263
+ FormLocator.new(@session, dom, id).locate.submit
264
+ end
265
+
266
+ def dom # :nodoc:
267
+ return @dom if @dom
268
+
269
+ if @selector
270
+ @dom = scoped_dom
271
+ else
272
+ @dom = page_dom
273
+ end
274
+
275
+ return @dom
276
+ end
277
+
278
+ protected
279
+
280
+ def page_dom #:nodoc:
281
+ return @response.dom if @response.respond_to?(:dom)
282
+
283
+ if @session.xml_content_type?
284
+ dom = Webrat::XML.xml_document(@response_body)
285
+ else
286
+ dom = Webrat::XML.html_document(@response_body)
287
+ end
288
+
289
+ Webrat.define_dom_method(@response, dom)
290
+ return dom
291
+ end
292
+
293
+ def scoped_dom #:nodoc:
294
+ source = Webrat::XML.to_html(Webrat::XML.css_search(@scope.dom, @selector).first)
295
+
296
+ if @session.xml_content_type?
297
+ Webrat::XML.xml_document(source)
298
+ else
299
+ Webrat::XML.html_document(source)
300
+ end
301
+ end
302
+
303
+ def locate_field(field_locator, *field_types) #:nodoc:
304
+ if field_locator.is_a?(Field)
305
+ field_locator
306
+ else
307
+ field(field_locator, *field_types)
308
+ end
309
+ end
310
+
311
+ def locate_id_prefix(options, &location_strategy) #:nodoc:
312
+ return options[:id_prefix] if options[:id_prefix]
313
+
314
+ if options[:from]
315
+ if (label = LabelLocator.new(@session, dom, options[:from]).locate)
316
+ label.for_id
317
+ else
318
+ raise NotFoundError.new("Could not find the label with text #{options[:from]}")
319
+ end
320
+ else
321
+ yield
322
+ end
323
+ end
324
+
325
+ def forms #:nodoc:
326
+ @forms ||= Form.load_all(@session, dom)
327
+ end
328
+
329
+ end
330
+ end