rwebspec-mechanize 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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