lib/firewatir/firefox.rb in firewatir-1.6.5 vs lib/firewatir/firefox.rb in firewatir-1.6.6.rc1

- old
+ new

@@ -1,1040 +1,984 @@ -=begin rdoc - This is FireWatir, Web Application Testing In Ruby using Firefox browser - - Typical usage: - # include the controller - require "firewatir" - - # go to the page you want to test - ff = FireWatir::Firefox.start("http://myserver/mypage") - - # enter "Angrez" into an input field named "username" - ff.text_field(:name, "username").set("Angrez") - - # enter "Ruby Co" into input field with id "company_ID" - ff.text_field(:id, "company_ID").set("Ruby Co") - - # click on a link that has "green" somewhere in the text that is displayed - # to the user, using a regular expression - ff.link(:text, /green/) - - # click button that has a caption of "Cancel" - ff.button(:value, "Cancel").click - - FireWatir allows your script to read and interact with HTML objects--HTML tags - and their attributes and contents. Types of objects that FireWatir can identify - include: - - Type Description - =========== =============================================================== - button <input> tags, with the type="button" attribute - check_box <input> tags, with the type="checkbox" attribute - div <div> tags - form - frame - hidden hidden <input> tags - image <img> tags - label - link <a> (anchor) tags - p <p> (paragraph) tags - radio radio buttons; <input> tags, with the type="radio" attribute - select_list <select> tags, known informally as drop-down boxes - span <span> tags - table <table> tags - text_field <input> tags with the type="text" attribute (a single-line - text field), the type="text_area" attribute (a multi-line - text field), and the type="password" attribute (a - single-line field in which the input is replaced with asterisks) - - In general, there are several ways to identify a specific object. FireWatir's - syntax is in the form (how, what), where "how" is a means of identifying - the object, and "what" is the specific string or regular expression - that FireWatir will seek, as shown in the examples above. Available "how" - options depend upon the type of object, but here are a few examples: - - How Description - ============ =============================================================== - :id Used to find an object that has an "id=" attribute. Since each - id should be unique, according to the XHTML specification, - this is recommended as the most reliable method to find an - object. - :name Used to find an object that has a "name=" attribute. This is - useful for older versions of HTML, but "name" is deprecated - in XHTML. - :value Used to find a text field with a given default value, or a - button with a given caption - :index Used to find the nth object of the specified type on a page. - For example, button(:index, 2) finds the second button. - Current versions of FireWatir use 1-based indexing, but future - versions will use 0-based indexing. - :xpath The xpath expression for identifying the element. - - Note that the XHTML specification requires that tags and their attributes be - in lower case. FireWatir doesn't enforce this; FireWatir will find tags and - attributes whether they're in upper, lower, or mixed case. This is either - a bug or a feature. - - FireWatir uses JSSh for interacting with the browser. For further information on - Firefox and DOM go to the following Web page: - - http://www.xulplanet.com/references/objref/ - -=end - -module FireWatir - include Watir::Exception - - class Firefox - include FireWatir::Container - - # XPath Result type. Return only first node that matches the xpath expression. - # More details: "http://developer.mozilla.org/en/docs/DOM:document.evaluate" - FIRST_ORDERED_NODE_TYPE = 9 - - # Description: - # Starts the firefox browser. - # On windows this starts the first version listed in the registry. - # - # Input: - # options - Hash of any of the following options: - # :waitTime - Time to wait for Firefox to start. By default it waits for 2 seconds. - # This is done because if Firefox is not started and we try to connect - # to jssh on port 9997 an exception is thrown. - # :profile - The Firefox profile to use. If none is specified, Firefox will use - # the last used profile. - # :suppress_launch_process - do not create a new firefox process. Connect to an existing one. - - # TODO: Start the firefox version given by user. - - def initialize(options = {}) - if(options.kind_of?(Integer)) - options = {:waitTime => options} - end - - # check for jssh not running, firefox may be open but not with -jssh - # if its not open at all, regardless of the :suppress_launch_process option start it - # error if running without jssh, we don't want to kill their current window (mac only) - jssh_down = false - begin - set_defaults() - rescue Watir::Exception::UnableToStartJSShException - jssh_down = true - end - - if current_os == :macosx && !%x{ps x | grep firefox-bin | grep -v grep}.empty? - raise "Firefox is running without -jssh" if jssh_down - open_window unless options[:suppress_launch_process] - elsif not options[:suppress_launch_process] - launch_browser(options) - end - - set_defaults() - get_window_number() - set_browser_document() - end - - def inspect - '#<%s:0x%x url=%s title=%s>' % [self.class, hash*2, url.inspect, title.inspect] - end - - - # Launches firebox browser - # options as .new - - def launch_browser(options = {}) - - if(options[:profile]) - profile_opt = "-no-remote -P #{options[:profile]}" - else - profile_opt = "" - end - - bin = path_to_bin() - @t = Thread.new { system("#{bin} -jssh #{profile_opt}") } - sleep options[:waitTime] || 2 - - end - private :launch_browser - - # Creates a new instance of Firefox. Loads the URL and return the instance. - # Input: - # url - url of the page to be loaded. - def self.start(url) - ff = Firefox.new - ff.goto(url) - return ff - end - - # Gets the window number opened. - # Currently, this returns the most recently opened window, which may or may - # not be the current window. - def get_window_number() - # If at any time a non-browser window like the "Downloads" window - # pops up, it will become the topmost window, so make sure we - # ignore it. - window_count = js_eval("getWindows().length").to_i - 1 - while js_eval("getWindows()[#{window_count}].getBrowser") == '' - window_count -= 1; - end - - # now correctly handles instances where only browserless windows are open - # opens one we can use if count is 0 - - if window_count < 0 - open_window - window_count = 1 - end - @window_index = window_count - end - private :get_window_number - - # Loads the given url in the browser. Waits for the page to get loaded. - def goto(url) - get_window_number() - set_browser_document() - js_eval "#{browser_var}.loadURI(\"#{url}\")" - wait() - end - - # Loads the previous page (if there is any) in the browser. Waits for the page to get loaded. - def back() - js_eval "if(#{browser_var}.canGoBack) #{browser_var}.goBack()" - wait() - end - - # Loads the next page (if there is any) in the browser. Waits for the page to get loaded. - def forward() - js_eval "if(#{browser_var}.canGoForward) #{browser_var}.goForward()" - wait() - end - - # Reloads the current page in the browser. Waits for the page to get loaded. - def refresh() - js_eval "#{browser_var}.reload()" - wait() - end - - # Executes the given JavaScript string - def execute_script(source) - result = js_eval source.to_s - wait() - - result - end - - private - # This function creates a new socket at port 9997 and sets the default values for instance and class variables. - # Generatesi UnableToStartJSShException if cannot connect to jssh even after 3 tries. - def set_defaults(no_of_tries = 0) - # JSSH listens on port 9997. Create a new socket to connect to port 9997. - begin - $jssh_socket = TCPSocket::new(MACHINE_IP, "9997") - $jssh_socket.sync = true - read_socket() - rescue - no_of_tries += 1 - retry if no_of_tries < 3 - 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" - end - @error_checkers = [] - end - - # Sets the document, window and browser variables to point to correct object in JSSh. - def set_browser_document - # Add eventlistener for browser window so that we can reset the document back whenever there is redirect - # or browser loads on its own after some time. Useful when you are searching for flight results etc and - # page goes to search page after that it goes automatically to results page. - # Details : http://zenit.senecac.on.ca/wiki/index.php/Mozilla.dev.tech.xul#What_is_an_example_of_addProgressListener.3F - jssh_command = "var listObj = new Object();"; # create new object - jssh_command << "listObj.wpl = Components.interfaces.nsIWebProgressListener;"; # set the web progress listener. - jssh_command << "listObj.QueryInterface = function(aIID) { - if (aIID.equals(listObj.wpl) || - aIID.equals(Components.interfaces.nsISupportsWeakReference) || - aIID.equals(Components.interfaces.nsISupports)) - return this; - throw Components.results.NS_NOINTERFACE; - };" # set function to locate the object via QueryInterface - jssh_command << "listObj.onStateChange = function(aProgress, aRequest, aFlag, aStatus) { - if (aFlag & listObj.wpl.STATE_STOP) { - if ( aFlag & listObj.wpl.STATE_IS_NETWORK ) { - #{document_var} = #{browser_var}.contentDocument; - #{body_var} = #{document_var}.body; - } - } - };" # add function to be called when window state is change. When state is STATE_STOP & - # STATE_IS_NETWORK then only everything is loaded. Now we can reset our variables. - jssh_command.gsub!(/\n/, "") - js_eval jssh_command - - jssh_command = "var #{window_var} = getWindows()[#{@window_index}];" - jssh_command << "var #{browser_var} = #{window_var}.getBrowser();" - # Add listener create above to browser object - jssh_command << "#{browser_var}.addProgressListener( listObj,Components.interfaces.nsIWebProgress.NOTIFY_STATE_WINDOW );" - jssh_command << "var #{document_var} = #{browser_var}.contentDocument;" - jssh_command << "var #{body_var} = #{document_var}.body;" - js_eval jssh_command - - @window_title = js_eval "#{document_var}.title" - @window_url = js_eval "#{document_var}.URL" - end - - public - def window_var - "window" - end - #private - def browser_var - "browser" - end - def document_var # unfinished - "document" - end - def body_var # unfinished - "body" - end - - public - # Closes the window. - def close - - if js_eval("getWindows().length").to_i == 1 - js_eval("getWindows()[0].close()") - - if current_os == :macosx - %x{ osascript -e 'tell application "Firefox" to quit' } - end - - # wait for the app to close properly - @t.join if @t - else - # Check if window exists, because there may be the case that it has been closed by click event on some element. - # For e.g: Close Button, Close this Window link etc. - window_number = find_window(:url, @window_url) - - # If matching window found. Close the window. - if window_number > 0 - js_eval "getWindows()[#{window_number}].close()" - end - - end - end - - # Used for attaching pop up window to an existing Firefox window, either by url or title. - # ff.attach(:url, 'http://www.google.com') - # ff.attach(:title, 'Google') - # - # Output: - # Instance of newly attached window. - def attach(how, what) - - $stderr.puts("warning: #{self.class}.attach is experimental") if $VERBOSE - window_number = find_window(how, what) - - if(window_number.nil?) - raise NoMatchingWindowFoundException.new("Unable to locate window, using #{how} and #{what}") - elsif(window_number >= 0) - @window_index = window_number - set_browser_document() - end - self - end - - # Class method to return a browser object if a window matches for how - # and what. Window can be referenced by url or title. - # The second argument can be either a string or a regular expression. - # Watir::Browser.attach(:url, 'http://www.google.com') - # Watir::Browser.attach(:title, 'Google') - def self.attach how, what - br = new :suppress_launch_process => true # don't create window - br.attach(how, what) - br - end - - # loads up a new window in an existing process - # Watir::Browser.attach() with no arguments passed the attach method will create a new window - # this will only be called one time per instance we're only ever going to run in 1 window - - def open_window - - if @opened_new_window - return @opened_new_window - end - - jssh_command = "var windows = getWindows(); var window = windows[0]; - window.open(); - var windows = getWindows(); var window_number = windows.length - 1; - window_number;" - - window_number = js_eval(jssh_command).to_i - @opened_new_window = window_number - return window_number if window_number >= 0 - end - private :open_window - - # return the window index for the browser window with the given title or url. - # how - :url or :title - # what - string or regexp - # Start searching windows in reverse order so that we attach/find the latest opened window. - def find_window(how, what) - jssh_command = "var windows = getWindows(); var window_number = false; var found = false; - for(var i = windows.length - 1; i >= 0; i--) - { - var attribute = ''; - if(typeof(windows[i].getBrowser) != 'function') - { - continue; - } - var browser = windows[i].getBrowser(); - if(!browser) - { - continue; - } - if(\"#{how}\" == \"url\") - { - attribute = browser.contentDocument.URL; - } - if(\"#{how}\" == \"title\") - { - attribute = browser.contentDocument.title; - }" - if(what.class == Regexp) - jssh_command << "var regExp = new RegExp(#{what.inspect}); - found = regExp.test(attribute);" - else - jssh_command << "found = (attribute == \"#{what}\");" - end - - jssh_command << "if(found) - { - window_number = i; - break; - } - } - window_number;" - window_number = js_eval(jssh_command).to_s - return window_number == 'false' ? nil : window_number.to_i - end - private :find_window - - # - # Description: - # Matches the given text with the current text shown in the browser. - # - # Input: - # target - Text to match. Can be a string or regex - # - # Output: - # Returns the index if the specified text was found. - # Returns matchdata object if the specified regexp was found. - # - def contains_text(target) - #puts "Text to match is : #{match_text}" - #puts "Html is : #{self.text}" - case target - when Regexp - self.text.match(target) - when String - self.text.index(target) - else - raise TypeError, "Argument #{target} should be a string or regexp." - end - end - - # Returns the url of the page currently loaded in the browser. - def url - @window_url = js_eval "#{document_var}.location" - end - - # Returns the title of the page currently loaded in the browser. - def title - @window_title = js_eval "#{document_var}.title" - end - - # Returns the Status of the page currently loaded in the browser from statusbar. - # - # Output: - # Status of the page. - # - def status - js_status = js_eval("#{window_var}.status") - js_status.empty? ? js_eval("#{WINDOW_VAR}.XULBrowserWindow.statusText;") : js_status - end - - - # Returns the html of the page currently loaded in the browser. - def html - result = js_eval("var htmlelem = #{document_var}.getElementsByTagName('html')[0]; htmlelem.innerHTML") - return "<html>" + result + "</html>" - end - - # Returns the text of the page currently loaded in the browser. - def text - js_eval("#{body_var}.textContent").strip - end - - # Maximize the current browser window. - def maximize() - js_eval "#{window_var}.maximize()" - end - - # Minimize the current browser window. - def minimize() - js_eval "#{window_var}.minimize()" - end - - # Waits for the page to get loaded. - def wait(last_url = nil) - #puts "In wait function " - isLoadingDocument = "" - start = Time.now - - while isLoadingDocument != "false" - isLoadingDocument = js_eval("#{browser_var}=#{window_var}.getBrowser(); #{browser_var}.webProgress.isLoadingDocument;") - #puts "Is browser still loading page: #{isLoadingDocument}" - - # Raise an exception if the page fails to load - if (Time.now - start) > 300 - raise "Page Load Timeout" - end - end - # If the redirect is to a download attachment that does not reload this page, this - # method will loop forever. Therefore, we need to ensure that if this method is called - # twice with the same URL, we simply accept that we're done. - url = js_eval("#{browser_var}.contentDocument.URL") - - if(url != last_url) - # Check for Javascript redirect. As we are connected to Firefox via JSSh. JSSh - # doesn't detect any javascript redirects so check it here. - # If page redirects to itself that this code will enter in infinite loop. - # So we currently don't wait for such a page. - # wait variable in JSSh tells if we should wait more for the page to get loaded - # or continue. -1 means page is not redirected. Anyother positive values means wait. - jssh_command = "var wait = -1; var meta = null; meta = #{browser_var}.contentDocument.getElementsByTagName('meta'); - if(meta != null) - { - var doc_url = #{browser_var}.contentDocument.URL; - for(var i=0; i< meta.length;++i) - { - var content = meta[i].content; - var regex = new RegExp(\"^refresh$\", \"i\"); - if(regex.test(meta[i].httpEquiv)) - { - var arrContent = content.split(';'); - var redirect_url = null; - if(arrContent.length > 0) - { - if(arrContent.length > 1) - redirect_url = arrContent[1]; - - if(redirect_url != null) - { - regex = new RegExp(\"^.*\" + redirect_url + \"$\"); - if(!regex.test(doc_url)) - { - wait = arrContent[0]; - } - } - break; - } - } - } - } - wait;" - wait_time = js_eval(jssh_command).to_i - begin - if(wait_time != -1) - sleep(wait_time) - # Call wait again. In case there are multiple redirects. - js_eval "#{browser_var} = #{window_var}.getBrowser()" - wait(url) - end - rescue - end - end - set_browser_document() - run_error_checks() - return self - end - - # Add an error checker that gets called on every page load. - # * checker - a Proc object - def add_checker(checker) - @error_checkers << checker - end - - # Disable an error checker - # * checker - a Proc object that is to be disabled - def disable_checker(checker) - @error_checkers.delete(checker) - end - - # Run the predefined error checks. This is automatically called on every page load. - def run_error_checks - @error_checkers.each { |e| e.call(self) } - end - - - #def jspopup_appeared(popupText = "", wait = 2) - # winHelper = WindowHelper.new() - # return winHelper.hasPopupAppeared(popupText, wait) - #end - - # - # Description: - # Redefines the alert and confirm methods on the basis of button to be clicked. - # This is done so that JSSh doesn't get blocked. You should use click_no_wait method before calling this function. - # - # Typical Usage: - # ff.button(:id, "button").click_no_wait - # ff.click_jspopup_button("OK") - # - # Input: - # button - JavaScript button to be clicked. Values can be OK or Cancel - # - #def click_jspopup_button(button) - # button = button.downcase - # element = Element.new(nil) - # element.click_js_popup(button) - #end - - # - # Description: - # Tells FireWatir to click javascript button in case one comes after performing some action on an element. Matches - # text of pop up with one if supplied as parameter. If text matches clicks the button else stop script execution until - # pop up is dismissed by manual intervention. - # - # Input: - # button - JavaScript button to be clicked. Values can be OK or Cancel - # waitTime - Time to wait for pop up to come. Not used just for compatibility with Watir. - # userInput - Not used just for compatibility with Watir - # text - Text that should appear on pop up. - # - def startClicker(button, waitTime = 1, userInput = nil, text = nil) - jssh_command = "var win = #{browser_var}.contentWindow;" - if(button =~ /ok/i) - jssh_command << "var popuptext = ''; - var old_alert = win.alert; - var old_confirm = win.confirm; - win.alert = function(param) {" - if(text != nil) - jssh_command << "if(param == \"#{text}\") { - popuptext = param; - return true; - } - else { - popuptext = param; - win.alert = old_alert; - win.alert(param); - }" - else - jssh_command << "popuptext = param; return true;" - end - jssh_command << "}; - win.confirm = function(param) {" - if(text != nil) - jssh_command << "if(param == \"#{text}\") { - popuptext = param; - return true; - } - else { - win.confirm = old_confirm; - win.confirm(param); - }" - else - jssh_command << "popuptext = param; return true;" - end - jssh_command << "};" - - elsif(button =~ /cancel/i) - jssh_command = "var old_confirm = win.confirm; - win.confirm = function(param) {" - if(text != nil) - jssh_command << "if(param == \"#{text}\") { - popuptext = param; - return false; - } - else { - win.confirm = old_confirm; - win.confirm(param); - }" - else - jssh_command << "popuptext = param; return false;" - end - jssh_command << "};" - end - js_eval jssh_command - end - - # - # Description: - # Returns text of javascript pop up in case it comes. - # - # Output: - # Text shown in javascript pop up. - # - def get_popup_text() - return_value = js_eval "popuptext" - # reset the variable - js_eval "popuptext = ''" - return return_value - end - - # Returns the document element of the page currently loaded in the browser. - def document - Document.new(self) - end - - # Returns the first element that matches the given xpath expression or query. - def element_by_xpath(xpath) - temp = Element.new(nil, self) - element_name = temp.element_by_xpath(self, xpath) - return element_factory(element_name) - end - - # Return object of correct Element class while using XPath to get the element. - def element_factory(element_name) - jssh_type = Element.new(element_name,self).element_type - #puts "jssh type is : #{jssh_type}" # DEBUG - candidate_class = jssh_type =~ /HTML(.*)Element/ ? $1 : '' - #puts candidate_class # DEBUG - if candidate_class == 'Input' - input_type = js_eval("#{element_name}.type").downcase.strip - firewatir_class = input_class(input_type) - else - firewatir_class = jssh2firewatir(candidate_class) - end - - #puts firewatir_class # DEBUG - klass = FireWatir.const_get(firewatir_class) - - if klass == Element - klass.new(element_name,self) - elsif klass == CheckBox - klass.new(self,:jssh_name,element_name,["checkbox"]) - elsif klass == Radio - klass.new(self,:jssh_name,element_name,["radio"]) - else - klass.new(self,:jssh_name,element_name) - end - end - private :element_factory - - # Return the class name for element of input type depending upon its type like checkbox, radio etc. - def input_class(input_type) - hash = { - 'select-one' => 'SelectList', - 'select-multiple' => 'SelectList', - 'text' => 'TextField', - 'password' => 'TextField', - 'textarea' => 'TextField', - # TODO when there's no type, it's a TextField - 'file' => 'FileField', - 'checkbox' => 'CheckBox', - 'radio' => 'Radio', - 'reset' => 'Button', - 'button' => 'Button', - 'submit' => 'Button', - 'image' => 'Button' - } - hash.default = 'Element' - - hash[input_type] - end - private :input_class - - # For a provided element type returned by JSSh like HTMLDivElement, - # returns its corresponding class in Firewatir. - def jssh2firewatir(candidate_class) - hash = { - 'Div' => 'Div', - 'Button' => 'Button', - 'Frame' => 'Frame', - 'Span' => 'Span', - 'Paragraph' => 'P', - 'Label' => 'Label', - 'Form' => 'Form', - 'Image' => 'Image', - 'Table' => 'Table', - 'TableCell' => 'TableCell', - 'TableRow' => 'TableRow', - 'Select' => 'SelectList', - 'Link' => 'Link', - 'Anchor' => 'Link' # FIXME is this right? - #'Option' => 'Option' #Option uses a different constructor - } - hash.default = 'Element' - hash[candidate_class] - end - private :jssh2firewatir - - # - # Description: - # Returns the array of elements that matches the xpath query. - # - # Input: - # Xpath expression or query. - # - # Output: - # Array of elements matching xpath query. - # - def elements_by_xpath(xpath) - element = Element.new(nil, self) - elem_names = element.elements_by_xpath(self, xpath) - elem_names.inject([]) {|elements,name| elements << element_factory(name)} - end - - # - # Description: - # Show all the forms available on the page. - # - # Output: - # Name, id, method and action of all the forms available on the page. - # - def show_forms - forms = Document.new(self).get_forms() - count = forms.length - puts "There are #{count} forms" - for i in 0..count - 1 do - puts "Form name: " + forms[i].name - puts " id: " + forms[i].id - puts " method: " + forms[i].attribute_value("method") - puts " action: " + forms[i].action - end - end - alias showForms show_forms - - # - # Description: - # Show all the images available on the page. - # - # Output: - # Name, id, src and index of all the images available on the page. - # - def show_images - images = Document.new(self).get_images - puts "There are #{images.length} images" - index = 1 - images.each do |l| - puts "image: name: #{l.name}" - puts " id: #{l.id}" - puts " src: #{l.src}" - puts " index: #{index}" - index += 1 - end - end - alias showImages show_images - - # - # Description: - # Show all the links available on the page. - # - # Output: - # Name, id, href and index of all the links available on the page. - # - def show_links - links = Document.new(self).get_links - puts "There are #{links.length} links" - index = 1 - links.each do |l| - puts "link: name: #{l.name}" - puts " id: #{l.id}" - puts " href: #{l.href}" - puts " index: #{index}" - index += 1 - end - end - alias showLinks show_links - - # - # Description: - # Show all the divs available on the page. - # - # Output: - # Name, id, class and index of all the divs available on the page. - # - def show_divs - divs = Document.new(self).get_divs - puts "There are #{divs.length} divs" - index = 1 - divs.each do |l| - puts "div: name: #{l.name}" - puts " id: #{l.id}" - puts " class: #{l.className}" - puts " index: #{index}" - index += 1 - end - end - alias showDivs show_divs - - # - # Description: - # Show all the tables available on the page. - # - # Output: - # Id, row count, column count (only first row) and index of all the tables available on the page. - # - def show_tables - tables = Document.new(self).get_tables - puts "There are #{tables.length} tables" - index = 1 - tables.each do |l| - puts "table: id: #{l.id}" - puts " rows: #{l.row_count}" - puts " columns: #{l.column_count}" - puts " index: #{index}" - index += 1 - end - end - alias showTables show_tables - - # - # Description: - # Show all the pre elements available on the page. - # - # Output: - # Id, name and index of all the pre elements available on the page. - # - def show_pres - pres = Document.new(self).get_pres - puts "There are #{pres.length} pres" - index = 1 - pres.each do |l| - puts "pre: id: #{l.id}" - puts " name: #{l.name}" - puts " index: #{index}" - index += 1 - end - end - alias showPres show_pres - - # - # Description: - # Show all the spans available on the page. - # - # Output: - # Name, id, class and index of all the spans available on the page. - # - def show_spans - spans = Document.new(self).get_spans - puts "There are #{spans.length} spans" - index = 1 - spans.each do |l| - puts "span: name: #{l.name}" - puts " id: #{l.id}" - puts " class: #{l.className}" - puts " index: #{index}" - index += 1 - end - end - alias showSpans show_spans - - # - # Description: - # Show all the labels available on the page. - # - # Output: - # Name, id, for and index of all the labels available on the page. - # - def show_labels - labels = Document.new(self).get_labels - puts "There are #{labels.length} labels" - index = 1 - labels.each do |l| - puts "label: name: #{l.name}" - puts " id: #{l.id}" - puts " for: #{l.for}" - puts " index: #{index}" - index += 1 - end - end - alias showLabels show_labels - - # - # Description: - # Show all the frames available on the page. Doesn't show nested frames. - # - # Output: - # Name, and index of all the frames available on the page. - # - def show_frames - jssh_command = "var frameset = #{window_var}.frames; - var elements_frames = new Array(); - for(var i = 0; i < frameset.length; i++) - { - var frames = frameset[i].frames; - for(var j = 0; j < frames.length; j++) - { - elements_frames.push(frames[j].frameElement); - } - } - elements_frames.length;" - - length = js_eval(jssh_command).to_i - - puts "There are #{length} frames" - - frames = Array.new(length) - for i in 0..length - 1 do - frames[i] = Frame.new(self, :jssh_name, "elements_frames[#{i}]") - end - - for i in 0..length - 1 do - puts "frame: name: #{frames[i].name}" - puts " index: #{i+1}" - end - end - alias showFrames show_frames - - private - - def path_to_bin - path = case current_os() - when :windows - path_from_registry - when :macosx - path_from_spotlight - when :linux - `which firefox`.strip - end - - raise "unable to locate Firefox executable" if path.nil? || path.empty? - - path - end - - def current_os - return @current_os if defined?(@current_os) - - platform = RUBY_PLATFORM =~ /java/ ? java.lang.System.getProperty("os.name") : RUBY_PLATFORM - - @current_os = case platform - when /mingw32|mswin|windows/i - :windows - when /darwin|mac os/i - :macosx - when /linux/i - :linux - end - end - - def path_from_registry - raise NotImplementedError, "(need to know how to access windows registry on JRuby)" if RUBY_PLATFORM =~ /java/ - require 'win32/registry.rb' - lm = Win32::Registry::HKEY_LOCAL_MACHINE - lm.open('SOFTWARE\Mozilla\Mozilla Firefox') do |reg| - reg1 = lm.open("SOFTWARE\\Mozilla\\Mozilla Firefox\\#{reg.keys[0]}\\Main") - if entry = reg1.find { |key, type, data| key =~ /pathtoexe/i } - return entry.last - end - end - end - - def path_from_spotlight - ff = %x[mdfind 'kMDItemCFBundleIdentifier == "org.mozilla.firefox"'] - ff = ff.empty? ? '/Applications/Firefox.app' : ff.split("\n").first - - "#{ff}/Contents/MacOS/firefox-bin" - end - - end # Firefox -end # FireWatir + +module FireWatir + include Watir::Exception + + class Firefox + include FireWatir::Container + + # XPath Result type. Return only first node that matches the xpath expression. + # More details: "http://developer.mozilla.org/en/docs/DOM:document.evaluate" + FIRST_ORDERED_NODE_TYPE = 9 + + # Description: + # Starts the firefox browser. + # On windows this starts the first version listed in the registry. + # + # Input: + # options - Hash of any of the following options: + # :waitTime - Time to wait for Firefox to start. By default it waits for 2 seconds. + # This is done because if Firefox is not started and we try to connect + # to jssh on port 9997 an exception is thrown. + # :profile - The Firefox profile to use. If none is specified, Firefox will use + # the last used profile. + # :suppress_launch_process - do not create a new firefox process. Connect to an existing one. + + # TODO: Start the firefox version given by user. + + def initialize(options = {}) + if(options.kind_of?(Integer)) + options = {:waitTime => options} + end + + # check for jssh not running, firefox may be open but not with -jssh + # if its not open at all, regardless of the :suppress_launch_process option start it + # error if running without jssh, we don't want to kill their current window (mac only) + jssh_down = false + begin + set_defaults() + rescue Watir::Exception::UnableToStartJSShException + jssh_down = true + end + + if current_os == :macosx && !%x{ps x | grep firefox-bin | grep -v grep}.empty? + raise "Firefox is running without -jssh" if jssh_down + open_window unless options[:suppress_launch_process] + elsif not options[:suppress_launch_process] + launch_browser(options) + end + + set_defaults() + get_window_number() + set_browser_document() + end + + def inspect + '#<%s:0x%x url=%s title=%s>' % [self.class, hash*2, url.inspect, title.inspect] + end + + + # Launches firebox browser + # options as .new + + def launch_browser(options = {}) + + if(options[:profile]) + profile_opt = "-no-remote -P #{options[:profile]}" + else + profile_opt = "" + end + + bin = path_to_bin() + @t = Thread.new { system("#{bin} -jssh #{profile_opt}") } + sleep options[:waitTime] || 2 + + end + private :launch_browser + + # Creates a new instance of Firefox. Loads the URL and return the instance. + # Input: + # url - url of the page to be loaded. + def self.start(url) + ff = Firefox.new + ff.goto(url) + return ff + end + + # Gets the window number opened. + # Currently, this returns the most recently opened window, which may or may + # not be the current window. + def get_window_number() + # If at any time a non-browser window like the "Downloads" window + # pops up, it will become the topmost window, so make sure we + # ignore it. + window_count = js_eval("getWindows().length").to_i - 1 + while js_eval("getWindows()[#{window_count}].getBrowser") == '' + window_count -= 1; + end + + # now correctly handles instances where only browserless windows are open + # opens one we can use if count is 0 + + if window_count < 0 + open_window + window_count = 1 + end + @window_index = window_count + end + private :get_window_number + + # Loads the given url in the browser. Waits for the page to get loaded. + def goto(url) + get_window_number() + set_browser_document() + js_eval "#{browser_var}.loadURI(\"#{url}\")" + wait() + end + + # Loads the previous page (if there is any) in the browser. Waits for the page to get loaded. + def back() + js_eval "if(#{browser_var}.canGoBack) #{browser_var}.goBack()" + wait() + end + + # Loads the next page (if there is any) in the browser. Waits for the page to get loaded. + def forward() + js_eval "if(#{browser_var}.canGoForward) #{browser_var}.goForward()" + wait() + end + + # Reloads the current page in the browser. Waits for the page to get loaded. + def refresh() + js_eval "#{browser_var}.reload()" + wait() + end + + # Executes the given JavaScript string + def execute_script(source) + result = js_eval source.to_s + wait() + + result + end + + private + # This function creates a new socket at port 9997 and sets the default values for instance and class variables. + # Generatesi UnableToStartJSShException if cannot connect to jssh even after 3 tries. + def set_defaults(no_of_tries = 0) + # JSSH listens on port 9997. Create a new socket to connect to port 9997. + begin + $jssh_socket = TCPSocket::new(MACHINE_IP, "9997") + $jssh_socket.sync = true + read_socket() + rescue + no_of_tries += 1 + retry if no_of_tries < 3 + 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" + end + @error_checkers = [] + end + + # Sets the document, window and browser variables to point to correct object in JSSh. + def set_browser_document + # Add eventlistener for browser window so that we can reset the document back whenever there is redirect + # or browser loads on its own after some time. Useful when you are searching for flight results etc and + # page goes to search page after that it goes automatically to results page. + # Details : http://zenit.senecac.on.ca/wiki/index.php/Mozilla.dev.tech.xul#What_is_an_example_of_addProgressListener.3F + jssh_command = "var listObj = new Object();"; # create new object + jssh_command << "listObj.wpl = Components.interfaces.nsIWebProgressListener;"; # set the web progress listener. + jssh_command << "listObj.QueryInterface = function(aIID) { + if (aIID.equals(listObj.wpl) || + aIID.equals(Components.interfaces.nsISupportsWeakReference) || + aIID.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_NOINTERFACE; + };" # set function to locate the object via QueryInterface + jssh_command << "listObj.onStateChange = function(aProgress, aRequest, aFlag, aStatus) { + if (aFlag & listObj.wpl.STATE_STOP) { + if ( aFlag & listObj.wpl.STATE_IS_NETWORK ) { + #{document_var} = #{browser_var}.contentDocument; + #{body_var} = #{document_var}.body; + } + } + };" # add function to be called when window state is change. When state is STATE_STOP & + # STATE_IS_NETWORK then only everything is loaded. Now we can reset our variables. + jssh_command.gsub!(/\n/, "") + js_eval jssh_command + + jssh_command = "var #{window_var} = getWindows()[#{@window_index}];" + jssh_command << "var #{browser_var} = #{window_var}.getBrowser();" + # Add listener create above to browser object + jssh_command << "#{browser_var}.addProgressListener( listObj,Components.interfaces.nsIWebProgress.NOTIFY_STATE_WINDOW );" + jssh_command << "var #{document_var} = #{browser_var}.contentDocument;" + jssh_command << "var #{body_var} = #{document_var}.body;" + js_eval jssh_command + + @window_title = js_eval "#{document_var}.title" + @window_url = js_eval "#{document_var}.URL" + end + + public + def window_var + "window" + end + #private + def browser_var + "browser" + end + def document_var # unfinished + "document" + end + def body_var # unfinished + "body" + end + + public + # Closes the window. + def close + + if js_eval("getWindows().length").to_i == 1 + js_eval("getWindows()[0].close()") + + if current_os == :macosx + %x{ osascript -e 'tell application "Firefox" to quit' } + end + + # wait for the app to close properly + @t.join if @t + else + # Check if window exists, because there may be the case that it has been closed by click event on some element. + # For e.g: Close Button, Close this Window link etc. + window_number = find_window(:url, @window_url) + + # If matching window found. Close the window. + if window_number > 0 + js_eval "getWindows()[#{window_number}].close()" + end + + end + end + + # Closes all firefox windows + def close_all + total_windows = js_eval("getWindows().length").to_i + + # start from last window + while(total_windows > 0) do + js_eval "getWindows()[#{total_windows - 1}].close()" + total_windows = total_windows - 1 + end + + if current_os == :macosx + %x{ osascript -e 'tell application "Firefox" to quit' } + end + + if current_os == :windows + system("taskkill /im firefox.exe /f /t >nul 2>&1") + end + end + + # Used for attaching pop up window to an existing Firefox window, either by url or title. + # ff.attach(:url, 'http://www.google.com') + # ff.attach(:title, 'Google') + # + # Output: + # Instance of newly attached window. + def attach(how, what) + + $stderr.puts("warning: #{self.class}.attach is experimental") if $VERBOSE + window_number = find_window(how, what) + + if(window_number.nil?) + raise NoMatchingWindowFoundException.new("Unable to locate window, using #{how} and #{what}") + elsif(window_number >= 0) + @window_index = window_number + set_browser_document() + end + self + end + + # Class method to return a browser object if a window matches for how + # and what. Window can be referenced by url or title. + # The second argument can be either a string or a regular expression. + # Watir::Browser.attach(:url, 'http://www.google.com') + # Watir::Browser.attach(:title, 'Google') + def self.attach how, what + br = new :suppress_launch_process => true # don't create window + br.attach(how, what) + br + end + + # loads up a new window in an existing process + # Watir::Browser.attach() with no arguments passed the attach method will create a new window + # this will only be called one time per instance we're only ever going to run in 1 window + + def open_window + + if @opened_new_window + return @opened_new_window + end + + jssh_command = "var windows = getWindows(); var window = windows[0]; + window.open(); + var windows = getWindows(); var window_number = windows.length - 1; + window_number;" + + window_number = js_eval(jssh_command).to_i + @opened_new_window = window_number + return window_number if window_number >= 0 + end + private :open_window + + # return the window index for the browser window with the given title or url. + # how - :url or :title + # what - string or regexp + # Start searching windows in reverse order so that we attach/find the latest opened window. + def find_window(how, what) + jssh_command = "var windows = getWindows(); var window_number = false; var found = false; + for(var i = windows.length - 1; i >= 0; i--) + { + var attribute = ''; + if(typeof(windows[i].getBrowser) != 'function') + { + continue; + } + var browser = windows[i].getBrowser(); + if(!browser) + { + continue; + } + if(\"#{how}\" == \"url\") + { + attribute = browser.contentDocument.URL; + } + if(\"#{how}\" == \"title\") + { + attribute = browser.contentDocument.title; + }" + if(what.class == Regexp) + jssh_command << "var regExp = new RegExp(#{what.inspect}); + found = regExp.test(attribute);" + else + jssh_command << "found = (attribute == \"#{what}\");" + end + + jssh_command << "if(found) + { + window_number = i; + break; + } + } + window_number;" + window_number = js_eval(jssh_command).to_s + return window_number == 'false' ? nil : window_number.to_i + end + private :find_window + + # + # Description: + # Matches the given text with the current text shown in the browser. + # + # Input: + # target - Text to match. Can be a string or regex + # + # Output: + # Returns the index if the specified text was found. + # Returns matchdata object if the specified regexp was found. + # + def contains_text(target) + #puts "Text to match is : #{match_text}" + #puts "Html is : #{self.text}" + case target + when Regexp + self.text.match(target) + when String + self.text.index(target) + else + raise TypeError, "Argument #{target} should be a string or regexp." + end + end + + # Returns the url of the page currently loaded in the browser. + def url + @window_url = js_eval "#{document_var}.URL" + end + + # Returns the title of the page currently loaded in the browser. + def title + @window_title = js_eval "#{document_var}.title" + end + + # Returns the Status of the page currently loaded in the browser from statusbar. + # + # Output: + # Status of the page. + # + def status + js_status = js_eval("#{window_var}.status") + js_status.empty? ? js_eval("#{window_var}.XULBrowserWindow.statusText;") : js_status + end + + + # Returns the html of the page currently loaded in the browser. + def html + result = js_eval("var htmlelem = #{document_var}.getElementsByTagName('html')[0]; htmlelem.innerHTML") + return "<html>" + result + "</html>" + end + + # Returns the text of the page currently loaded in the browser. + def text + js_eval("#{body_var}.textContent").strip + end + + # Maximize the current browser window. + def maximize() + js_eval "#{window_var}.maximize()" + end + + # Minimize the current browser window. + def minimize() + js_eval "#{window_var}.minimize()" + end + + # Waits for the page to get loaded. + def wait(last_url = nil) + #puts "In wait function " + isLoadingDocument = "" + start = Time.now + + while isLoadingDocument != "false" + isLoadingDocument = js_eval("#{browser_var}=#{window_var}.getBrowser(); #{browser_var}.webProgress.isLoadingDocument;") + #puts "Is browser still loading page: #{isLoadingDocument}" + + # Raise an exception if the page fails to load + if (Time.now - start) > 300 + raise "Page Load Timeout" + end + end + # If the redirect is to a download attachment that does not reload this page, this + # method will loop forever. Therefore, we need to ensure that if this method is called + # twice with the same URL, we simply accept that we're done. + url = js_eval("#{browser_var}.contentDocument.URL") + + if(url != last_url) + # Check for Javascript redirect. As we are connected to Firefox via JSSh. JSSh + # doesn't detect any javascript redirects so check it here. + # If page redirects to itself that this code will enter in infinite loop. + # So we currently don't wait for such a page. + # wait variable in JSSh tells if we should wait more for the page to get loaded + # or continue. -1 means page is not redirected. Anyother positive values means wait. + jssh_command = "var wait = -1; var meta = null; meta = #{browser_var}.contentDocument.getElementsByTagName('meta'); + if(meta != null) + { + var doc_url = #{browser_var}.contentDocument.URL; + for(var i=0; i< meta.length;++i) + { + var content = meta[i].content; + var regex = new RegExp(\"^refresh$\", \"i\"); + if(regex.test(meta[i].httpEquiv)) + { + var arrContent = content.split(';'); + var redirect_url = null; + if(arrContent.length > 0) + { + if(arrContent.length > 1) + redirect_url = arrContent[1]; + + if(redirect_url != null) + { + regex = new RegExp(\"^.*\" + redirect_url + \"$\"); + if(!regex.test(doc_url)) + { + wait = arrContent[0]; + } + } + break; + } + } + } + } + wait;" + wait_time = js_eval(jssh_command).to_i + begin + if(wait_time != -1) + sleep(wait_time) + # Call wait again. In case there are multiple redirects. + js_eval "#{browser_var} = #{window_var}.getBrowser()" + wait(url) + end + rescue + end + end + set_browser_document() + run_error_checks() + return self + end + + # Add an error checker that gets called on every page load. + # * checker - a Proc object + def add_checker(checker) + @error_checkers << checker + end + + # Disable an error checker + # * checker - a Proc object that is to be disabled + def disable_checker(checker) + @error_checkers.delete(checker) + end + + # Run the predefined error checks. This is automatically called on every page load. + def run_error_checks + @error_checkers.each { |e| e.call(self) } + end + + + #def jspopup_appeared(popupText = "", wait = 2) + # winHelper = WindowHelper.new() + # return winHelper.hasPopupAppeared(popupText, wait) + #end + + # + # Description: + # Redefines the alert and confirm methods on the basis of button to be clicked. + # This is done so that JSSh doesn't get blocked. You should use click_no_wait method before calling this function. + # + # Typical Usage: + # ff.button(:id, "button").click_no_wait + # ff.click_jspopup_button("OK") + # + # Input: + # button - JavaScript button to be clicked. Values can be OK or Cancel + # + #def click_jspopup_button(button) + # button = button.downcase + # element = Element.new(nil) + # element.click_js_popup(button) + #end + + # + # Description: + # Tells FireWatir to click javascript button in case one comes after performing some action on an element. Matches + # text of pop up with one if supplied as parameter. If text matches clicks the button else stop script execution until + # pop up is dismissed by manual intervention. + # + # Input: + # button - JavaScript button to be clicked. Values can be OK or Cancel + # waitTime - Time to wait for pop up to come. Not used just for compatibility with Watir. + # userInput - Not used just for compatibility with Watir + # text - Text that should appear on pop up. + # + def startClicker(button, waitTime = 1, userInput = nil, text = nil) + jssh_command = "var win = #{browser_var}.contentWindow;" + if(button =~ /ok/i) + jssh_command << "var popuptext = ''; + var old_alert = win.alert; + var old_confirm = win.confirm; + win.alert = function(param) {" + if(text != nil) + jssh_command << "if(param == \"#{text}\") { + popuptext = param; + return true; + } + else { + popuptext = param; + win.alert = old_alert; + win.alert(param); + }" + else + jssh_command << "popuptext = param; return true;" + end + jssh_command << "}; + win.confirm = function(param) {" + if(text != nil) + jssh_command << "if(param == \"#{text}\") { + popuptext = param; + return true; + } + else { + win.confirm = old_confirm; + win.confirm(param); + }" + else + jssh_command << "popuptext = param; return true;" + end + jssh_command << "};" + + elsif(button =~ /cancel/i) + jssh_command = "var old_confirm = win.confirm; + win.confirm = function(param) {" + if(text != nil) + jssh_command << "if(param == \"#{text}\") { + popuptext = param; + return false; + } + else { + win.confirm = old_confirm; + win.confirm(param); + }" + else + jssh_command << "popuptext = param; return false;" + end + jssh_command << "};" + end + js_eval jssh_command + end + + # + # Description: + # Returns text of javascript pop up in case it comes. + # + # Output: + # Text shown in javascript pop up. + # + def get_popup_text() + return_value = js_eval "popuptext" + # reset the variable + js_eval "popuptext = ''" + return return_value + end + + # Returns the document element of the page currently loaded in the browser. + def document + Document.new(self) + end + + # Returns the first element that matches the given xpath expression or query. + def element_by_xpath(xpath) + temp = Element.new(nil, self) + element_name = temp.element_by_xpath(self, xpath) + return element_factory(element_name) + end + + # Return object of correct Element class while using XPath to get the element. + def element_factory(element_name) + jssh_type = Element.new(element_name,self).element_type + #puts "jssh type is : #{jssh_type}" # DEBUG + candidate_class = jssh_type =~ /HTML(.*)Element/ ? $1 : '' + #puts candidate_class # DEBUG + if candidate_class == 'Input' + input_type = js_eval("#{element_name}.type").downcase.strip + firewatir_class = input_class(input_type) + else + firewatir_class = jssh2firewatir(candidate_class) + end + + #puts firewatir_class # DEBUG + klass = FireWatir.const_get(firewatir_class) + + if klass == Element + klass.new(element_name,self) + elsif klass == CheckBox + klass.new(self,:jssh_name,element_name,["checkbox"]) + elsif klass == Radio + klass.new(self,:jssh_name,element_name,["radio"]) + else + klass.new(self,:jssh_name,element_name) + end + end + private :element_factory + + # Return the class name for element of input type depending upon its type like checkbox, radio etc. + def input_class(input_type) + hash = { + 'select-one' => 'SelectList', + 'select-multiple' => 'SelectList', + 'text' => 'TextField', + 'password' => 'TextField', + 'textarea' => 'TextField', + # TODO when there's no type, it's a TextField + 'file' => 'FileField', + 'checkbox' => 'CheckBox', + 'radio' => 'Radio', + 'reset' => 'Button', + 'button' => 'Button', + 'submit' => 'Button', + 'image' => 'Button' + } + hash.default = 'Element' + + hash[input_type] + end + private :input_class + + # For a provided element type returned by JSSh like HTMLDivElement, + # returns its corresponding class in Firewatir. + def jssh2firewatir(candidate_class) + hash = { + 'Div' => 'Div', + 'Button' => 'Button', + 'Frame' => 'Frame', + 'Span' => 'Span', + 'Paragraph' => 'P', + 'Label' => 'Label', + 'Form' => 'Form', + 'Image' => 'Image', + 'Table' => 'Table', + 'TableCell' => 'TableCell', + 'TableRow' => 'TableRow', + 'Select' => 'SelectList', + 'Link' => 'Link', + 'Anchor' => 'Link' # FIXME is this right? + #'Option' => 'Option' #Option uses a different constructor + } + hash.default = 'Element' + hash[candidate_class] + end + private :jssh2firewatir + + # + # Description: + # Returns the array of elements that matches the xpath query. + # + # Input: + # Xpath expression or query. + # + # Output: + # Array of elements matching xpath query. + # + def elements_by_xpath(xpath) + element = Element.new(nil, self) + elem_names = element.elements_by_xpath(self, xpath) + elem_names.inject([]) {|elements,name| elements << element_factory(name)} + end + + # + # Description: + # Show all the forms available on the page. + # + # Output: + # Name, id, method and action of all the forms available on the page. + # + def show_forms + forms = Document.new(self).get_forms() + count = forms.length + puts "There are #{count} forms" + for i in 0..count - 1 do + puts "Form name: " + forms[i].name + puts " id: " + forms[i].id + puts " method: " + forms[i].attribute_value("method") + puts " action: " + forms[i].action + end + end + alias showForms show_forms + + # + # Description: + # Show all the images available on the page. + # + # Output: + # Name, id, src and index of all the images available on the page. + # + def show_images + images = Document.new(self).get_images + puts "There are #{images.length} images" + index = 1 + images.each do |l| + puts "image: name: #{l.name}" + puts " id: #{l.id}" + puts " src: #{l.src}" + puts " index: #{index}" + index += 1 + end + end + alias showImages show_images + + # + # Description: + # Show all the links available on the page. + # + # Output: + # Name, id, href and index of all the links available on the page. + # + def show_links + links = Document.new(self).get_links + puts "There are #{links.length} links" + index = 1 + links.each do |l| + puts "link: name: #{l.name}" + puts " id: #{l.id}" + puts " href: #{l.href}" + puts " index: #{index}" + index += 1 + end + end + alias showLinks show_links + + # + # Description: + # Show all the divs available on the page. + # + # Output: + # Name, id, class and index of all the divs available on the page. + # + def show_divs + divs = Document.new(self).get_divs + puts "There are #{divs.length} divs" + index = 1 + divs.each do |l| + puts "div: name: #{l.name}" + puts " id: #{l.id}" + puts " class: #{l.className}" + puts " index: #{index}" + index += 1 + end + end + alias showDivs show_divs + + # + # Description: + # Show all the tables available on the page. + # + # Output: + # Id, row count, column count (only first row) and index of all the tables available on the page. + # + def show_tables + tables = Document.new(self).get_tables + puts "There are #{tables.length} tables" + index = 1 + tables.each do |l| + puts "table: id: #{l.id}" + puts " rows: #{l.row_count}" + puts " columns: #{l.column_count}" + puts " index: #{index}" + index += 1 + end + end + alias showTables show_tables + + # + # Description: + # Show all the pre elements available on the page. + # + # Output: + # Id, name and index of all the pre elements available on the page. + # + def show_pres + pres = Document.new(self).get_pres + puts "There are #{pres.length} pres" + index = 1 + pres.each do |l| + puts "pre: id: #{l.id}" + puts " name: #{l.name}" + puts " index: #{index}" + index += 1 + end + end + alias showPres show_pres + + # + # Description: + # Show all the spans available on the page. + # + # Output: + # Name, id, class and index of all the spans available on the page. + # + def show_spans + spans = Document.new(self).get_spans + puts "There are #{spans.length} spans" + index = 1 + spans.each do |l| + puts "span: name: #{l.name}" + puts " id: #{l.id}" + puts " class: #{l.className}" + puts " index: #{index}" + index += 1 + end + end + alias showSpans show_spans + + # + # Description: + # Show all the labels available on the page. + # + # Output: + # Name, id, for and index of all the labels available on the page. + # + def show_labels + labels = Document.new(self).get_labels + puts "There are #{labels.length} labels" + index = 1 + labels.each do |l| + puts "label: name: #{l.name}" + puts " id: #{l.id}" + puts " for: #{l.for}" + puts " index: #{index}" + index += 1 + end + end + alias showLabels show_labels + + # + # Description: + # Show all the frames available on the page. Doesn't show nested frames. + # + # Output: + # Name, and index of all the frames available on the page. + # + def show_frames + jssh_command = "var frameset = #{window_var}.frames; + var elements_frames = new Array(); + for(var i = 0; i < frameset.length; i++) + { + var frames = frameset[i].frames; + for(var j = 0; j < frames.length; j++) + { + elements_frames.push(frames[j].frameElement); + } + } + elements_frames.length;" + + length = js_eval(jssh_command).to_i + + puts "There are #{length} frames" + + frames = Array.new(length) + for i in 0..length - 1 do + frames[i] = Frame.new(self, :jssh_name, "elements_frames[#{i}]") + end + + for i in 0..length - 1 do + puts "frame: name: #{frames[i].name}" + puts " index: #{i+1}" + end + end + alias showFrames show_frames + + private + + def path_to_bin + path = case current_os() + when :windows + path_from_registry + when :macosx + path_from_spotlight + when :linux + `which firefox`.strip + end + + raise "unable to locate Firefox executable" if path.nil? || path.empty? + + path + end + + def current_os + return @current_os if defined?(@current_os) + + platform = RUBY_PLATFORM =~ /java/ ? Java::java.lang.System.getProperty("os.name") : RUBY_PLATFORM + + @current_os = case platform + when /mingw32|mswin|windows/i + :windows + when /darwin|mac os/i + :macosx + when /linux/i + :linux + end + end + + def path_from_registry + require 'win32/registry.rb' + lm = Win32::Registry::HKEY_LOCAL_MACHINE + lm.open('SOFTWARE\Mozilla\Mozilla Firefox') do |reg| + reg1 = lm.open("SOFTWARE\\Mozilla\\Mozilla Firefox\\#{reg.keys[0]}\\Main") + if entry = reg1.find { |key, type, data| key =~ /pathtoexe/i } + return entry.last + end + end + rescue LoadError + if RUBY_PLATFORM =~ /java/ + return(ENV['FIREFOX_HOME'] or raise( + NotImplementedError, + 'No Registry support in this JRuby; upgrade or set FIREFOX_HOME')) + else + raise + end + end + + def path_from_spotlight + ff = %x[mdfind 'kMDItemCFBundleIdentifier == "org.mozilla.firefox"'] + ff = ff.empty? ? '/Applications/Firefox.app' : ff.split("\n").first + + "#{ff}/Contents/MacOS/firefox-bin" + end + + end # Firefox +end # FireWatir