firewatir 1.6.2 → 1.6.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/LICENSE +32 -0
  2. data/lib/firewatir.rb +40 -50
  3. data/lib/firewatir/container.rb +491 -534
  4. data/lib/firewatir/document.rb +239 -0
  5. data/lib/firewatir/element.rb +1365 -0
  6. data/lib/firewatir/element_collections.rb +314 -0
  7. data/lib/firewatir/elements/button.rb +15 -0
  8. data/lib/firewatir/elements/file_field.rb +29 -0
  9. data/lib/firewatir/elements/form.rb +40 -0
  10. data/lib/firewatir/elements/frame.rb +55 -0
  11. data/lib/firewatir/elements/hidden.rb +56 -0
  12. data/lib/firewatir/elements/image.rb +139 -0
  13. data/lib/firewatir/elements/input_element.rb +44 -0
  14. data/lib/firewatir/elements/link.rb +76 -0
  15. data/lib/firewatir/elements/non_control_element.rb +53 -0
  16. data/lib/firewatir/elements/non_control_elements.rb +108 -0
  17. data/lib/firewatir/elements/not_used.rb +278 -0
  18. data/lib/firewatir/elements/option.rb +131 -0
  19. data/lib/firewatir/elements/radio_check_common.rb +163 -0
  20. data/lib/firewatir/elements/select_list.rb +188 -0
  21. data/lib/firewatir/elements/table.rb +218 -0
  22. data/lib/firewatir/elements/table_cell.rb +54 -0
  23. data/lib/firewatir/elements/table_row.rb +100 -0
  24. data/lib/firewatir/elements/text_field.rb +218 -0
  25. data/lib/firewatir/exceptions.rb +10 -10
  26. data/lib/firewatir/firefox.rb +1040 -1127
  27. data/lib/firewatir/jssh_socket.rb +101 -0
  28. data/lib/firewatir/version.rb +5 -5
  29. data/unittests/attach_to_new_window_test.rb +49 -42
  30. data/unittests/bug_fixes_test.rb +195 -198
  31. data/unittests/buttons_xpath_test.rb +88 -88
  32. data/unittests/checkbox_test.rb +158 -155
  33. data/unittests/checkbox_xpath_test.rb +107 -107
  34. data/unittests/div_test.rb +275 -276
  35. data/unittests/filefield_test.rb +49 -45
  36. data/unittests/filefield_xpath_test.rb +35 -35
  37. data/unittests/form_test.rb +296 -308
  38. data/unittests/frame_test.rb +159 -152
  39. data/unittests/hidden_test.rb +85 -85
  40. data/unittests/hidden_xpath_test.rb +72 -72
  41. data/unittests/html/blankpage.html +11 -11
  42. data/unittests/html/buttons1.html +61 -61
  43. data/unittests/html/cssTest.html +42 -42
  44. data/unittests/html/div.html +72 -72
  45. data/unittests/html/fileupload.html +45 -45
  46. data/unittests/html/formTest1.html +38 -38
  47. data/unittests/html/forms2.html +45 -45
  48. data/unittests/html/frame_buttons.html +3 -3
  49. data/unittests/html/iframeTest.html +14 -14
  50. data/unittests/html/iframeTest1.html +13 -13
  51. data/unittests/html/iframeTest2.html +5 -5
  52. data/unittests/html/links1.html +42 -42
  53. data/unittests/html/nestedFrames.html +6 -6
  54. data/unittests/html/new_browser.html +1 -0
  55. data/unittests/html/new_browser_popup.html +8 -0
  56. data/unittests/html/pass.html +9 -9
  57. data/unittests/html/pre.html +27 -27
  58. data/unittests/html/redirect.html +10 -10
  59. data/unittests/html/redirect1.html +8 -8
  60. data/unittests/html/redirect2.html +8 -8
  61. data/unittests/html/redirect3.html +8 -8
  62. data/unittests/html/simple_table_columns.html +74 -74
  63. data/unittests/html/table1.html +165 -165
  64. data/unittests/html/textfields1.html +62 -62
  65. data/unittests/images_test.rb +198 -205
  66. data/unittests/images_xpath_test.rb +118 -119
  67. data/unittests/javascript_test.rb +75 -75
  68. data/unittests/links_test.rb +231 -232
  69. data/unittests/links_xpath_test.rb +79 -79
  70. data/unittests/mozilla_all_tests.rb +7 -7
  71. data/unittests/pre_test.rb +75 -76
  72. data/unittests/radios_xpath_test.rb +101 -101
  73. data/unittests/redirect_test.rb +41 -41
  74. data/unittests/selectbox_test.rb +142 -142
  75. data/unittests/selectbox_xpath_test.rb +129 -129
  76. data/unittests/setup.rb +29 -30
  77. data/unittests/table_test.rb +385 -373
  78. data/unittests/table_xpath_test.rb +185 -185
  79. data/unittests/textfields_test.rb +234 -233
  80. data/unittests/textfields_xpath_test.rb +113 -113
  81. metadata +33 -11
  82. data/lib/firewatir/MozillaBaseElement.rb +0 -1863
  83. data/lib/firewatir/htmlelements.rb +0 -1911
  84. data/unittests/iostring.rb +0 -30
  85. data/unittests/iostring_test.rb +0 -48
