rwebspec-mechanize 0.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.
@@ -0,0 +1,650 @@
1
+ #***********************************************************
2
+ #***********************************************************
3
+ #* Copyright (c) 2006, Zhimin Zhan.
4
+ #* Distributed open-source, see full license in MIT-LICENSE
5
+ #***********************************************************
6
+
7
+ require 'mechanize'
8
+ require 'nokogiri'
9
+
10
+ module RWebSpec
11
+ module Mechanize
12
+
13
+ ##
14
+ # Wrapping WATIR IE and FireWatir Firefox
15
+ #
16
+ class WebBrowser
17
+
18
+ attr_accessor :context
19
+
20
+ def initialize(base_url = nil, existing_browser = nil, options = {})
21
+ default_options = {:go => false}
22
+ options = default_options.merge options
23
+ @context = Context.new base_url if base_url
24
+ @browser = ::Mechanize.new
25
+
26
+ require 'logger'
27
+ # http://mechanize.rubyforge.org/Mechanize.html
28
+ # TODO option to turn off mechanize logging
29
+ # puts "Max file buffer #{@browser.max_file_buffer}"
30
+ # puts "Follow redirect? #{@browser.follow_redirect?}"
31
+ # puts "redirect limit => #{@browser.redirection_limit}"
32
+ # @browser.log = Logger.new("mech.log")
33
+ @browser.keep_alive = false
34
+ @browser.open_timeout = 15
35
+ @browser.read_timeout = 15
36
+ # @browser.max_file_buffer=(bytes)
37
+
38
+ if options[:go]
39
+ @browser.get(base_url)
40
+ else
41
+ @browser
42
+ end
43
+ end
44
+
45
+ # set proxy password
46
+ def set_proxy(address, port, user = nil, password = nil)
47
+ @browser.set_proxy(address, port, user, password)
48
+ end
49
+
50
+ def agent
51
+ @browser
52
+ end
53
+
54
+ ##
55
+ # Delegate to Watir
56
+ #
57
+ [:button, :cell, :checkbox, :div, :form, :frame, :h1, :h2, :h3, :h4, :h5, :h6, :hidden, :image, :li, :link, :map, :pre, :row, :radio, :select_list, :span, :table, :text_field, :paragraph, :file_field, :label].each do |method|
58
+ define_method method do |*args|
59
+ @browser.send(method, *args)
60
+ end
61
+ end
62
+ alias td cell
63
+ alias check_box checkbox # seems watir doc is wrong, checkbox not check_box
64
+ alias tr row
65
+ alias a link
66
+ alias img image
67
+
68
+ # Wrapp of Watir's area to support Firefox and Watir
69
+ #
70
+ # Note: FireWatir does not support area directly, treat it as text_field
71
+ def area(*args)
72
+ if is_firefox?
73
+ text_field(*args)
74
+ else
75
+ @browser.send("area", *args)
76
+ end
77
+ end
78
+
79
+ def modal_dialog(how=nil, what=nil)
80
+ @browser.modal_dialog(how, what)
81
+ end
82
+
83
+ # This is the main method for accessing a generic element with a given attibute
84
+ # * how - symbol - how we access the element. Supports all values except :index and :xpath
85
+ # * what - string, integer or regular expression - what we are looking for,
86
+ #
87
+ # Valid values for 'how' are listed in the Watir Wiki - http://wiki.openqa.org/display/WTR/Methods+supported+by+Element
88
+ #
89
+ # returns an Watir::Element object
90
+ #
91
+ # Typical Usage
92
+ #
93
+ # element(:class, /foo/) # access the first element with class 'foo'. We can use a string in place of the regular expression
94
+ # element(:id, "11") # access the first element that matches an id
95
+ def element(how, what)
96
+ return @browser.element(how, what)
97
+ end
98
+
99
+ # this is the main method for accessing generic html elements by an attribute
100
+ #
101
+ # Returns a HTMLElements object
102
+ #
103
+ # Typical usage:
104
+ #
105
+ # elements(:class, 'test').each { |l| puts l.to_s } # iterate through all elements of a given attribute
106
+ # elements(:alt, 'foo')[1].to_s # get the first element of a given attribute
107
+ # elements(:id, 'foo').length # show how many elements are foung in the collection
108
+ #
109
+ def elements(how, what)
110
+ return @browser.elements(how, what)
111
+ end
112
+
113
+ def show_all_objects
114
+ @browser.show_all_objects
115
+ end
116
+
117
+ # Returns the specified ole object for input elements on a web page.
118
+ #
119
+ # This method is used internally by Watir and should not be used externally. It cannot be marked as private because of the way mixins and inheritance work in watir
120
+ #
121
+ # * how - symbol - the way we look for the object. Supported values are
122
+ # - :name
123
+ # - :id
124
+ # - :index
125
+ # - :value etc
126
+ # * what - string that we are looking for, ex. the name, or id tag attribute or index of the object we are looking for.
127
+ # * types - what object types we will look at.
128
+ # * value - used for objects that have one name, but many values. ex. radio lists and checkboxes
129
+ def locate_input_element(how, what, types, value=nil)
130
+ @browser.locate_input_element(how, what, types, value)
131
+ end
132
+
133
+ # This is the main method for accessing map tags - http://msdn.microsoft.com/workshop/author/dhtml/reference/objects/map.asp?frame=true
134
+ # * how - symbol - how we access the map,
135
+ # * what - string, integer or regular expression - what we are looking for,
136
+ #
137
+ # Valid values for 'how' are listed in the Watir Wiki - http://wiki.openqa.org/display/WTR/Methods+supported+by+Element
138
+ #
139
+ # returns a map object
140
+ #
141
+ # Typical Usage
142
+ #
143
+ # map(:id, /list/) # access the first map that matches list.
144
+ # map(:index,2) # access the second map on the page
145
+ # map(:title, "A Picture") # access a map using the tooltip text. See http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/title_1.asp?frame=true
146
+ #
147
+ def map(how, what=nil)
148
+ @browser.map(how, what)
149
+ end
150
+
151
+ def contains_text(text)
152
+ @browser.contains_text(text);
153
+ end
154
+
155
+ # return HTML of current web page
156
+ def page_source
157
+ @browser.page.body
158
+ #@browser.document.body
159
+ end
160
+ alias html_body page_source
161
+ alias html page_source
162
+
163
+
164
+ def page
165
+ @browser.current_page
166
+ end
167
+
168
+ # return plain text of current web page
169
+ def text(squeeze_spaces = true)
170
+ require 'nokogiri'
171
+ begin
172
+ page_text_string = Nokogiri::HTML(html).text
173
+ page_text_string = page_text_string.squeeze(" ") if squeeze_spaces# remove duplicated (spaces)
174
+ return page_text_string
175
+ rescue => e
176
+ puts "failed to santize html source => text, #{e}"
177
+ end
178
+ return html
179
+ end
180
+
181
+ # Sanitize gem does not work in Java, return empty spaces back
182
+ def text_sanitize(squeeze_spaces = true)
183
+ if RUBY_PLATFORM =~ /java/ || RUBY_PLATFORM =~ /jruby/
184
+ require 'sanitize'
185
+ end
186
+
187
+ begin
188
+ page_text_string = Sanitize.clean(html)
189
+ page_text_string = page_text_string.squeeze(" ") if squeeze_spaces# remove duplicated (spaces)
190
+ return page_text_string
191
+ rescue => e
192
+ puts "failed to santize html source => text, #{e}"
193
+ end
194
+ # @browser.text
195
+ end
196
+
197
+ def page_title
198
+ @browser.page.title
199
+ end
200
+
201
+ [:images, :links, :buttons, :select_lists, :checkboxes, :radios, :text_fields, :divs, :dls, :dds, :dts, :ems, :lis, :maps, :spans, :strongs, :ps, :pres, :labels, :cells, :rows].each do |method|
202
+ define_method method do
203
+ @browser.current_page.send(method)
204
+ end
205
+ end
206
+ alias as links
207
+ alias trs rows
208
+ alias tds cells
209
+ alias imgs images
210
+
211
+ # current url
212
+ def url
213
+ @browser.url
214
+ end
215
+
216
+ def base_url=(new_base_url)
217
+ if @context
218
+ @conext.base_url = new_base_url
219
+ return
220
+ end
221
+ @context = Context.new base_url
222
+ end
223
+
224
+ # Close the browser window. Useful for automated test suites to reduce
225
+ # test interaction.
226
+ def close_browser
227
+ @browser = nil
228
+ end
229
+ alias close close_browser
230
+
231
+ def self.close_all_browsers(browser_type = :ie)
232
+
233
+ end
234
+
235
+ def full_url(relative_url)
236
+ if @context && @context.base_url
237
+ @context.base_url + relative_url
238
+ else
239
+ relative_url
240
+ end
241
+ end
242
+
243
+ def begin_at(relative_url)
244
+ @browser.goto full_url(relative_url)
245
+ end
246
+
247
+ def browser_opened?
248
+ begin
249
+ @browser != nil
250
+ rescue => e
251
+ return false
252
+ end
253
+ end
254
+
255
+ # Some browsers (i.e. IE) need to be waited on before more actions can be
256
+ # performed. Most action methods in Watir::Simple already call this before
257
+ # and after.
258
+ def wait_for_browser
259
+ end
260
+
261
+ # A convenience method to wait at both ends of an operation for the browser
262
+ # to catch up.
263
+ def wait_before_and_after
264
+ wait_for_browser
265
+ yield
266
+ wait_for_browser
267
+ end
268
+
269
+ [:back, :forward, :refresh, :focus, :close_others].each do |method|
270
+ define_method(method) do
271
+ @browser.send(method)
272
+ end
273
+ end
274
+ alias refresh_page refresh
275
+ alias go_back back
276
+ alias go_forward forward
277
+
278
+ # Go to a page
279
+ # Usage:
280
+ # open_browser("http://www.itest2.com"
281
+ # ....
282
+ # goto_page("/purchase") # full url => http://www.itest.com/purchase
283
+ def goto_page(page)
284
+ goto_url full_url(page);
285
+ end
286
+
287
+ # Go to a URL directly
288
+ # goto_url("http://www.itest2.com/downloads")
289
+ def goto_url(url)
290
+ @browser.get(url)
291
+ end
292
+
293
+ # text fields
294
+ def enter_text_into_field_with_name(name, value)
295
+ the_form = identify_form
296
+ wait_before_and_after {
297
+ the_form.fields_with(:name => name).each do |x|
298
+ next unless x.type == "text" || x.type == "password" || x.type == "hidden" || x.type == "textarea"
299
+ x.value = value
300
+ end
301
+ }
302
+ end
303
+
304
+ alias set_form_element enter_text_into_field_with_name
305
+ alias enter_text enter_text_into_field_with_name
306
+ alias set_hidden_field set_form_element
307
+
308
+ #links
309
+ def click_link_with_id(link_id, opts = {})
310
+ if opts && opts[:index]
311
+ wait_before_and_after {
312
+ page.links_with(:id => link_id)[options[:index]].click
313
+ }
314
+ else
315
+ wait_before_and_after {
316
+ page.link_with(:id => link_id).click
317
+ }
318
+ end
319
+ end
320
+
321
+ def click_link_with_text(link_text, opts = {})
322
+ if opts && opts[:index]
323
+ wait_before_and_after {
324
+ page.link_with(:text => link_text)[opts[:index]].click
325
+ }
326
+ else
327
+ wait_before_and_after {
328
+ begin
329
+ page.link_with(:text => link_text).click
330
+ rescue => e
331
+ # NOTE
332
+ # e.g. <a href="..."> New client</a>, the link with space
333
+ # puts "exact link #{link_text} not found, just regex"
334
+ page.link_with(:text => /#{link_text}/).click
335
+ end
336
+ }
337
+ end
338
+ end
339
+ alias click_link click_link_with_text
340
+
341
+
342
+ # Click a button with give HTML id
343
+ # Usage:
344
+ # click_button_with_id("btn_sumbit")
345
+ #
346
+ # Occasionaly, the button with ID exists, but now shown in the form (debug form.inspect), add efault to submit
347
+ #
348
+ def click_button_with_id(id, opts = {})
349
+ the_form = identify_form
350
+ the_button = nil
351
+ # puts "[DEBUG] click button with id => #{id} | #{opts.inspect} | #{the_form.inspect}"
352
+ if opts && opts[:index]
353
+ wait_before_and_after {
354
+ the_button = the_form.buttons_with(:id => id)[opts[:index]]
355
+ }
356
+ else
357
+ wait_before_and_after {
358
+ the_button = the_form.button_with(:id => id)
359
+ }
360
+ end
361
+
362
+ if the_button.nil?
363
+ @browser.submit(the_form)
364
+ return
365
+ end
366
+
367
+ # raise "No button with id: #{id} found " unless the_button
368
+ if the_button.type == "submit"
369
+ puts "[DEBUG] submit the form"
370
+ @browser.submit(the_form, the_button)
371
+ else
372
+ puts "[DEBUG] Warning not submit button"
373
+ @browser.submit(the_form, the_button)
374
+ end
375
+
376
+ end
377
+
378
+ # Click a button with give name
379
+ # Usage:
380
+ # click_button_with_name("confirm")
381
+ def click_button_with_name(name, opts={})
382
+ the_form = identify_form
383
+ if opts && opts[:index]
384
+ wait_before_and_after {
385
+ the_button = the_form.buttons_with(:name => name)[opts[:index]]
386
+ # button(:name => name, :index => opts[:index]).click
387
+ }
388
+ else
389
+ wait_before_and_after {
390
+ the_button = the_form.button_with(:name => name)
391
+ }
392
+ end
393
+
394
+ raise "No button with id: #{id} found " unless the_button
395
+ if the_button.type == "submit"
396
+ @browser.submit(the_form, the_button)
397
+ else
398
+ puts "Warning not submit button"
399
+ @browser.submit(the_form, the_button)
400
+ end
401
+
402
+ end
403
+
404
+ def identify_form
405
+ the_form = page.forms.first
406
+ if the_form.nil?
407
+ sleep 0.1
408
+ the_form = page.forms.first
409
+ end
410
+ return the_form
411
+ end
412
+
413
+ # Click a button with caption
414
+ # Usage:
415
+ # click_button_with_caption("Confirm payment")
416
+ def click_button_with_caption(caption, opts={})
417
+ the_form = identify_form
418
+ if opts && opts[:index]
419
+ wait_before_and_after {
420
+ the_form.button_with(:value => caption)[opts[:index]].click
421
+ }
422
+ else
423
+ wait_before_and_after {
424
+ # button(:caption, caption).click;
425
+ if the_form.button(:value => caption).type == "submit"
426
+ @browser.submit(the_form, the_form.button(:value => caption))
427
+ else
428
+ the_form.button_with(:value => caption).click
429
+ end
430
+ }
431
+ end
432
+ end
433
+ alias click_button click_button_with_caption
434
+ alias click_button_with_text click_button_with_caption
435
+ alias click_button_with_value click_button_with_caption
436
+
437
+
438
+ # Select a dropdown list by name
439
+ # Usage:
440
+ # select_option("country", "Australia")
441
+ def select_option(selectName, optionText)
442
+ the_form = identify_form
443
+ the_select_list = the_form.field_with(:name => selectName)
444
+ the_select_list.options.each do |option|
445
+ if option.text == optionText
446
+ option.click
447
+ break
448
+ end
449
+ end
450
+
451
+ end
452
+
453
+ # submit first submit button
454
+ def submit(buttonName = nil)
455
+ if (buttonName.nil?) then
456
+ page.forms.first.submit
457
+ # buttons.each { |button|
458
+ # next if button.type != 'submit'
459
+ # button.click
460
+ # return
461
+ # }
462
+ else
463
+ click_button_with_name(buttonName)
464
+ end
465
+ end
466
+
467
+ # Check a checkbox
468
+ # Usage:
469
+ # check_checkbox("agree")
470
+ # check_checkbox("agree", "true")
471
+ def check_checkbox(checkBoxName, values=nil)
472
+ the_form = identify_form
473
+ if values
474
+ values.class == Array ? arys = values : arys = [values]
475
+ arys.each {|cbx_value|
476
+ the_form.checkboxes_with(:name => checkBoxName).each do |cb|
477
+ cb.check if cb.value == cbx_value
478
+ end
479
+ }
480
+ else
481
+ the_form.checkbox_with(:name => checkBoxName).check
482
+ end
483
+ end
484
+
485
+ # UnCheck a checkbox
486
+ # Usage:
487
+ # uncheck_checkbox("agree")
488
+ # uncheck_checkbox("agree", "false")
489
+ def uncheck_checkbox(checkBoxName, values = nil)
490
+ the_form = identify_form
491
+ if values
492
+ values.class == Array ? arys = values : arys = [values]
493
+ arys.each {|cbx_value|
494
+ the_form.checkboxes_with(:name => checkBoxName).each do |cb|
495
+ cb.uncheck if cb.value == cbx_value
496
+ end
497
+ }
498
+ else
499
+ the_form.checkbox_with(:name => checkBoxName).uncheck
500
+ end
501
+ end
502
+
503
+
504
+ # Click a radio button
505
+ # Usage:
506
+ # click_radio_option("country", "Australia")
507
+ def click_radio_option(radio_group, radio_option)
508
+ # radio(:name => radio_group, :value => radio_option).set
509
+ the_form = identify_form
510
+ the_form.radiobutton_with(:name => radio_group, :value => radio_option).check
511
+ end
512
+ alias click_radio_button click_radio_option
513
+
514
+ # Clear a radio button
515
+ # Usage:
516
+ # click_radio_option("country", "Australia")
517
+ def clear_radio_option(radio_group, radio_option)
518
+ radio(:name => radio_group, :value => radio_option).uncheck
519
+ end
520
+ alias clear_radio_button clear_radio_option
521
+
522
+ # Deprecated: using Watir style directly instead
523
+ def element_by_id(elem_id)
524
+ elem = @browser.document.getElementById(elem_id)
525
+ end
526
+
527
+ def element_value(elementId)
528
+ elem = element_by_id(elementId)
529
+ elem ? elem.invoke('innerText') : nil
530
+ end
531
+
532
+ def element_source(elementId)
533
+ elem = element_by_id(elementId)
534
+ assert_not_nil(elem, "HTML element: #{elementId} not exists")
535
+ elem.innerHTML
536
+ end
537
+
538
+ def select_file_for_upload(file_field, file_path)
539
+ normalized_file_path = RUBY_PLATFORM.downcase.include?("mingw") ? file_path.gsub("/", "\\") : file_path
540
+ the_form = identify_form
541
+ the_form.file_uploads.first.file_name = normalized_file_path
542
+ end
543
+
544
+ # Watir 1.9
545
+ def javascript_dialog
546
+ @browser.javascript_dialog
547
+ end
548
+
549
+ def start_window(url = nil)
550
+ @browser.start_window(url);
551
+ end
552
+
553
+ # Attach to existing browser
554
+ #
555
+ # Usage:
556
+ # WebBrowser.attach_browser(:title, "iTest2")
557
+ # WebBrowser.attach_browser(:url, "http://www.itest2.com")
558
+ # WebBrowser.attach_browser(:url, "http://www.itest2.com", {:browser => "Firefox", :base_url => "http://www.itest2.com"})
559
+ # WebBrowser.attach_browser(:title, /agileway\.com\.au\/attachment/) # regular expression
560
+ def self.attach_browser(how, what, options={})
561
+ default_options = {:browser => "IE"}
562
+ options = default_options.merge(options)
563
+ site_context = Context.new(options[:base_url]) if options[:base_url]
564
+ if (options[:browser] == "Firefox")
565
+ ff = FireWatir::Firefox.attach(how, what)
566
+ return WebBrowser.new_from_existing(ff, site_context)
567
+ else
568
+ return WebBrowser.new_from_existing(Watir::IE.attach(how, what), site_context)
569
+ end
570
+ end
571
+
572
+ # Attach a Watir::IE instance to a popup window.
573
+ #
574
+ # Typical usage
575
+ # new_popup_window(:url => "http://www.google.com/a.pdf")
576
+ def new_popup_window(options, browser = "ie")
577
+ if is_firefox?
578
+ raise "not implemented"
579
+ else
580
+ if options[:url]
581
+ Watir::IE.attach(:url, options[:url])
582
+ elsif options[:title]
583
+ Watir::IE.attach(:title, options[:title])
584
+ else
585
+ raise 'Please specify title or url of new pop up window'
586
+ end
587
+ end
588
+ end
589
+
590
+ # ---
591
+ # For deubgging
592
+ # ---
593
+ def dump_response(stream = nil)
594
+ stream.nil? ? puts(page_source) : stream.puts(page_source)
595
+ end
596
+
597
+ # A Better Popup Handler using the latest Watir version. Posted by Mark_cain@rl.gov
598
+ #
599
+ # http://wiki.openqa.org/display/WTR/FAQ#FAQ-HowdoIattachtoapopupwindow%3F
600
+ #
601
+ def start_clicker( button, waitTime= 9, user_input=nil)
602
+ # get a handle if one exists
603
+ hwnd = @browser.enabled_popup(waitTime)
604
+ if (hwnd) # yes there is a popup
605
+ w = WinClicker.new
606
+ if ( user_input )
607
+ w.setTextValueForFileNameField( hwnd, "#{user_input}" )
608
+ end
609
+ # I put this in to see the text being input it is not necessary to work
610
+ sleep 3
611
+ # "OK" or whatever the name on the button is
612
+ w.clickWindowsButton_hwnd( hwnd, "#{button}" )
613
+ #
614
+ # this is just cleanup
615
+ w = nil
616
+ end
617
+ end
618
+
619
+
620
+ # Save current web page source to file
621
+ # usage:
622
+ # save_page("/tmp/01.html")
623
+ # save_page() => # will save to "20090830112200.html"
624
+ def save_page(file_name = nil)
625
+ file_name ||= Time.now.strftime("%Y%m%d%H%M%S") + ".html"
626
+ puts "about to save page: #{File.expand_path(file_name)}" if $DEBUG
627
+ File.open(file_name, "w").puts page_source
628
+ end
629
+
630
+
631
+ # Verify the next page following an operation.
632
+ #
633
+ # Typical usage:
634
+ # browser.expect_page HomePage
635
+ def expect_page(page_clazz, argument = nil)
636
+ if argument
637
+ page_clazz.new(self, argument)
638
+ else
639
+ page_clazz.new(self)
640
+ end
641
+ end
642
+
643
+ # is it running in MS Windows platforms?
644
+ def self.is_windows?
645
+ RUBY_PLATFORM.downcase.include?("mswin") or RUBY_PLATFORM.downcase.include?("mingw")
646
+ end
647
+
648
+ end
649
+ end
650
+ end