@@ -0,0 +1,100 @@
1
+ module FireWatir
2
+ #
3
+ # Description:
4
+ # Class for Table row element.
5
+ #
6
+ class TableRow < Element
7
+ attr_accessor :element_name
8
+
9
+ #
10
+ # Description:
11
+ # Locate the table row element on the page.
12
+ #
13
+ def locate
14
+ @o = nil
15
+ case @how
16
+ when :jssh_name
17
+ @element_name = @what
18
+ when :xpath
19
+ @element_name = element_by_xpath(@container, @what)
20
+ else
21
+ @element_name = locate_tagged_element("TR", @how, @what)
22
+ end
23
+ @o = self
24
+ end
25
+
26
+ #
27
+ # Description:
28
+ # Initializes the instance of table row object.
29
+ #
30
+ # Input:
31
+ # - how - Attribute to identify the table row element.
32
+ # - what - Value of that attribute.
33
+ #
34
+ def initialize(container, how, what)
35
+ @how = how
36
+ @what = what
37
+ @container = container
38
+ #super nil
39
+ end
40
+
41
+ #
42
+ # Description:
43
+ # Gets the length of columns in table row.
44
+ #
45
+ # Output:
46
+ # Length of columns in table row.
47
+ #
48
+ def column_count
49
+ assert_exists
50
+ arr_cells = cells
51
+ return arr_cells.length
52
+ end
53
+
54
+ #
55
+ # Description:
56
+ # Get cell at specified index in a row.
57
+ #
58
+ # Input:
59
+ # key - column index.
60
+ #
61
+ # Output:
62
+ # Table cell element at specified index.
63
+ #
64
+ def [] (key)
65
+ assert_exists
66
+ arr_cells = cells
67
+ return arr_cells[key - 1]
68
+ end
69
+
70
+ #
71
+ # Description:
72
+ # Iterate over each cell in a row.
73
+ #
74
+ def each
75
+ assert_exists
76
+ arr_cells = cells
77
+ for i in 0..arr_cells.length - 1 do
78
+ yield arr_cells[i]
79
+ end
80
+ end
81
+
82
+ #
83
+ # Description:
84
+ # Get array of all cells in Table Row
85
+ #
86
+ # Output:
87
+ # Array containing Table Cell elements.
88
+ #
89
+ def cells
90
+ assert_exists
91
+ arr_cells = get_cells
92
+ row_cells = Array.new(arr_cells.length)
93
+ for i in 0..arr_cells.length - 1 do
94
+ row_cells[i] = TableCell.new(@container, :jssh_name, arr_cells[i])
95
+ end
96
+ return row_cells
97
+ end
98
+
99
+ end # TableRow
100
+ end # FireWatir
@@ -0,0 +1,218 @@
1
+ module FireWatir
2
+ #
3
+ # Description:
4
+ # Class for Text Field element.
5
+ #
6
+ class TextField < InputElement
7
+ INPUT_TYPES = ["text", "password", "textarea"]
8
+
9
+ # Gets the size of the text field element.
10
+ def_wrap :size
11
+ # Gets max length of the text field element.
12
+ def_wrap :maxlength_string, :maxlength
13
+ def maxlength
14
+ maxlength_string.to_i
15
+ end
16
+ # Returns true if the text field is read only, false otherwise.
17
+ def_wrap :readonly?, :readOnly
18
+
19
+ #
20
+ # Description:
21
+ # Used to populate the properties in to_s method
22
+ #
23
+ #def text_string_creator
24
+ # n = []
25
+ # n << "length:".ljust(TO_S_SIZE) + self.size.to_s
26
+ # n << "max length:".ljust(TO_S_SIZE) + self.maxlength.to_s
27
+ # n << "read only:".ljust(TO_S_SIZE) + self.readonly?.to_s
28
+ #
29
+ # return n
30
+ #end
31
+ #private :text_string_creator
32
+
33
+ # TODO: Impelement the to_s method.
34
+ def to_s
35
+ assert_exists
36
+ super({"length" => "size","max length" => "maxlength","read only" => "readOnly" })
37
+ end
38
+
39
+ #
40
+ # Description:
41
+ # Checks if object is read-only or not.
42
+ #
43
+ def assert_not_readonly
44
+ raise ObjectReadOnlyException, "Textfield #{@how} and #{@what} is read only." if self.readonly?
45
+ end
46
+
47
+ #
48
+ # Description:
49
+ # Checks if the provided text matches with the contents of text field. Text can be a string or regular expression.
50
+ #
51
+ # Input:
52
+ # - containsThis - Text to verify.
53
+ #
54
+ # Output:
55
+ # True if provided text matches with the contents of text field, false otherwise.
56
+ #
57
+ def verify_contains( containsThis )
58
+ assert_exists
59
+ if containsThis.kind_of? String
60
+ return true if self.value == containsThis
61
+ elsif containsThis.kind_of? Regexp
62
+ return true if self.value.match(containsThis) != nil
63
+ end
64
+ return false
65
+ end
66
+
67
+ # this method is used to drag the entire contents of the text field to another text field
68
+ # 19 Jan 2005 - It is added as prototype functionality, and may change
69
+ # * destination_how - symbol, :id, :name how we identify the drop target
70
+ # * destination_what - string or regular expression, the name, id, etc of the text field that will be the drop target
71
+ # TODO: Can we have support for this in Firefox.
72
+ #def drag_contents_to( destination_how , destination_what)
73
+ # assert_exists
74
+ # destination = element.text_field(destination_how, destination_what)
75
+ # raise UnknownObjectException , "Unable to locate destination using #{destination_how } and #{destination_what } " if destination.exists? == false
76
+
77
+ # @o.focus
78
+ # @o.select()
79
+ # value = self.value
80
+
81
+ # @o.fireEvent("onSelect")
82
+ # @o.fireEvent("ondragstart")
83
+ # @o.fireEvent("ondrag")
84
+ # destination.fireEvent("onDragEnter")
85
+ # destination.fireEvent("onDragOver")
86
+ # destination.fireEvent("ondrop")
87
+
88
+ # @o.fireEvent("ondragend")
89
+ # destination.value= ( destination.value + value.to_s )
90
+ # self.value = ""
91
+ #end
92
+ # alias dragContentsTo drag_contents_to
93
+
94
+ #
95
+ # Description:
96
+ # Clears the contents of the text field.
97
+ # Raises ObjectDisabledException if text field is disabled.
98
+ # Raises ObjectReadOnlyException if text field is read only.
99
+ #
100
+ def clear
101
+ assert_exists
102
+ assert_enabled
103
+ assert_not_readonly
104
+
105
+ highlight(:set)
106
+
107
+ @o.scrollIntoView
108
+ @o.focus
109
+ @o.select()
110
+ @o.fireEvent("onSelect")
111
+ @o.value = ""
112
+ @o.fireEvent("onKeyPress")
113
+ @o.fireEvent("onChange")
114
+ @container.wait()
115
+ highlight(:clear)
116
+ end
117
+
118
+ #
119
+ # Description:
120
+ # Append the provided text to the contents of the text field.
121
+ # Raises ObjectDisabledException if text field is disabled.
122
+ # Raises ObjectReadOnlyException if text field is read only.
123
+ #
124
+ # Input:
125
+ # - setThis - Text to be appended.
126
+ #
127
+ def append( setThis)
128
+ assert_exists
129
+ assert_enabled
130
+ assert_not_readonly
131
+
132
+ highlight(:set)
133
+ @o.scrollIntoView
134
+ @o.focus
135
+ doKeyPress( setThis )
136
+ highlight(:clear)
137
+ end
138
+
139
+ #
140
+ # Description:
141
+ # Sets the contents of the text field to the provided text. Overwrite the existing contents.
142
+ # Raises ObjectDisabledException if text field is disabled.
143
+ # Raises ObjectReadOnlyException if text field is read only.
144
+ #
145
+ # Input:
146
+ # - setThis - Text to be set.
147
+ #
148
+ def set( setThis )
149
+ assert_exists
150
+ assert_enabled
151
+ assert_not_readonly
152
+
153
+ highlight(:set)
154
+ @o.scrollIntoView
155
+ @o.focus
156
+ @o.select()
157
+ @o.fireEvent("onSelect")
158
+ @o.value = ""
159
+ @o.fireEvent("onKeyPress")
160
+ doKeyPress( setThis )
161
+ highlight(:clear)
162
+ @o.fireEvent("onChange")
163
+ @o.fireEvent("onBlur")
164
+ end
165
+
166
+ #
167
+ # Description:
168
+ # Sets the text of the text field withoud firing the events like onKeyPress, onKeyDown etc. This should not be used generally, but it
169
+ # is useful in situations where you need to set large text to the text field and you know that you don't have any event to be
170
+ # fired.
171
+ #
172
+ # Input:
173
+ # - v - Text to be set.
174
+ #
175
+ #def value=(v)
176
+ # assert_exists
177
+ # @o.value = v.to_s
178
+ #end
179
+
180
+ #
181
+ # Description:
182
+ # Used to set the value of text box and fires the event onKeyPress, onKeyDown, onKeyUp after each character.
183
+ # Shouldnot be used externally. Used internally by set and append methods.
184
+ #
185
+ # Input:
186
+ # - value - The string to enter into the text field
187
+ #
188
+ def doKeyPress( value )
189
+ begin
190
+ max = maxlength
191
+ if (max > 0 && value.length > max)
192
+ original_value = value
193
+ value = original_value[0...max]
194
+ element.log " Supplied string is #{suppliedValue.length} chars, which exceeds the max length (#{max}) of the field. Using value: #{value}"
195
+ end
196
+ rescue
197
+ # probably a text area - so it doesnt have a max Length
198
+ end
199
+ for i in 0..value.length-1
200
+ #sleep element.typingspeed # typing speed
201
+ c = value[i,1]
202
+ #element.log " adding c.chr " + c #.chr.to_s
203
+ @o.value = "#{(@o.value.to_s + c)}" #c.chr
204
+ @o.fireEvent("onKeyDown")
205
+ @o.fireEvent("onKeyPress")
206
+ @o.fireEvent("onKeyUp")
207
+ end
208
+
209
+ end
210
+ private :doKeyPress
211
+
212
+ alias readOnly? :readonly?
213
+ alias getContents value
214
+ alias maxLength maxlength
215
+
216
+ end # TextField
217
+ end # FireWatir
218
+
@@ -1,10 +1,10 @@
1
- require 'watir/exceptions' # from watir-common
2
-
3
- module Watir
4
- module Exception
5
- # This exception is thrown if we are unable to connect to JSSh.
6
- class UnableToStartJSShException < WatirException; end
7
- end
8
- end
9
-
10
-
1
+ require 'watir/exceptions' # from commonwatir
2
+
3
+ module Watir
4
+ module Exception
5
+ # This exception is thrown if we are unable to connect to JSSh.
6
+ class UnableToStartJSShException < WatirException; end
7
+ end
8
+ end
9
+
10
+
@@ -1,1127 +1,1040 @@
1
- =begin rdoc
2
- This is FireWatir, Web Application Testing In Ruby using Firefox browser
3
-
4
- Typical usage:
5
- # include the controller
6
- require "firewatir"
7
-
8
- # go to the page you want to test
9
- ff = FireWatir::Firefox.start("http://myserver/mypage")
10
-
11
- # enter "Angrez" into an input field named "username"
12
- ff.text_field(:name, "username").set("Angrez")
13
-
14
- # enter "Ruby Co" into input field with id "company_ID"
15
- ff.text_field(:id, "company_ID").set("Ruby Co")
16
-
17
- # click on a link that has "green" somewhere in the text that is displayed
18
- # to the user, using a regular expression
19
- ff.link(:text, /green/)
20
-
21
- # click button that has a caption of "Cancel"
22
- ff.button(:value, "Cancel").click
23
-
24
- FireWatir allows your script to read and interact with HTML objects--HTML tags
25
- and their attributes and contents. Types of objects that FireWatir can identify
26
- include:
27
-
28
- Type Description
29
- =========== ===============================================================
30
- button <input> tags, with the type="button" attribute
31
- check_box <input> tags, with the type="checkbox" attribute
32
- div <div> tags
33
- form
34
- frame
35
- hidden hidden <input> tags
36
- image <img> tags
37
- label
38
- link <a> (anchor) tags
39
- p <p> (paragraph) tags
40
- radio radio buttons; <input> tags, with the type="radio" attribute
41
- select_list <select> tags, known informally as drop-down boxes
42
- span <span> tags
43
- table <table> tags
44
- text_field <input> tags with the type="text" attribute (a single-line
45
- text field), the type="text_area" attribute (a multi-line
46
- text field), and the type="password" attribute (a
47
- single-line field in which the input is replaced with asterisks)
48
-
49
- In general, there are several ways to identify a specific object. FireWatir's
50
- syntax is in the form (how, what), where "how" is a means of identifying
51
- the object, and "what" is the specific string or regular expression
52
- that FireWatir will seek, as shown in the examples above. Available "how"
53
- options depend upon the type of object, but here are a few examples:
54
-
55
- How Description
56
- ============ ===============================================================
57
- :id Used to find an object that has an "id=" attribute. Since each
58
- id should be unique, according to the XHTML specification,
59
- this is recommended as the most reliable method to find an
60
- object.
61
- :name Used to find an object that has a "name=" attribute. This is
62
- useful for older versions of HTML, but "name" is deprecated
63
- in XHTML.
64
- :value Used to find a text field with a given default value, or a
65
- button with a given caption
66
- :index Used to find the nth object of the specified type on a page.
67
- For example, button(:index, 2) finds the second button.
68
- Current versions of FireWatir use 1-based indexing, but future
69
- versions will use 0-based indexing.
70
- :xpath The xpath expression for identifying the element.
71
-
72
- Note that the XHTML specification requires that tags and their attributes be
73
- in lower case. FireWatir doesn't enforce this; FireWatir will find tags and
74
- attributes whether they're in upper, lower, or mixed case. This is either
75
- a bug or a feature.
76
-
77
- FireWatir uses JSSh for interacting with the browser. For further information on
78
- Firefox and DOM go to the following Web page:
79
-
80
- http://www.xulplanet.com/references/objref/
81
-
82
- =end
83
-
84
- module FireWatir
85
- include Watir::Exception
86
-
87
- class Firefox
88
-
89
- include FireWatir::Container
90
-
91
- # XPath Result type. Return only first node that matches the xpath expression.
92
- # More details: "http://developer.mozilla.org/en/docs/DOM:document.evaluate"
93
- FIRST_ORDERED_NODE_TYPE = 9
94
-
95
- # variable to check if firefox browser has been started or not. Currently this is
96
- # used only while starting firefox on windows. For other platforms you need to start
97
- # firefox manually.
98
- #@@firefox_started = false
99
-
100
- # Stack to hold windows.
101
- @@window_stack = Array.new
102
-
103
- # This allows us to identify the window uniquely and close them accordingly.
104
- @window_title = nil
105
- @window_url = nil
106
-
107
- # Description:
108
- # Starts the firefox browser.
109
- # On windows this starts the first version listed in the registry.
110
- #
111
- # Input:
112
- # options - Hash of any of the following options:
113
- # :waitTime - Time to wait for Firefox to start. By default it waits for 2 seconds.
114
- # This is done because if Firefox is not started and we try to connect
115
- # to jssh on port 9997 an exception is thrown.
116
- # :profile - The Firefox profile to use. If none is specified, Firefox will use
117
- # the last used profile.
118
-
119
- # TODO: Start the firefox version given by user. For example
120
- # ff = FireWatir::Firefox.new("1.5.0.4")
121
- #
122
-
123
- def initialize(options = {})
124
- if(options.kind_of?(Integer))
125
- options = {:waitTime => options}
126
- end
127
-
128
- if(options[:profile])
129
- profile_opt = "-no-remote -P #{options[:profile]}"
130
- else
131
- profile_opt = ""
132
- end
133
-
134
- waitTime = options[:waitTime] || 2
135
-
136
- case RUBY_PLATFORM
137
- when /mswin/
138
- # Get the path to Firefox.exe using Registry.
139
- require 'win32/registry.rb'
140
- path_to_bin = ""
141
- Win32::Registry::HKEY_LOCAL_MACHINE.open('SOFTWARE\Mozilla\Mozilla Firefox') do |reg|
142
- keys = reg.keys
143
- reg1 = Win32::Registry::HKEY_LOCAL_MACHINE.open("SOFTWARE\\Mozilla\\Mozilla Firefox\\#{keys[0]}\\Main")
144
- reg1.each do |subkey, type, data|
145
- if(subkey =~ /pathtoexe/i)
146
- path_to_bin = data
147
- end
148
- end
149
- end
150
-
151
- when /linux/i
152
- path_to_bin = `which firefox`.strip
153
- when /darwin/i
154
- path_to_bin = '/Applications/Firefox.app/Contents/MacOS/firefox'
155
- when /java/
156
- raise "Not implemented: Create a browser finder in JRuby"
157
- end
158
- @t = Thread.new { system("#{path_to_bin} -jssh #{profile_opt}")}
159
- sleep waitTime
160
-
161
- set_defaults()
162
- get_window_number()
163
- set_browser_document()
164
- end
165
-
166
- #
167
- # Description:
168
- # Creates a new instance of Firefox. Loads the URL and return the instance.
169
- #
170
- # Input:
171
- # url - url of the page to be loaded.
172
- #
173
- # Output:
174
- # New instance of firefox browser with the given url loaded.
175
- #
176
- def self.start(url)
177
- ff = Firefox.new
178
- ff.goto(url)
179
- return ff
180
- end
181
-
182
- #
183
- # Description:
184
- # Gets the window number opened. Used internally by Firewatir.
185
- #
186
- def get_window_number()
187
- $jssh_socket.send("getWindows().length;\n", 0)
188
- @@current_window = read_socket().to_i - 1
189
-
190
- # Derek Berner 5/16/08
191
- # If at any time a non-browser window like the "Downloads" window
192
- # pops up, it will become the topmost window, so make sure we
193
- # ignore it.
194
- @@current_window = js_eval("getWindows().length").to_i - 1
195
- while js_eval("getWindows()[#{@@current_window}].getBrowser") == ''
196
- @@current_window -= 1;
197
- end
198
-
199
- # This will store the information about the window.
200
- #@@window_stack.push(@@current_window)
201
- #puts "here in get_window_number window number is #{@@current_window}"
202
- return @@current_window
203
- end
204
- private :get_window_number
205
-
206
- #
207
- # Description:
208
- # Loads the given url in the browser. Waits for the page to get loaded.
209
- #
210
- # Input:
211
- # url - url to be loaded.
212
- #
213
- def goto(url)
214
- #set_defaults()
215
- get_window_number()
216
- set_browser_document()
217
- # Load the given url.
218
- $jssh_socket.send("#{BROWSER_VAR}.loadURI(\"#{url}\");\n" , 0)
219
- read_socket()
220
-
221
- wait()
222
- end
223
-
224
- #
225
- # Description:
226
- # Loads the previous page (if there is any) in the browser. Waits for the page to get loaded.
227
- #
228
- def back()
229
- #set_browser_document()
230
- $jssh_socket.send("if(#{BROWSER_VAR}.canGoBack) #{BROWSER_VAR}.goBack();\n", 0)
231
- read_socket();
232
- wait()
233
- end
234
-
235
- #
236
- # Description:
237
- # Loads the next page (if there is any) in the browser. Waits for the page to get loaded.
238
- #
239
- def forward()
240
- #set_browser_document()
241
- $jssh_socket.send("if(#{BROWSER_VAR}.canGoForward) #{BROWSER_VAR}.goForward();\n", 0)
242
- read_socket();
243
- wait()
244
- end
245
-
246
- #
247
- # Description:
248
- # Reloads the current page in the browser. Waits for the page to get loaded.
249
- #
250
- def refresh()
251
- #set_browser_document()
252
- $jssh_socket.send("#{BROWSER_VAR}.reload();\n", 0)
253
- read_socket();
254
- wait()
255
- end
256
-
257
- #
258
- # Description:
259
- # This function creates a new socket at port 9997 and sets the default values for instance and class variables.
260
- # Generatesi UnableToStartJSShException if cannot connect to jssh even after 3 tries.
261
- #
262
- def set_defaults(no_of_tries = 0)
263
- # JSSH listens on port 9997. Create a new socket to connect to port 9997.
264
- begin
265
- $jssh_socket = TCPSocket::new(MACHINE_IP, "9997")
266
- $jssh_socket.sync = true
267
- read_socket()
268
- rescue
269
- no_of_tries += 1
270
- retry if no_of_tries < 3
271
- raise UnableToStartJSShException, "Unable to connect to machine : #{MACHINE_IP} on port 9997. Make sure that JSSh is properly installed and Firefox is running with '-jssh' option"
272
- end
273
- @error_checkers = []
274
- end
275
- private :set_defaults
276
-
277
- def set_slow_speed
278
- @typingspeed = DEFAULT_TYPING_SPEED
279
- @defaultSleepTime = DEFAULT_SLEEP_TIME
280
- end
281
- private :set_slow_speed
282
-
283
- #
284
- # Description:
285
- # Sets the document, window and browser variables to point to correct object in JSSh.
286
- #
287
- def set_browser_document
288
- # Get the window in variable WINDOW_VAR.
289
- # Get the browser in variable BROWSER_VAR.
290
- jssh_command = "var #{WINDOW_VAR} = getWindows()[#{@@current_window}];"
291
- jssh_command += " var #{BROWSER_VAR} = #{WINDOW_VAR}.getBrowser();"
292
- # Get the document and body in variable DOCUMENT_VAR and BODY_VAR respectively.
293
- jssh_command += "var #{DOCUMENT_VAR} = #{BROWSER_VAR}.contentDocument;"
294
- jssh_command += "var #{BODY_VAR} = #{DOCUMENT_VAR}.body;"
295
-
296
- $jssh_socket.send("#{jssh_command}\n", 0)
297
- read_socket()
298
-
299
- # Get window and window's parent title and url
300
- $jssh_socket.send("#{DOCUMENT_VAR}.title;\n", 0)
301
- @window_title = read_socket()
302
- $jssh_socket.send("#{DOCUMENT_VAR}.URL;\n", 0)
303
- @window_url = read_socket()
304
- end
305
- private :set_browser_document
306
-
307
- #
308
- # Description:
309
- # Closes the window.
310
- #
311
- def close()
312
- #puts "current window number is : #{@@current_window}"
313
- # Derek Berner 5/16/08
314
- # Try to join thread only if there is exactly one open window
315
- if js_eval("getWindows().length").to_i == 1
316
- $jssh_socket.send(" getWindows()[0].close(); \n", 0)
317
- @t.join if @t != nil
318
- #sleep 5
319
- else
320
- # Check if window exists, because there may be the case that it has been closed by click event on some element.
321
- # For e.g: Close Button, Close this Window link etc.
322
- window_number = find_window("url", @window_url)
323
-
324
- # If matching window found. Close the window.
325
- if(window_number > 0)
326
- $jssh_socket.send(" getWindows()[#{window_number}].close();\n", 0)
327
- read_socket();
328
- end
329
-
330
- #Get the parent window url from the stack and return that window.
331
- #@@current_window = @@window_stack.pop()
332
- @window_url = @@window_stack.pop()
333
- @window_title = @@window_stack.pop()
334
- # Find window with this url.
335
- window_number = find_window("url", @window_url)
336
- @@current_window = window_number
337
- set_browser_document()
338
- end
339
- end
340
-
341
- #
342
- # Description:
343
- # Used for attaching pop up window to an existing Firefox window, either by url or title.
344
- # ff.attach(:url, 'http://www.google.com')
345
- # ff.attach(:title, 'Google')
346
- #
347
- # Output:
348
- # Instance of newly attached window.
349
- #
350
- def attach(how, what)
351
- window_number = find_window(how, what)
352
-
353
- if(window_number == 0)
354
- raise NoMatchingWindowFoundException.new("Unable to locate window, using #{how} and #{what}")
355
- elsif(window_number > 0)
356
- # Push the window_title and window_url of parent window. So that when we close the child window
357
- # appropriate handle of parent window is returned back.
358
- @@window_stack.push(@window_title)
359
- @@window_stack.push(@window_url)
360
-
361
- @@current_window = window_number.to_i
362
- set_browser_document()
363
- end
364
- self
365
- end
366
-
367
- #
368
- # Description:
369
- # Finds a Firefox browser window with a given title or url.
370
- #
371
- def find_window(how, what)
372
- jssh_command = "getWindows().length;";
373
- $jssh_socket.send("#{jssh_command}\n", 0)
374
- @@total_windows = read_socket()
375
- #puts "total windows are : " + @@total_windows.to_s
376
-
377
- jssh_command = "var windows = getWindows(); var window_number = 0;var found = false;
378
- for(var i = 0; i < windows.length; i++)
379
- {
380
- var attribute = '';
381
- if(\"#{how}\" == \"url\")
382
- {
383
- attribute = windows[i].getBrowser().contentDocument.URL;
384
- }
385
- if(\"#{how}\" == \"title\")
386
- {
387
- attribute = windows[i].getBrowser().contentDocument.title;
388
- }"
389
- if(what.class == Regexp)
390
- # Construct the regular expression because we can't use it directly by converting it to string.
391
- # If reg ex is /Google/i then its string conversion will be (?i-mx:Google) so we can't use it.
392
- # Construct the regular expression again from the string conversion.
393
- oldRegExp = what.to_s
394
- newRegExp = "/" + what.source + "/"
395
- flags = oldRegExp.slice(2, oldRegExp.index(':') - 2)
396
-
397
- for i in 0..flags.length do
398
- flag = flags[i, 1]
399
- if(flag == '-')
400
- break;
401
- else
402
- newRegExp << flag
403
- end
404
- end
405
-
406
- jssh_command += "var regExp = new RegExp(#{newRegExp});
407
- found = regExp.test(attribute);"
408
- else
409
- jssh_command += "found = (attribute == \"#{what}\");"
410
- end
411
-
412
- jssh_command += "if(found)
413
- {
414
- window_number = i;
415
- break;
416
- }
417
- }
418
- window_number;"
419
-
420
- jssh_command.gsub!(/\n/, "")
421
- #puts "jssh_command is : #{jssh_command}"
422
- $jssh_socket.send("#{jssh_command}\n", 0)
423
- window_number = read_socket()
424
- #puts "window number is : " + window_number.to_s
425
-
426
- return window_number.to_i
427
- end
428
- private :find_window
429
-
430
- #
431
- # Description:
432
- # Matches the given text with the current text shown in the browser.
433
- #
434
- # Input:
435
- # target - Text to match. Can be a string or regex
436
- #
437
- # Output:
438
- # Returns the index if the specified text was found.
439
- # Returns matchdata object if the specified regexp was found.
440
- #
441
- def contains_text(target)
442
- #puts "Text to match is : #{match_text}"
443
- #puts "Html is : #{self.text}"
444
- case target
445
- when Regexp
446
- self.text.match(target)
447
- when String
448
- self.text.index(target)
449
- else
450
- raise ArgumentError, "Argument #{target} should be a string or regexp."
451
- end
452
- end
453
-
454
- #
455
- # Description:
456
- # Returns the url of the page currently loaded in the browser.
457
- #
458
- # Output:
459
- # URL of the page.
460
- #
461
- def url()
462
- @window_url
463
- end
464
-
465
- #
466
- # Description:
467
- # Returns the title of the page currently loaded in the browser.
468
- #
469
- # Output:
470
- # Title of the page.
471
- #
472
- def title()
473
- @window_title
474
- end
475
-
476
- #
477
- # Description:
478
- # Returns the html of the page currently loaded in the browser.
479
- #
480
- # Output:
481
- # HTML shown on the page.
482
- #
483
- def html()
484
- $jssh_socket.send("var htmlelem = #{DOCUMENT_VAR}.getElementsByTagName('html')[0]; htmlelem.innerHTML;\n", 0)
485
- #$jssh_socket.send("#{BODY_VAR}.innerHTML;\n", 0)
486
- result = read_socket()
487
- return "<html>" + result + "</html>"
488
- end
489
-
490
- #
491
- # Description:
492
- # Returns the text of the page currently loaded in the browser.
493
- #
494
- # Output:
495
- # Text shown on the page.
496
- #
497
- def text()
498
- $jssh_socket.send("#{BODY_VAR}.textContent;\n", 0)
499
- return read_socket().strip
500
- end
501
-
502
- #
503
- # Description:
504
- # Maximize the current browser window.
505
- #
506
- def maximize()
507
- $jssh_socket.send("#{WINDOW_VAR}.maximize();\n", 0)
508
- read_socket()
509
- end
510
-
511
- #
512
- # Description:
513
- # Minimize the current browser window.
514
- #
515
- def minimize()
516
- $jssh_socket.send("#{WINDOW_VAR}.minimize();\n", 0)
517
- read_socket()
518
- end
519
-
520
- #
521
- # Description:
522
- # Waits for the page to get loaded.
523
- #
524
- def wait(last_url = nil)
525
- #puts "In wait function "
526
- isLoadingDocument = ""
527
- start = Time.now
528
-
529
- while isLoadingDocument != "false"
530
- isLoadingDocument = js_eval("#{BROWSER_VAR}=#{WINDOW_VAR}.getBrowser(); #{BROWSER_VAR}.webProgress.isLoadingDocument;")
531
- #puts "Is browser still loading page: #{isLoadingDocument}"
532
-
533
- # Derek Berner 5/16/08
534
- # Raise an exception if the page fails to load
535
- if (Time.now - start) > 300
536
- raise "Page Load Timeout"
537
- end
538
- end
539
- # Derek Berner 5/16/08
540
- # If the redirect is to a download attachment that does not reload this page, this
541
- # method will loop forever. Therefore, we need to ensure that if this method is called
542
- # twice with the same URL, we simply accept that we're done.
543
- $jssh_socket.send("#{BROWSER_VAR}.contentDocument.URL;\n", 0)
544
- url = read_socket()
545
-
546
- if(url != last_url)
547
- # Check for Javascript redirect. As we are connected to Firefox via JSSh. JSSh
548
- # doesn't detect any javascript redirects so check it here.
549
- # If page redirects to itself that this code will enter in infinite loop.
550
- # So we currently don't wait for such a page.
551
- # wait variable in JSSh tells if we should wait more for the page to get loaded
552
- # or continue. -1 means page is not redirected. Anyother positive values means wait.
553
- jssh_command = "var wait = -1; var meta = null; meta = #{BROWSER_VAR}.contentDocument.getElementsByTagName('meta');
554
- if(meta != null)
555
- {
556
- var doc_url = #{BROWSER_VAR}.contentDocument.URL;
557
- for(var i=0; i< meta.length;++i)
558
- {
559
- var content = meta[i].content;
560
- var regex = new RegExp(\"^refresh$\", \"i\");
561
- if(regex.test(meta[i].httpEquiv))
562
- {
563
- var arrContent = content.split(';');
564
- var redirect_url = null;
565
- if(arrContent.length > 0)
566
- {
567
- if(arrContent.length > 1)
568
- redirect_url = arrContent[1];
569
-
570
- if(redirect_url != null)
571
- {
572
- regex = new RegExp(\"^.*\" + redirect_url + \"$\");
573
- if(!regex.test(doc_url))
574
- {
575
- wait = arrContent[0];
576
- }
577
- }
578
- break;
579
- }
580
- }
581
- }
582
- }
583
- wait;"
584
- #puts "command in wait is : #{jssh_command}"
585
- jssh_command = jssh_command.gsub(/\n/, "")
586
- $jssh_socket.send("#{jssh_command}; \n", 0)
587
- wait_time = read_socket();
588
- #puts "wait time is : #{wait_time}"
589
- begin
590
- wait_time = wait_time.to_i
591
- if(wait_time != -1)
592
- sleep(wait_time)
593
- # Call wait again. In case there are multiple redirects.
594
- $jssh_socket.send("#{BROWSER_VAR} = #{WINDOW_VAR}.getBrowser(); \n",0)
595
- read_socket()
596
- wait(url)
597
- end
598
- rescue
599
- end
600
- end
601
- set_browser_document()
602
- run_error_checks()
603
- return self
604
- end
605
-
606
- # Add an error checker that gets called on every page load.
607
- #
608
- # * checker - a Proc object
609
- def add_checker(checker)
610
- @error_checkers << checker
611
- end
612
-
613
- # Disable an error checker
614
- #
615
- # * checker - a Proc object that is to be disabled
616
- def disable_checker(checker)
617
- @error_checkers.delete(checker)
618
- end
619
-
620
- # Run the predefined error checks. This is automatically called on every page load.
621
- def run_error_checks
622
- @error_checkers.each { |e| e.call(self) }
623
- end
624
-
625
-
626
- #def jspopup_appeared(popupText = "", wait = 2)
627
- # winHelper = WindowHelper.new()
628
- # return winHelper.hasPopupAppeared(popupText, wait)
629
- #end
630
-
631
- #
632
- # Description:
633
- # Redefines the alert and confirm methods on the basis of button to be clicked.
634
- # This is done so that JSSh doesn't get blocked. You should use click_no_wait method before calling this function.
635
- #
636
- # Typical Usage:
637
- # ff.button(:id, "button").click_no_wait
638
- # ff.click_jspopup_button("OK")
639
- #
640
- # Input:
641
- # button - JavaScript button to be clicked. Values can be OK or Cancel
642
- #
643
- #def click_jspopup_button(button)
644
- # button = button.downcase
645
- # element = Element.new(nil)
646
- # element.click_js_popup(button)
647
- #end
648
-
649
- #
650
- # Description:
651
- # Tells FireWatir to click javascript button in case one comes after performing some action on an element. Matches
652
- # text of pop up with one if supplied as parameter. If text matches clicks the button else stop script execution until
653
- # pop up is dismissed by manual intervention.
654
- #
655
- # Input:
656
- # button - JavaScript button to be clicked. Values can be OK or Cancel
657
- # waitTime - Time to wait for pop up to come. Not used just for compatibility with Watir.
658
- # userInput - Not used just for compatibility with Watir
659
- # text - Text that should appear on pop up.
660
- #
661
- def startClicker(button, waitTime = 1, userInput = nil, text = nil)
662
- jssh_command = "var win = #{BROWSER_VAR}.contentWindow;"
663
- if(button =~ /ok/i)
664
- jssh_command += "var popuptext = '';
665
- var old_alert = win.alert;
666
- var old_confirm = win.confirm;
667
- win.alert = function(param) {"
668
- if(text != nil)
669
- jssh_command += "if(param == \"#{text}\") {
670
- popuptext = param;
671
- return true;
672
- }
673
- else {
674
- popuptext = param;
675
- win.alert = old_alert;
676
- win.alert(param);
677
- }"
678
- else
679
- jssh_command += "popuptext = param; return true;"
680
- end
681
- jssh_command += "};
682
- win.confirm = function(param) {"
683
- if(text != nil)
684
- jssh_command += "if(param == \"#{text}\") {
685
- popuptext = param;
686
- return true;
687
- }
688
- else {
689
- win.confirm = old_confirm;
690
- win.confirm(param);
691
- }"
692
- else
693
- jssh_command += "popuptext = param; return true;"
694
- end
695
- jssh_command += "};"
696
-
697
- elsif(button =~ /cancel/i)
698
- jssh_command = "var old_confirm = win.confirm;
699
- win.confirm = function(param) {"
700
- if(text != nil)
701
- jssh_command += "if(param == \"#{text}\") {
702
- popuptext = param;
703
- return false;
704
- }
705
- else {
706
- win.confirm = old_confirm;
707
- win.confirm(param);
708
- }"
709
- else
710
- jssh_command += "popuptext = param; return false;"
711
- end
712
- jssh_command += "};"
713
- end
714
- jssh_command.gsub!(/\n/, "")
715
- #puts "jssh command sent for js pop up is : #{jssh_command}"
716
- $jssh_socket.send("#{jssh_command}\n", 0)
717
- read_socket()
718
- end
719
-
720
- #
721
- # Description:
722
- # Returns text of javascript pop up in case it comes.
723
- #
724
- # Output:
725
- # Text shown in javascript pop up.
726
- #
727
- def get_popup_text()
728
- $jssh_socket.send("popuptext;\n", 0)
729
- return_value = read_socket()
730
- # reset the variable
731
- $jssh_socket.send("popuptext = '';\n", 0)
732
- read_socket()
733
- return return_value
734
- end
735
-
736
- #
737
- # Description:
738
- # Returns the document element of the page currently loaded in the browser.
739
- #
740
- # Output:
741
- # Document element.
742
- #
743
- def document
744
- Document.new("#{DOCUMENT_VAR}")
745
- end
746
-
747
- #
748
- # Description:
749
- # Returns the first element that matches the xpath query.
750
- #
751
- # Input:
752
- # Xpath expression or query.
753
- #
754
- # Output:
755
- # Element matching the xpath query.
756
- #
757
- def element_by_xpath(xpath)
758
- temp = Element.new(nil, self)
759
- element_name = temp.element_by_xpath(self, xpath)
760
- return element_factory(element_name)
761
- end
762
-
763
- #
764
- # Description:
765
- # Factory method to create object of correct Element class while using XPath to get the element.
766
- #
767
- def element_factory(element_name)
768
- jssh_type = Element.new(element_name,self).element_type
769
- #puts "jssh type is : #{jssh_type}" # DEBUG
770
- candidate_class = jssh_type =~ /HTML(.*)Element/ ? $1 : ''
771
- #puts candidate_class # DEBUG
772
- if candidate_class == 'Input'
773
- $jssh_socket.send("#{element_name}.type;\n", 0)
774
- input_type = read_socket().downcase.strip
775
- puts input_type # DEBUG
776
- firewatir_class = input_class(input_type)
777
- else
778
- firewatir_class = jssh2firewatir(candidate_class)
779
- end
780
-
781
- #puts firewatir_class # DEBUG
782
- klass = FireWatir.const_get(firewatir_class)
783
-
784
- if klass == Element
785
- klass.new(element_name,self)
786
- elsif klass == CheckBox
787
- klass.new(self,:jssh_name,element_name,["checkbox"])
788
- elsif klass == Radio
789
- klass.new(self,:jssh_name,element_name,["radio"])
790
- else
791
- klass.new(self,:jssh_name,element_name)
792
- end
793
- end
794
- private :element_factory
795
-
796
- #
797
- # Description:
798
- # Get the class name for element of input type depending upon its type like checkbox, radio etc.
799
- #
800
- def input_class(input_type)
801
- hash = {
802
- 'select-one' => 'SelectList',
803
- 'select-multiple' => 'SelectList',
804
- 'text' => 'TextField',
805
- 'password' => 'TextField',
806
- 'textarea' => 'TextField',
807
- # TODO when there's no type, it's a TextField
808
- 'file' => 'FileField',
809
- 'checkbox' => 'CheckBox',
810
- 'radio' => 'Radio',
811
- 'reset' => 'Button',
812
- 'button' => 'Button',
813
- 'submit' => 'Button',
814
- 'image' => 'Button'
815
- }
816
- hash.default = 'Element'
817
-
818
- hash[input_type]
819
- end
820
- private :input_class
821
-
822
- #
823
- # Description:
824
- # Converts element type returned by JSSh like HTMLDivElement to its corresponding class in Firewatir.
825
- #
826
- def jssh2firewatir(candidate_class)
827
- hash = {
828
- 'Div' => 'Div',
829
- 'Button' => 'Button',
830
- 'Frame' => 'Frame',
831
- 'Span' => 'Span',
832
- 'Paragraph' => 'P',
833
- 'Label' => 'Label',
834
- 'Form' => 'Form',
835
- 'Image' => 'Image',
836
- 'Table' => 'Table',
837
- 'TableCell' => 'TableCell',
838
- 'TableRow' => 'TableRow',
839
- 'Select' => 'SelectList',
840
- 'Link' => 'Link',
841
- 'Anchor' => 'Link' # FIXME is this right?
842
- #'Option' => 'Option' #Option uses a different constructor
843
- }
844
- hash.default = 'Element'
845
- hash[candidate_class]
846
- end
847
- private :jssh2firewatir
848
-
849
- #
850
- # Description:
851
- # Returns the array of elements that matches the xpath query.
852
- #
853
- # Input:
854
- # Xpath expression or query.
855
- #
856
- # Output:
857
- # Array of elements matching xpath query.
858
- #
859
- def elements_by_xpath(xpath)
860
- element = Element.new(nil, self)
861
- elem_names = element.elements_by_xpath(self, xpath)
862
- elem_names.inject([]) {|elements,name| elements << element_factory(name)}
863
- end
864
-
865
- #
866
- # Description:
867
- # Show all the forms available on the page.
868
- #
869
- # Output:
870
- # Name, id, method and action of all the forms available on the page.
871
- #
872
- def show_forms
873
- forms = Document.new(self).get_forms()
874
- count = forms.length
875
- puts "There are #{count} forms"
876
- for i in 0..count - 1 do
877
- puts "Form name: " + forms[i].name
878
- puts " id: " + forms[i].id
879
- puts " method: " + forms[i].attribute_value("method")
880
- puts " action: " + forms[i].action
881
- end
882
- end
883
- alias showForms show_forms
884
-
885
- #
886
- # Description:
887
- # Show all the images available on the page.
888
- #
889
- # Output:
890
- # Name, id, src and index of all the images available on the page.
891
- #
892
- def show_images
893
- images = Document.new(self).get_images
894
- puts "There are #{images.length} images"
895
- index = 1
896
- images.each do |l|
897
- puts "image: name: #{l.name}"
898
- puts " id: #{l.id}"
899
- puts " src: #{l.src}"
900
- puts " index: #{index}"
901
- index += 1
902
- end
903
- end
904
- alias showImages show_images
905
-
906
- #
907
- # Description:
908
- # Show all the links available on the page.
909
- #
910
- # Output:
911
- # Name, id, href and index of all the links available on the page.
912
- #
913
- def show_links
914
- links = Document.new(self).get_links
915
- puts "There are #{links.length} links"
916
- index = 1
917
- links.each do |l|
918
- puts "link: name: #{l.name}"
919
- puts " id: #{l.id}"
920
- puts " href: #{l.href}"
921
- puts " index: #{index}"
922
- index += 1
923
- end
924
- end
925
- alias showLinks show_links
926
-
927
- #
928
- # Description:
929
- # Show all the divs available on the page.
930
- #
931
- # Output:
932
- # Name, id, class and index of all the divs available on the page.
933
- #
934
- def show_divs
935
- divs = Document.new(self).get_divs
936
- puts "There are #{divs.length} divs"
937
- index = 1
938
- divs.each do |l|
939
- puts "div: name: #{l.name}"
940
- puts " id: #{l.id}"
941
- puts " class: #{l.className}"
942
- puts " index: #{index}"
943
- index += 1
944
- end
945
- end
946
- alias showDivs show_divs
947
-
948
- #
949
- # Description:
950
- # Show all the tables available on the page.
951
- #
952
- # Output:
953
- # Id, row count, column count (only first row) and index of all the tables available on the page.
954
- #
955
- def show_tables
956
- tables = Document.new(self).get_tables
957
- puts "There are #{tables.length} tables"
958
- index = 1
959
- tables.each do |l|
960
- puts "table: id: #{l.id}"
961
- puts " rows: #{l.row_count}"
962
- puts " columns: #{l.column_count}"
963
- puts " index: #{index}"
964
- index += 1
965
- end
966
- end
967
- alias showTables show_tables
968
-
969
- #
970
- # Description:
971
- # Show all the pre elements available on the page.
972
- #
973
- # Output:
974
- # Id, name and index of all the pre elements available on the page.
975
- #
976
- def show_pres
977
- pres = Document.new(self).get_pres
978
- puts "There are #{pres.length} pres"
979
- index = 1
980
- pres.each do |l|
981
- puts "pre: id: #{l.id}"
982
- puts " name: #{l.name}"
983
- puts " index: #{index}"
984
- index += 1
985
- end
986
- end
987
- alias showPres show_pres
988
-
989
- #
990
- # Description:
991
- # Show all the spans available on the page.
992
- #
993
- # Output:
994
- # Name, id, class and index of all the spans available on the page.
995
- #
996
- def show_spans
997
- spans = Document.new(self).get_spans
998
- puts "There are #{spans.length} spans"
999
- index = 1
1000
- spans.each do |l|
1001
- puts "span: name: #{l.name}"
1002
- puts " id: #{l.id}"
1003
- puts " class: #{l.className}"
1004
- puts " index: #{index}"
1005
- index += 1
1006
- end
1007
- end
1008
- alias showSpans show_spans
1009
-
1010
- #
1011
- # Description:
1012
- # Show all the labels available on the page.
1013
- #
1014
- # Output:
1015
- # Name, id, for and index of all the labels available on the page.
1016
- #
1017
- def show_labels
1018
- labels = Document.new(self).get_labels
1019
- puts "There are #{labels.length} labels"
1020
- index = 1
1021
- labels.each do |l|
1022
- puts "label: name: #{l.name}"
1023
- puts " id: #{l.id}"
1024
- puts " for: #{l.for}"
1025
- puts " index: #{index}"
1026
- index += 1
1027
- end
1028
- end
1029
- alias showLabels show_labels
1030
-
1031
- #
1032
- # Description:
1033
- # Show all the frames available on the page. Doesn't show nested frames.
1034
- #
1035
- # Output:
1036
- # Name, and index of all the frames available on the page.
1037
- #
1038
- def show_frames
1039
- jssh_command = "var frameset = #{WINDOW_VAR}.frames;
1040
- var elements_frames = new Array();
1041
- for(var i = 0; i < frameset.length; i++)
1042
- {
1043
- var frames = frameset[i].frames;
1044
- for(var j = 0; j < frames.length; j++)
1045
- {
1046
- elements_frames.push(frames[j].frameElement);
1047
- }
1048
- }
1049
- elements_frames.length;"
1050
-
1051
- jssh_command.gsub!("\n", "")
1052
- $jssh_socket.send("#{jssh_command};\n", 0)
1053
- length = read_socket().to_i
1054
-
1055
- puts "There are #{length} frames"
1056
-
1057
- frames = Array.new(length)
1058
- for i in 0..length - 1 do
1059
- frames[i] = Frame.new(self, :jssh_name, "elements_frames[#{i}]")
1060
- end
1061
-
1062
- for i in 0..length - 1 do
1063
- puts "frame: name: #{frames[i].name}"
1064
- puts " index: #{i+1}"
1065
- end
1066
- end
1067
- alias showFrames show_frames
1068
-
1069
- # 5/16/08 Derek Berner
1070
- # Wrapper method to send JS commands concisely,
1071
- # and propagate errors
1072
- def js_eval(str)
1073
- #puts "JS Eval: #{str}"
1074
- $jssh_socket.send("#{str};\n",0)
1075
- value = read_socket()
1076
- if md=/^(\w+)Error:(.*)$/.match(value)
1077
- eval "class JS#{md[1]}Error\nend"
1078
- raise (eval "JS#{md[1]}Error"), md[2]
1079
- end
1080
- #puts "Value: #{value}"
1081
- value
1082
- end
1083
-
1084
- end # Class Firefox
1085
-
1086
- #
1087
- # Module for handling the Javascript pop-ups. Not in use currently, will be available in future.
1088
- # Use ff.startClicker() method for clicking javascript pop ups. Refer to unit tests on how to handle
1089
- # javascript pop up (unittests/javascript_test.rb)
1090
- #module Dialog
1091
- # # Class for handling javascript popup. Not in use currently, will be available in future. See unit tests on how to handle
1092
- # # javascript pop up (unittests/javascript_test.rb).
1093
- # class JSPopUp
1094
- # include Container
1095
- #
1096
- # def has_appeared(text)
1097
- # require 'socket'
1098
- # sleep 4
1099
- # shell = TCPSocket.new("localhost", 9997)
1100
- # read_socket(shell)
1101
- # #jssh_command = "var url = #{DOCUMENT_VAR}.URL;"
1102
- # jssh_command = "var length = getWindows().length; var win;length;\n"
1103
- # #jssh_command += "for(var i = 0; i < length; i++)"
1104
- # #jssh_command += "{"
1105
- # #jssh_command += " win = getWindows()[i];"
1106
- # #jssh_command += " if(win.opener != null && "
1107
- # #jssh_command += " win.title == \"[JavaScript Application]\" &&"
1108
- # #jssh_command += " win.opener.document.URL == url)"
1109
- # #jssh_command += " {"
1110
- # #jssh_command += " break;"
1111
- # #jssh_command += " }"
1112
- # #jssh_command += "}"
1113
- #
1114
- # #jssh_command += " win.title;\n";
1115
- # #jssh_command += "var dialog = win.document.childNodes[0];"
1116
- # #jssh_command += "vbox = dialog.childNodes[1].childNodes[1];"
1117
- # #jssh_command += "vbox.childNodes[1].childNodes[0].childNodes[0].textContent;\n"
1118
- # puts jssh_command
1119
- # shell.send("#{jssh_command}", 0)
1120
- # jstext = read_socket(shell)
1121
- # puts jstext
1122
- # return jstext == text
1123
- # end
1124
- # end
1125
- #end
1126
-
1127
- end
1
+ =begin rdoc
2
+ This is FireWatir, Web Application Testing In Ruby using Firefox browser
3
+
4
+ Typical usage:
5
+ # include the controller
6
+ require "firewatir"
7
+
8
+ # go to the page you want to test
9
+ ff = FireWatir::Firefox.start("http://myserver/mypage")
10
+
11
+ # enter "Angrez" into an input field named "username"
12
+ ff.text_field(:name, "username").set("Angrez")
13
+
14
+ # enter "Ruby Co" into input field with id "company_ID"
15
+ ff.text_field(:id, "company_ID").set("Ruby Co")
16
+
17
+ # click on a link that has "green" somewhere in the text that is displayed
18
+ # to the user, using a regular expression
19
+ ff.link(:text, /green/)
20
+
21
+ # click button that has a caption of "Cancel"
22
+ ff.button(:value, "Cancel").click
23
+
24
+ FireWatir allows your script to read and interact with HTML objects--HTML tags
25
+ and their attributes and contents. Types of objects that FireWatir can identify
26
+ include:
27
+
28
+ Type Description
29
+ =========== ===============================================================
30
+ button <input> tags, with the type="button" attribute
31
+ check_box <input> tags, with the type="checkbox" attribute
32
+ div <div> tags
33
+ form
34
+ frame
35
+ hidden hidden <input> tags
36
+ image <img> tags
37
+ label
38
+ link <a> (anchor) tags
39
+ p <p> (paragraph) tags
40
+ radio radio buttons; <input> tags, with the type="radio" attribute
41
+ select_list <select> tags, known informally as drop-down boxes
42
+ span <span> tags
43
+ table <table> tags
44
+ text_field <input> tags with the type="text" attribute (a single-line
45
+ text field), the type="text_area" attribute (a multi-line
46
+ text field), and the type="password" attribute (a
47
+ single-line field in which the input is replaced with asterisks)
48
+
49
+ In general, there are several ways to identify a specific object. FireWatir's
50
+ syntax is in the form (how, what), where "how" is a means of identifying
51
+ the object, and "what" is the specific string or regular expression
52
+ that FireWatir will seek, as shown in the examples above. Available "how"
53
+ options depend upon the type of object, but here are a few examples:
54
+
55
+ How Description
56
+ ============ ===============================================================
57
+ :id Used to find an object that has an "id=" attribute. Since each
58
+ id should be unique, according to the XHTML specification,
59
+ this is recommended as the most reliable method to find an
60
+ object.
61
+ :name Used to find an object that has a "name=" attribute. This is
62
+ useful for older versions of HTML, but "name" is deprecated
63
+ in XHTML.
64
+ :value Used to find a text field with a given default value, or a
65
+ button with a given caption
66
+ :index Used to find the nth object of the specified type on a page.
67
+ For example, button(:index, 2) finds the second button.
68
+ Current versions of FireWatir use 1-based indexing, but future
69
+ versions will use 0-based indexing.
70
+ :xpath The xpath expression for identifying the element.
71
+
72
+ Note that the XHTML specification requires that tags and their attributes be
73
+ in lower case. FireWatir doesn't enforce this; FireWatir will find tags and
74
+ attributes whether they're in upper, lower, or mixed case. This is either
75
+ a bug or a feature.
76
+
77
+ FireWatir uses JSSh for interacting with the browser. For further information on
78
+ Firefox and DOM go to the following Web page:
79
+
80
+ http://www.xulplanet.com/references/objref/
81
+
82
+ =end
83
+
84
+ module FireWatir
85
+ include Watir::Exception
86
+
87
+ class Firefox
88
+ include FireWatir::Container
89
+
90
+ # XPath Result type. Return only first node that matches the xpath expression.
91
+ # More details: "http://developer.mozilla.org/en/docs/DOM:document.evaluate"
92
+ FIRST_ORDERED_NODE_TYPE = 9
93
+
94
+ # Description:
95
+ # Starts the firefox browser.
96
+ # On windows this starts the first version listed in the registry.
97
+ #
98
+ # Input:
99
+ # options - Hash of any of the following options:
100
+ # :waitTime - Time to wait for Firefox to start. By default it waits for 2 seconds.
101
+ # This is done because if Firefox is not started and we try to connect
102
+ # to jssh on port 9997 an exception is thrown.
103
+ # :profile - The Firefox profile to use. If none is specified, Firefox will use
104
+ # the last used profile.
105
+ # :suppress_launch_process - do not create a new firefox process. Connect to an existing one.
106
+
107
+ # TODO: Start the firefox version given by user.
108
+
109
+ def initialize(options = {})
110
+ if(options.kind_of?(Integer))
111
+ options = {:waitTime => options}
112
+ end
113
+
114
+ # check for jssh not running, firefox may be open but not with -jssh
115
+ # if its not open at all, regardless of the :suppress_launch_process option start it
116
+ # error if running without jssh, we don't want to kill their current window (mac only)
117
+ jssh_down = false
118
+ begin
119
+ set_defaults()
120
+ rescue Watir::Exception::UnableToStartJSShException
121
+ jssh_down = true
122
+ end
123
+
124
+ if current_os == :macosx && !%x{ps x | grep firefox-bin | grep -v grep}.empty?
125
+ raise "Firefox is running without -jssh" if jssh_down
126
+ open_window unless options[:suppress_launch_process]
127
+ elsif not options[:suppress_launch_process]
128
+ launch_browser(options)
129
+ end
130
+
131
+ set_defaults()
132
+ get_window_number()
133
+ set_browser_document()
134
+ end
135
+
136
+ def inspect
137
+ '#<%s:0x%x url=%s title=%s>' % [self.class, hash*2, url.inspect, title.inspect]
138
+ end
139
+
140
+
141
+ # Launches firebox browser
142
+ # options as .new
143
+
144
+ def launch_browser(options = {})
145
+
146
+ if(options[:profile])
147
+ profile_opt = "-no-remote -P #{options[:profile]}"
148
+ else
149
+ profile_opt = ""
150
+ end
151
+
152
+ bin = path_to_bin()
153
+ @t = Thread.new { system("#{bin} -jssh #{profile_opt}") }
154
+ sleep options[:waitTime] || 2
155
+
156
+ end
157
+ private :launch_browser
158
+
159
+ # Creates a new instance of Firefox. Loads the URL and return the instance.
160
+ # Input:
161
+ # url - url of the page to be loaded.
162
+ def self.start(url)
163
+ ff = Firefox.new
164
+ ff.goto(url)
165
+ return ff
166
+ end
167
+
168
+ # Gets the window number opened.
169
+ # Currently, this returns the most recently opened window, which may or may
170
+ # not be the current window.
171
+ def get_window_number()
172
+ # If at any time a non-browser window like the "Downloads" window
173
+ # pops up, it will become the topmost window, so make sure we
174
+ # ignore it.
175
+ window_count = js_eval("getWindows().length").to_i - 1
176
+ while js_eval("getWindows()[#{window_count}].getBrowser") == ''
177
+ window_count -= 1;
178
+ end
179
+
180
+ # now correctly handles instances where only browserless windows are open
181
+ # opens one we can use if count is 0
182
+
183
+ if window_count < 0
184
+ open_window
185
+ window_count = 1
186
+ end
187
+ @window_index = window_count
188
+ end
189
+ private :get_window_number
190
+
191
+ # Loads the given url in the browser. Waits for the page to get loaded.
192
+ def goto(url)
193
+ get_window_number()
194
+ set_browser_document()
195
+ js_eval "#{browser_var}.loadURI(\"#{url}\")"
196
+ wait()
197
+ end
198
+
199
+ # Loads the previous page (if there is any) in the browser. Waits for the page to get loaded.
200
+ def back()
201
+ js_eval "if(#{browser_var}.canGoBack) #{browser_var}.goBack()"
202
+ wait()
203
+ end
204
+
205
+ # Loads the next page (if there is any) in the browser. Waits for the page to get loaded.
206
+ def forward()
207
+ js_eval "if(#{browser_var}.canGoForward) #{browser_var}.goForward()"
208
+ wait()
209
+ end
210
+
211
+ # Reloads the current page in the browser. Waits for the page to get loaded.
212
+ def refresh()
213
+ js_eval "#{browser_var}.reload()"
214
+ wait()
215
+ end
216
+
217
+ # Executes the given JavaScript string
218
+ def execute_script(source)
219
+ result = js_eval source.to_s
220
+ wait()
221
+
222
+ result
223
+ end
224
+
225
+ private
226
+ # This function creates a new socket at port 9997 and sets the default values for instance and class variables.
227
+ # Generatesi UnableToStartJSShException if cannot connect to jssh even after 3 tries.
228
+ def set_defaults(no_of_tries = 0)
229
+ # JSSH listens on port 9997. Create a new socket to connect to port 9997.
230
+ begin
231
+ $jssh_socket = TCPSocket::new(MACHINE_IP, "9997")
232
+ $jssh_socket.sync = true
233
+ read_socket()
234
+ rescue
235
+ no_of_tries += 1
236
+ retry if no_of_tries < 3
237
+ raise UnableToStartJSShException, "Unable to connect to machine : #{MACHINE_IP} on port 9997. Make sure that JSSh is properly installed and Firefox is running with '-jssh' option"
238
+ end
239
+ @error_checkers = []
240
+ end
241
+
242
+ # Sets the document, window and browser variables to point to correct object in JSSh.
243
+ def set_browser_document
244
+ # Add eventlistener for browser window so that we can reset the document back whenever there is redirect
245
+ # or browser loads on its own after some time. Useful when you are searching for flight results etc and
246
+ # page goes to search page after that it goes automatically to results page.
247
+ # Details : http://zenit.senecac.on.ca/wiki/index.php/Mozilla.dev.tech.xul#What_is_an_example_of_addProgressListener.3F
248
+ jssh_command = "var listObj = new Object();"; # create new object
249
+ jssh_command << "listObj.wpl = Components.interfaces.nsIWebProgressListener;"; # set the web progress listener.
250
+ jssh_command << "listObj.QueryInterface = function(aIID) {
251
+ if (aIID.equals(listObj.wpl) ||
252
+ aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
253
+ aIID.equals(Components.interfaces.nsISupports))
254
+ return this;
255
+ throw Components.results.NS_NOINTERFACE;
256
+ };" # set function to locate the object via QueryInterface
257
+ jssh_command << "listObj.onStateChange = function(aProgress, aRequest, aFlag, aStatus) {
258
+ if (aFlag & listObj.wpl.STATE_STOP) {
259
+ if ( aFlag & listObj.wpl.STATE_IS_NETWORK ) {
260
+ #{document_var} = #{browser_var}.contentDocument;
261
+ #{body_var} = #{document_var}.body;
262
+ }
263
+ }
264
+ };" # add function to be called when window state is change. When state is STATE_STOP &
265
+ # STATE_IS_NETWORK then only everything is loaded. Now we can reset our variables.
266
+ jssh_command.gsub!(/\n/, "")
267
+ js_eval jssh_command
268
+
269
+ jssh_command = "var #{window_var} = getWindows()[#{@window_index}];"
270
+ jssh_command << "var #{browser_var} = #{window_var}.getBrowser();"
271
+ # Add listener create above to browser object
272
+ jssh_command << "#{browser_var}.addProgressListener( listObj,Components.interfaces.nsIWebProgress.NOTIFY_STATE_WINDOW );"
273
+ jssh_command << "var #{document_var} = #{browser_var}.contentDocument;"
274
+ jssh_command << "var #{body_var} = #{document_var}.body;"
275
+ js_eval jssh_command
276
+
277
+ @window_title = js_eval "#{document_var}.title"
278
+ @window_url = js_eval "#{document_var}.URL"
279
+ end
280
+
281
+ public
282
+ def window_var
283
+ "window"
284
+ end
285
+ #private
286
+ def browser_var
287
+ "browser"
288
+ end
289
+ def document_var # unfinished
290
+ "document"
291
+ end
292
+ def body_var # unfinished
293
+ "body"
294
+ end
295
+
296
+ public
297
+ # Closes the window.
298
+ def close
299
+
300
+ if js_eval("getWindows().length").to_i == 1
301
+ js_eval("getWindows()[0].close()")
302
+
303
+ if current_os == :macosx
304
+ %x{ osascript -e 'tell application "Firefox" to quit' }
305
+ end
306
+
307
+ # wait for the app to close properly
308
+ @t.join if @t
309
+ else
310
+ # Check if window exists, because there may be the case that it has been closed by click event on some element.
311
+ # For e.g: Close Button, Close this Window link etc.
312
+ window_number = find_window(:url, @window_url)
313
+
314
+ # If matching window found. Close the window.
315
+ if window_number > 0
316
+ js_eval "getWindows()[#{window_number}].close()"
317
+ end
318
+
319
+ end
320
+ end
321
+
322
+ # Used for attaching pop up window to an existing Firefox window, either by url or title.
323
+ # ff.attach(:url, 'http://www.google.com')
324
+ # ff.attach(:title, 'Google')
325
+ #
326
+ # Output:
327
+ # Instance of newly attached window.
328
+ def attach(how, what)
329
+
330
+ $stderr.puts("warning: #{self.class}.attach is experimental") if $VERBOSE
331
+ window_number = find_window(how, what)
332
+
333
+ if(window_number.nil?)
334
+ raise NoMatchingWindowFoundException.new("Unable to locate window, using #{how} and #{what}")
335
+ elsif(window_number >= 0)
336
+ @window_index = window_number
337
+ set_browser_document()
338
+ end
339
+ self
340
+ end
341
+
342
+ # Class method to return a browser object if a window matches for how
343
+ # and what. Window can be referenced by url or title.
344
+ # The second argument can be either a string or a regular expression.
345
+ # Watir::Browser.attach(:url, 'http://www.google.com')
346
+ # Watir::Browser.attach(:title, 'Google')
347
+ def self.attach how, what
348
+ br = new :suppress_launch_process => true # don't create window
349
+ br.attach(how, what)
350
+ br
351
+ end
352
+
353
+ # loads up a new window in an existing process
354
+ # Watir::Browser.attach() with no arguments passed the attach method will create a new window
355
+ # this will only be called one time per instance we're only ever going to run in 1 window
356
+
357
+ def open_window
358
+
359
+ if @opened_new_window
360
+ return @opened_new_window
361
+ end
362
+
363
+ jssh_command = "var windows = getWindows(); var window = windows[0];
364
+ window.open();
365
+ var windows = getWindows(); var window_number = windows.length - 1;
366
+ window_number;"
367
+
368
+ window_number = js_eval(jssh_command).to_i
369
+ @opened_new_window = window_number
370
+ return window_number if window_number >= 0
371
+ end
372
+ private :open_window
373
+
374
+ # return the window index for the browser window with the given title or url.
375
+ # how - :url or :title
376
+ # what - string or regexp
377
+ # Start searching windows in reverse order so that we attach/find the latest opened window.
378
+ def find_window(how, what)
379
+ jssh_command = "var windows = getWindows(); var window_number = false; var found = false;
380
+ for(var i = windows.length - 1; i >= 0; i--)
381
+ {
382
+ var attribute = '';
383
+ if(typeof(windows[i].getBrowser) != 'function')
384
+ {
385
+ continue;
386
+ }
387
+ var browser = windows[i].getBrowser();
388
+ if(!browser)
389
+ {
390
+ continue;
391
+ }
392
+ if(\"#{how}\" == \"url\")
393
+ {
394
+ attribute = browser.contentDocument.URL;
395
+ }
396
+ if(\"#{how}\" == \"title\")
397
+ {
398
+ attribute = browser.contentDocument.title;
399
+ }"
400
+ if(what.class == Regexp)
401
+ jssh_command << "var regExp = new RegExp(#{what.inspect});
402
+ found = regExp.test(attribute);"
403
+ else
404
+ jssh_command << "found = (attribute == \"#{what}\");"
405
+ end
406
+
407
+ jssh_command << "if(found)
408
+ {
409
+ window_number = i;
410
+ break;
411
+ }
412
+ }
413
+ window_number;"
414
+ window_number = js_eval(jssh_command).to_s
415
+ return window_number == 'false' ? nil : window_number.to_i
416
+ end
417
+ private :find_window
418
+
419
+ #
420
+ # Description:
421
+ # Matches the given text with the current text shown in the browser.
422
+ #
423
+ # Input:
424
+ # target - Text to match. Can be a string or regex
425
+ #
426
+ # Output:
427
+ # Returns the index if the specified text was found.
428
+ # Returns matchdata object if the specified regexp was found.
429
+ #
430
+ def contains_text(target)
431
+ #puts "Text to match is : #{match_text}"
432
+ #puts "Html is : #{self.text}"
433
+ case target
434
+ when Regexp
435
+ self.text.match(target)
436
+ when String
437
+ self.text.index(target)
438
+ else
439
+ raise TypeError, "Argument #{target} should be a string or regexp."
440
+ end
441
+ end
442
+
443
+ # Returns the url of the page currently loaded in the browser.
444
+ def url
445
+ @window_url = js_eval "#{document_var}.location"
446
+ end
447
+
448
+ # Returns the title of the page currently loaded in the browser.
449
+ def title
450
+ @window_title = js_eval "#{document_var}.title"
451
+ end
452
+
453
+ # Returns the Status of the page currently loaded in the browser from statusbar.
454
+ #
455
+ # Output:
456
+ # Status of the page.
457
+ #
458
+ def status
459
+ js_status = js_eval("#{window_var}.status")
460
+ js_status.empty? ? js_eval("#{WINDOW_VAR}.XULBrowserWindow.statusText;") : js_status
461
+ end
462
+
463
+
464
+ # Returns the html of the page currently loaded in the browser.
465
+ def html
466
+ result = js_eval("var htmlelem = #{document_var}.getElementsByTagName('html')[0]; htmlelem.innerHTML")
467
+ return "<html>" + result + "</html>"
468
+ end
469
+
470
+ # Returns the text of the page currently loaded in the browser.
471
+ def text
472
+ js_eval("#{body_var}.textContent").strip
473
+ end
474
+
475
+ # Maximize the current browser window.
476
+ def maximize()
477
+ js_eval "#{window_var}.maximize()"
478
+ end
479
+
480
+ # Minimize the current browser window.
481
+ def minimize()
482
+ js_eval "#{window_var}.minimize()"
483
+ end
484
+
485
+ # Waits for the page to get loaded.
486
+ def wait(last_url = nil)
487
+ #puts "In wait function "
488
+ isLoadingDocument = ""
489
+ start = Time.now
490
+
491
+ while isLoadingDocument != "false"
492
+ isLoadingDocument = js_eval("#{browser_var}=#{window_var}.getBrowser(); #{browser_var}.webProgress.isLoadingDocument;")
493
+ #puts "Is browser still loading page: #{isLoadingDocument}"
494
+
495
+ # Raise an exception if the page fails to load
496
+ if (Time.now - start) > 300
497
+ raise "Page Load Timeout"
498
+ end
499
+ end
500
+ # If the redirect is to a download attachment that does not reload this page, this
501
+ # method will loop forever. Therefore, we need to ensure that if this method is called
502
+ # twice with the same URL, we simply accept that we're done.
503
+ url = js_eval("#{browser_var}.contentDocument.URL")
504
+
505
+ if(url != last_url)
506
+ # Check for Javascript redirect. As we are connected to Firefox via JSSh. JSSh
507
+ # doesn't detect any javascript redirects so check it here.
508
+ # If page redirects to itself that this code will enter in infinite loop.
509
+ # So we currently don't wait for such a page.
510
+ # wait variable in JSSh tells if we should wait more for the page to get loaded
511
+ # or continue. -1 means page is not redirected. Anyother positive values means wait.
512
+ jssh_command = "var wait = -1; var meta = null; meta = #{browser_var}.contentDocument.getElementsByTagName('meta');
513
+ if(meta != null)
514
+ {
515
+ var doc_url = #{browser_var}.contentDocument.URL;
516
+ for(var i=0; i< meta.length;++i)
517
+ {
518
+ var content = meta[i].content;
519
+ var regex = new RegExp(\"^refresh$\", \"i\");
520
+ if(regex.test(meta[i].httpEquiv))
521
+ {
522
+ var arrContent = content.split(';');
523
+ var redirect_url = null;
524
+ if(arrContent.length > 0)
525
+ {
526
+ if(arrContent.length > 1)
527
+ redirect_url = arrContent[1];
528
+
529
+ if(redirect_url != null)
530
+ {
531
+ regex = new RegExp(\"^.*\" + redirect_url + \"$\");
532
+ if(!regex.test(doc_url))
533
+ {
534
+ wait = arrContent[0];
535
+ }
536
+ }
537
+ break;
538
+ }
539
+ }
540
+ }
541
+ }
542
+ wait;"
543
+ wait_time = js_eval(jssh_command).to_i
544
+ begin
545
+ if(wait_time != -1)
546
+ sleep(wait_time)
547
+ # Call wait again. In case there are multiple redirects.
548
+ js_eval "#{browser_var} = #{window_var}.getBrowser()"
549
+ wait(url)
550
+ end
551
+ rescue
552
+ end
553
+ end
554
+ set_browser_document()
555
+ run_error_checks()
556
+ return self
557
+ end
558
+
559
+ # Add an error checker that gets called on every page load.
560
+ # * checker - a Proc object
561
+ def add_checker(checker)
562
+ @error_checkers << checker
563
+ end
564
+
565
+ # Disable an error checker
566
+ # * checker - a Proc object that is to be disabled
567
+ def disable_checker(checker)
568
+ @error_checkers.delete(checker)
569
+ end
570
+
571
+ # Run the predefined error checks. This is automatically called on every page load.
572
+ def run_error_checks
573
+ @error_checkers.each { |e| e.call(self) }
574
+ end
575
+
576
+
577
+ #def jspopup_appeared(popupText = "", wait = 2)
578
+ # winHelper = WindowHelper.new()
579
+ # return winHelper.hasPopupAppeared(popupText, wait)
580
+ #end
581
+
582
+ #
583
+ # Description:
584
+ # Redefines the alert and confirm methods on the basis of button to be clicked.
585
+ # This is done so that JSSh doesn't get blocked. You should use click_no_wait method before calling this function.
586
+ #
587
+ # Typical Usage:
588
+ # ff.button(:id, "button").click_no_wait
589
+ # ff.click_jspopup_button("OK")
590
+ #
591
+ # Input:
592
+ # button - JavaScript button to be clicked. Values can be OK or Cancel
593
+ #
594
+ #def click_jspopup_button(button)
595
+ # button = button.downcase
596
+ # element = Element.new(nil)
597
+ # element.click_js_popup(button)
598
+ #end
599
+
600
+ #
601
+ # Description:
602
+ # Tells FireWatir to click javascript button in case one comes after performing some action on an element. Matches
603
+ # text of pop up with one if supplied as parameter. If text matches clicks the button else stop script execution until
604
+ # pop up is dismissed by manual intervention.
605
+ #
606
+ # Input:
607
+ # button - JavaScript button to be clicked. Values can be OK or Cancel
608
+ # waitTime - Time to wait for pop up to come. Not used just for compatibility with Watir.
609
+ # userInput - Not used just for compatibility with Watir
610
+ # text - Text that should appear on pop up.
611
+ #
612
+ def startClicker(button, waitTime = 1, userInput = nil, text = nil)
613
+ jssh_command = "var win = #{browser_var}.contentWindow;"
614
+ if(button =~ /ok/i)
615
+ jssh_command << "var popuptext = '';
616
+ var old_alert = win.alert;
617
+ var old_confirm = win.confirm;
618
+ win.alert = function(param) {"
619
+ if(text != nil)
620
+ jssh_command << "if(param == \"#{text}\") {
621
+ popuptext = param;
622
+ return true;
623
+ }
624
+ else {
625
+ popuptext = param;
626
+ win.alert = old_alert;
627
+ win.alert(param);
628
+ }"
629
+ else
630
+ jssh_command << "popuptext = param; return true;"
631
+ end
632
+ jssh_command << "};
633
+ win.confirm = function(param) {"
634
+ if(text != nil)
635
+ jssh_command << "if(param == \"#{text}\") {
636
+ popuptext = param;
637
+ return true;
638
+ }
639
+ else {
640
+ win.confirm = old_confirm;
641
+ win.confirm(param);
642
+ }"
643
+ else
644
+ jssh_command << "popuptext = param; return true;"
645
+ end
646
+ jssh_command << "};"
647
+
648
+ elsif(button =~ /cancel/i)
649
+ jssh_command = "var old_confirm = win.confirm;
650
+ win.confirm = function(param) {"
651
+ if(text != nil)
652
+ jssh_command << "if(param == \"#{text}\") {
653
+ popuptext = param;
654
+ return false;
655
+ }
656
+ else {
657
+ win.confirm = old_confirm;
658
+ win.confirm(param);
659
+ }"
660
+ else
661
+ jssh_command << "popuptext = param; return false;"
662
+ end
663
+ jssh_command << "};"
664
+ end
665
+ js_eval jssh_command
666
+ end
667
+
668
+ #
669
+ # Description:
670
+ # Returns text of javascript pop up in case it comes.
671
+ #
672
+ # Output:
673
+ # Text shown in javascript pop up.
674
+ #
675
+ def get_popup_text()
676
+ return_value = js_eval "popuptext"
677
+ # reset the variable
678
+ js_eval "popuptext = ''"
679
+ return return_value
680
+ end
681
+
682
+ # Returns the document element of the page currently loaded in the browser.
683
+ def document
684
+ Document.new(self)
685
+ end
686
+
687
+ # Returns the first element that matches the given xpath expression or query.
688
+ def element_by_xpath(xpath)
689
+ temp = Element.new(nil, self)
690
+ element_name = temp.element_by_xpath(self, xpath)
691
+ return element_factory(element_name)
692
+ end
693
+
694
+ # Return object of correct Element class while using XPath to get the element.
695
+ def element_factory(element_name)
696
+ jssh_type = Element.new(element_name,self).element_type
697
+ #puts "jssh type is : #{jssh_type}" # DEBUG
698
+ candidate_class = jssh_type =~ /HTML(.*)Element/ ? $1 : ''
699
+ #puts candidate_class # DEBUG
700
+ if candidate_class == 'Input'
701
+ input_type = js_eval("#{element_name}.type").downcase.strip
702
+ firewatir_class = input_class(input_type)
703
+ else
704
+ firewatir_class = jssh2firewatir(candidate_class)
705
+ end
706
+
707
+ #puts firewatir_class # DEBUG
708
+ klass = FireWatir.const_get(firewatir_class)
709
+
710
+ if klass == Element
711
+ klass.new(element_name,self)
712
+ elsif klass == CheckBox
713
+ klass.new(self,:jssh_name,element_name,["checkbox"])
714
+ elsif klass == Radio
715
+ klass.new(self,:jssh_name,element_name,["radio"])
716
+ else
717
+ klass.new(self,:jssh_name,element_name)
718
+ end
719
+ end
720
+ private :element_factory
721
+
722
+ # Return the class name for element of input type depending upon its type like checkbox, radio etc.
723
+ def input_class(input_type)
724
+ hash = {
725
+ 'select-one' => 'SelectList',
726
+ 'select-multiple' => 'SelectList',
727
+ 'text' => 'TextField',
728
+ 'password' => 'TextField',
729
+ 'textarea' => 'TextField',
730
+ # TODO when there's no type, it's a TextField
731
+ 'file' => 'FileField',
732
+ 'checkbox' => 'CheckBox',
733
+ 'radio' => 'Radio',
734
+ 'reset' => 'Button',
735
+ 'button' => 'Button',
736
+ 'submit' => 'Button',
737
+ 'image' => 'Button'
738
+ }
739
+ hash.default = 'Element'
740
+
741
+ hash[input_type]
742
+ end
743
+ private :input_class
744
+
745
+ # For a provided element type returned by JSSh like HTMLDivElement,
746
+ # returns its corresponding class in Firewatir.
747
+ def jssh2firewatir(candidate_class)
748
+ hash = {
749
+ 'Div' => 'Div',
750
+ 'Button' => 'Button',
751
+ 'Frame' => 'Frame',
752
+ 'Span' => 'Span',
753
+ 'Paragraph' => 'P',
754
+ 'Label' => 'Label',
755
+ 'Form' => 'Form',
756
+ 'Image' => 'Image',
757
+ 'Table' => 'Table',
758
+ 'TableCell' => 'TableCell',
759
+ 'TableRow' => 'TableRow',
760
+ 'Select' => 'SelectList',
761
+ 'Link' => 'Link',
762
+ 'Anchor' => 'Link' # FIXME is this right?
763
+ #'Option' => 'Option' #Option uses a different constructor
764
+ }
765
+ hash.default = 'Element'
766
+ hash[candidate_class]
767
+ end
768
+ private :jssh2firewatir
769
+
770
+ #
771
+ # Description:
772
+ # Returns the array of elements that matches the xpath query.
773
+ #
774
+ # Input:
775
+ # Xpath expression or query.
776
+ #
777
+ # Output:
778
+ # Array of elements matching xpath query.
779
+ #
780
+ def elements_by_xpath(xpath)
781
+ element = Element.new(nil, self)
782
+ elem_names = element.elements_by_xpath(self, xpath)
783
+ elem_names.inject([]) {|elements,name| elements << element_factory(name)}
784
+ end
785
+
786
+ #
787
+ # Description:
788
+ # Show all the forms available on the page.
789
+ #
790
+ # Output:
791
+ # Name, id, method and action of all the forms available on the page.
792
+ #
793
+ def show_forms
794
+ forms = Document.new(self).get_forms()
795
+ count = forms.length
796
+ puts "There are #{count} forms"
797
+ for i in 0..count - 1 do
798
+ puts "Form name: " + forms[i].name
799
+ puts " id: " + forms[i].id
800
+ puts " method: " + forms[i].attribute_value("method")
801
+ puts " action: " + forms[i].action
802
+ end
803
+ end
804
+ alias showForms show_forms
805
+
806
+ #
807
+ # Description:
808
+ # Show all the images available on the page.
809
+ #
810
+ # Output:
811
+ # Name, id, src and index of all the images available on the page.
812
+ #
813
+ def show_images
814
+ images = Document.new(self).get_images
815
+ puts "There are #{images.length} images"
816
+ index = 1
817
+ images.each do |l|
818
+ puts "image: name: #{l.name}"
819
+ puts " id: #{l.id}"
820
+ puts " src: #{l.src}"
821
+ puts " index: #{index}"
822
+ index += 1
823
+ end
824
+ end
825
+ alias showImages show_images
826
+
827
+ #
828
+ # Description:
829
+ # Show all the links available on the page.
830
+ #
831
+ # Output:
832
+ # Name, id, href and index of all the links available on the page.
833
+ #
834
+ def show_links
835
+ links = Document.new(self).get_links
836
+ puts "There are #{links.length} links"
837
+ index = 1
838
+ links.each do |l|
839
+ puts "link: name: #{l.name}"
840
+ puts " id: #{l.id}"
841
+ puts " href: #{l.href}"
842
+ puts " index: #{index}"
843
+ index += 1
844
+ end
845
+ end
846
+ alias showLinks show_links
847
+
848
+ #
849
+ # Description:
850
+ # Show all the divs available on the page.
851
+ #
852
+ # Output:
853
+ # Name, id, class and index of all the divs available on the page.
854
+ #
855
+ def show_divs
856
+ divs = Document.new(self).get_divs
857
+ puts "There are #{divs.length} divs"
858
+ index = 1
859
+ divs.each do |l|
860
+ puts "div: name: #{l.name}"
861
+ puts " id: #{l.id}"
862
+ puts " class: #{l.className}"
863
+ puts " index: #{index}"
864
+ index += 1
865
+ end
866
+ end
867
+ alias showDivs show_divs
868
+
869
+ #
870
+ # Description:
871
+ # Show all the tables available on the page.
872
+ #
873
+ # Output:
874
+ # Id, row count, column count (only first row) and index of all the tables available on the page.
875
+ #
876
+ def show_tables
877
+ tables = Document.new(self).get_tables
878
+ puts "There are #{tables.length} tables"
879
+ index = 1
880
+ tables.each do |l|
881
+ puts "table: id: #{l.id}"
882
+ puts " rows: #{l.row_count}"
883
+ puts " columns: #{l.column_count}"
884
+ puts " index: #{index}"
885
+ index += 1
886
+ end
887
+ end
888
+ alias showTables show_tables
889
+
890
+ #
891
+ # Description:
892
+ # Show all the pre elements available on the page.
893
+ #
894
+ # Output:
895
+ # Id, name and index of all the pre elements available on the page.
896
+ #
897
+ def show_pres
898
+ pres = Document.new(self).get_pres
899
+ puts "There are #{pres.length} pres"
900
+ index = 1
901
+ pres.each do |l|
902
+ puts "pre: id: #{l.id}"
903
+ puts " name: #{l.name}"
904
+ puts " index: #{index}"
905
+ index += 1
906
+ end
907
+ end
908
+ alias showPres show_pres
909
+
910
+ #
911
+ # Description:
912
+ # Show all the spans available on the page.
913
+ #
914
+ # Output:
915
+ # Name, id, class and index of all the spans available on the page.
916
+ #
917
+ def show_spans
918
+ spans = Document.new(self).get_spans
919
+ puts "There are #{spans.length} spans"
920
+ index = 1
921
+ spans.each do |l|
922
+ puts "span: name: #{l.name}"
923
+ puts " id: #{l.id}"
924
+ puts " class: #{l.className}"
925
+ puts " index: #{index}"
926
+ index += 1
927
+ end
928
+ end
929
+ alias showSpans show_spans
930
+
931
+ #
932
+ # Description:
933
+ # Show all the labels available on the page.
934
+ #
935
+ # Output:
936
+ # Name, id, for and index of all the labels available on the page.
937
+ #
938
+ def show_labels
939
+ labels = Document.new(self).get_labels
940
+ puts "There are #{labels.length} labels"
941
+ index = 1
942
+ labels.each do |l|
943
+ puts "label: name: #{l.name}"
944
+ puts " id: #{l.id}"
945
+ puts " for: #{l.for}"
946
+ puts " index: #{index}"
947
+ index += 1
948
+ end
949
+ end
950
+ alias showLabels show_labels
951
+
952
+ #
953
+ # Description:
954
+ # Show all the frames available on the page. Doesn't show nested frames.
955
+ #
956
+ # Output:
957
+ # Name, and index of all the frames available on the page.
958
+ #
959
+ def show_frames
960
+ jssh_command = "var frameset = #{window_var}.frames;
961
+ var elements_frames = new Array();
962
+ for(var i = 0; i < frameset.length; i++)
963
+ {
964
+ var frames = frameset[i].frames;
965
+ for(var j = 0; j < frames.length; j++)
966
+ {
967
+ elements_frames.push(frames[j].frameElement);
968
+ }
969
+ }
970
+ elements_frames.length;"
971
+
972
+ length = js_eval(jssh_command).to_i
973
+
974
+ puts "There are #{length} frames"
975
+
976
+ frames = Array.new(length)
977
+ for i in 0..length - 1 do
978
+ frames[i] = Frame.new(self, :jssh_name, "elements_frames[#{i}]")
979
+ end
980
+
981
+ for i in 0..length - 1 do
982
+ puts "frame: name: #{frames[i].name}"
983
+ puts " index: #{i+1}"
984
+ end
985
+ end
986
+ alias showFrames show_frames
987
+
988
+ private
989
+
990
+ def path_to_bin
991
+ path = case current_os()
992
+ when :windows
993
+ path_from_registry
994
+ when :macosx
995
+ path_from_spotlight
996
+ when :linux
997
+ `which firefox`.strip
998
+ end
999
+
1000
+ raise "unable to locate Firefox executable" if path.nil? || path.empty?
1001
+
1002
+ path
1003
+ end
1004
+
1005
+ def current_os
1006
+ return @current_os if defined?(@current_os)
1007
+
1008
+ platform = RUBY_PLATFORM =~ /java/ ? java.lang.System.getProperty("os.name") : RUBY_PLATFORM
1009
+
1010
+ @current_os = case platform
1011
+ when /mingw32|mswin|windows/i
1012
+ :windows
1013
+ when /darwin|mac os/i
1014
+ :macosx
1015
+ when /linux/i
1016
+ :linux
1017
+ end
1018
+ end
1019
+
1020
+ def path_from_registry
1021
+ raise NotImplementedError, "(need to know how to access windows registry on JRuby)" if RUBY_PLATFORM =~ /java/
1022
+ require 'win32/registry.rb'
1023
+ lm = Win32::Registry::HKEY_LOCAL_MACHINE
1024
+ lm.open('SOFTWARE\Mozilla\Mozilla Firefox') do |reg|
1025
+ reg1 = lm.open("SOFTWARE\\Mozilla\\Mozilla Firefox\\#{reg.keys[0]}\\Main")
1026
+ if entry = reg1.find { |key, type, data| key =~ /pathtoexe/i }
1027
+ return entry.last
1028
+ end
1029
+ end
1030
+ end
1031
+
1032
+ def path_from_spotlight
1033
+ ff = %x[mdfind 'kMDItemCFBundleIdentifier == "org.mozilla.firefox"']
1034
+ ff = ff.empty? ? '/Applications/Firefox.app' : ff.split("\n").first
1035
+
1036
+ "#{ff}/Contents/MacOS/firefox-bin"
1037
+ end
1038
+
1039
+ end # Firefox
1040
+ end # FireWatir