Module: Vapir::Element
- Extended by:
- ElementHelper
- Includes:
- Configurable, ElementObjectCandidates
- Defined in:
- lib/vapir-common/element.rb
Overview
this is included by every Element. it relies on the including class implementing a #element_object method some stuff assumes the element has a defined @container.
Instance Attribute Summary collapse
-
#browser ⇒ Object
readonly
Returns the value of attribute browser.
-
#how ⇒ Object
readonly
Returns the value of attribute how.
-
#index ⇒ Object
readonly
Returns the value of attribute index.
-
#page_container ⇒ Object
readonly
Returns the value of attribute page_container.
-
#what ⇒ Object
readonly
Returns the value of attribute what.
Class Method Summary collapse
Instance Method Summary collapse
-
#attr(attribute) ⇒ Object
method to access dom attributes by defined aliases.
- #attributes_for_stringifying ⇒ Object
- #browser_window_object ⇒ Object
-
#client_center ⇒ Object
returns a two-element Vector with the position of the center of this element on the client area.
-
#client_offset ⇒ Object
returns a two-element Vector containing the offset of this element on the client area.
- #configuration_parent ⇒ Object
- #container ⇒ Object
- #content_window_object ⇒ Object
-
#default_initialize(how, what, extra = {}) ⇒ Object
(also: #initialize)
the class-specific Elements may implement their own #initialize, but should call to this after they’ve done their stuff.
-
#dimensions ⇒ Object
returns a two-element Vector with the width and height of this element.
-
#document_center ⇒ Object
returns a two-element Vector with the position of the center of this element on the document.
- #document_object ⇒ Object
-
#document_offset ⇒ Object
returns a Vector with two elements, the x,y coordinates of this element (its top left point) from the top left edge of the window.
-
#element_object ⇒ Object
accesses the object representing this Element in the DOM.
-
#exists? ⇒ Boolean
(also: #exist?)
Returns whether this element actually exists.
-
#flash(options = {}) ⇒ Object
Flash the element the specified number of times.
- #html ⇒ Object
- #inspect ⇒ Object
-
#locate ⇒ Object
locates the element object for this element.
- #locate! ⇒ Object
-
#parent(options = {}) ⇒ Object
Return the element immediately containing this element.
- #pretty_print(pp) ⇒ Object
-
#screen_center ⇒ Object
returns a two-element Vector containing the current position of the center of this element on the screen.
-
#screen_offset ⇒ Object
returns a two-element Vector containing the position of this element on the screen.
-
#scroll_offset ⇒ Object
returns a two-element Vector containing the current scroll offset of this element relative to any scrolling parents.
-
#text_nodes ⇒ Object
returns an array of all text nodes below this element in the DOM heirarchy.
- #to_s ⇒ Object
-
#to_subtype ⇒ Object
(also: #to_factory)
returns an Element that represents the same object as self, but is an instance of the most-specific class < self.class that can represent that object.
-
#visible? ⇒ Boolean
Checks this element and its parents for display: none or visibility: hidden, these are the most common methods to hide an html element.
-
#with_highlight(options = {}) ⇒ Object
takes a block.
Methods included from ElementHelper
add_specifier, container_collection_method, container_single_method, included
Methods included from ElementClassAndModuleMethods
#add_container_method_extra_args, #all_dom_attr_aliases, #all_dom_attrs, #class_array_append, #class_array_get, #class_hash_get, #class_hash_merge, #container_collection_methods, #container_method_extra_args, #container_single_methods, #default_how, #dom_attr, #dom_attr_locate_alias, #dom_function, #dom_setter, #element_collection, #factory, #inspect_these, #inspect_this_if, #parent_element_module, #set_or_get_class_var, #specifiers
Methods included from Configurable
Instance Attribute Details
#browser ⇒ Object (readonly)
Returns the value of attribute browser.
582 583 584 |
# File 'lib/vapir-common/element.rb', line 582 def browser @browser end |
#how ⇒ Object (readonly)
Returns the value of attribute how.
107 108 109 |
# File 'lib/vapir-common/element.rb', line 107 def how @how end |
#index ⇒ Object (readonly)
Returns the value of attribute index.
109 110 111 |
# File 'lib/vapir-common/element.rb', line 109 def index @index end |
#page_container ⇒ Object (readonly)
Returns the value of attribute page_container.
583 584 585 |
# File 'lib/vapir-common/element.rb', line 583 def page_container @page_container end |
#what ⇒ Object (readonly)
Returns the value of attribute what.
108 109 110 |
# File 'lib/vapir-common/element.rb', line 108 def what @what end |
Class Method Details
.object_collection_to_enumerable(object) ⇒ Object
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 |
# File 'lib/vapir-common/element.rb', line 647 def object_collection_to_enumerable(object) if object.is_a?(Enumerable) object elsif Object.const_defined?('JsshObject') && object.is_a?(JsshObject) object.to_array elsif Object.const_defined?('WIN32OLE') && object.is_a?(WIN32OLE) array=[] length = object.length (0...length).each do |i| begin array << object.item(i) rescue WIN32OLERuntimeError # not rescuing, just adding information raise $!.class, "accessing item #{i} of #{length}, encountered:\n"+$!., $!.backtrace end end array else raise TypeError, "Don't know how to make enumerable from given object #{object.inspect} (#{object.class})" end end |
Instance Method Details
#attr(attribute) ⇒ Object
method to access dom attributes by defined aliases. unlike get_attribute, this only looks at the specific dom attributes that Watir knows about, and the aliases for those that Watir defines.
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/vapir-common/element.rb', line 288 def attr(attribute) unless attribute.is_a?(String) || attribute.is_a?(Symbol) raise TypeError, "attribute should be string or symbol; got #{attribute.inspect}" end attribute=attribute.to_sym all_aliases=self.class.all_dom_attr_aliases dom_attrs=all_aliases.reject{|dom_attr, attr_aliases| !attr_aliases.include?(attribute) }.keys case dom_attrs.size when 0 raise ArgumentError, "Not a recognized attribute: #{attribute}" when 1 method_from_element_object(dom_attrs.first) else raise ArgumentError, "Ambiguously aliased attribute #{attribute} may refer to any of: #{dom_attrs.join(', ')}" end end |
#attributes_for_stringifying ⇒ Object
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 |
# File 'lib/vapir-common/element.rb', line 598 def attributes_to_inspect=self.class.attributes_to_inspect unless exists? attributes_to_inspect=[{:value => :exists?, :label => :exists?}]+attributes_to_inspect.select{|inspect_hash| [:how, :what, :index].include?(inspect_hash[:label]) } end attributes_to_inspect.map do |inspect_hash| if !inspect_hash[:if] || inspect_hash[:if].call(self) value=case inspect_hash[:value] when /\A@/ # starts with @, look for instance variable instance_variable_get(inspect_hash[:value]).inspect when Symbol send(inspect_hash[:value]) when Proc inspect_hash[:value].call(self) else inspect_hash[:value] end [inspect_hash[:label].to_s, value] end end.compact end |
#browser_window_object ⇒ Object
593 594 595 596 |
# File 'lib/vapir-common/element.rb', line 593 def browser_window_object assert_container @container.browser_window_object end |
#client_center ⇒ Object
returns a two-element Vector with the position of the center of this element on the client area. intended to be used with mouse events’ clientX and clientY. developer.mozilla.org/en/DOM/event.clientX developer.mozilla.org/en/DOM/event.clientY
525 526 527 |
# File 'lib/vapir-common/element.rb', line 525 def client_center client_offset+dimensions.map{|dim| dim/2} end |
#client_offset ⇒ Object
returns a two-element Vector containing the offset of this element on the client area. see also #client_center
516 517 518 |
# File 'lib/vapir-common/element.rb', line 516 def client_offset document_offset-scroll_offset end |
#configuration_parent ⇒ Object
13 14 15 |
# File 'lib/vapir-common/element.rb', line 13 def configuration_parent @container ? @container.config : @browser ? @browser.config : browser_class.config end |
#container ⇒ Object
577 578 579 580 |
# File 'lib/vapir-common/element.rb', line 577 def container assert_container @container end |
#content_window_object ⇒ Object
589 590 591 592 |
# File 'lib/vapir-common/element.rb', line 589 def content_window_object assert_container @container.content_window_object end |
#default_initialize(how, what, extra = {}) ⇒ Object Also known as: initialize
the class-specific Elements may implement their own #initialize, but should call to this after they’ve done their stuff
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/vapir-common/element.rb', line 121 def default_initialize(how, what, extra={}) @how, @what=how, what raise ArgumentError, "how (first argument) should be a Symbol, not: #{how.inspect}" unless how.is_a?(Symbol) || how==nil @extra=extra @index=begin valid_symbols=[:first, :last] if valid_symbols.include?(@extra[:index]) || @extra[:index].nil? || (@extra[:index].is_a?(Integer) && @extra[:index] > 0) @extra[:index] elsif valid_symbols.map{|sym| sym.to_s}.include?(@extra[:index]) @extra[:index].to_sym elsif @extra[:index] =~ /\A\d+\z/ Integer(@extra[:index]) else raise ArgumentError, "expected extra[:index] to be a positive integer, a string that looks like a positive integer, :first, or :last. received #{@extra[:index]} (#{@extra[:index].class})" end end @container=extra[:container] @browser=extra[:browser] @page_container=extra[:page_container] @element_object=extra[:element_object] # this will in most cases not be set, but may be set in some cases from ElementCollection enumeration extra[:locate]=true unless @extra.key?(:locate) # set default case extra[:locate] when :assert locate! when true locate when false else raise ArgumentError, "Unrecognized value given for extra[:locate]: #{extra[:locate].inspect} (#{extra[:locate].class})" end end |
#dimensions ⇒ Object
returns a two-element Vector with the width and height of this element.
563 564 565 |
# File 'lib/vapir-common/element.rb', line 563 def dimensions Vector[element_object.offsetWidth, element_object.offsetHeight] end |
#document_center ⇒ Object
returns a two-element Vector with the position of the center of this element on the document.
568 569 570 |
# File 'lib/vapir-common/element.rb', line 568 def document_center document_offset+dimensions.map{|dim| dim/2} end |
#document_object ⇒ Object
585 586 587 588 |
# File 'lib/vapir-common/element.rb', line 585 def document_object assert_container @container.document_object end |
#document_offset ⇒ Object
returns a Vector with two elements, the x,y coordinates of this element (its top left point) from the top left edge of the window
504 505 506 507 508 509 510 511 512 |
# File 'lib/vapir-common/element.rb', line 504 def document_offset xy=Vector[0,0] el=element_object begin xy+=Vector[el.offsetLeft, el.offsetTop] el=el.offsetParent end while el xy end |
#element_object ⇒ Object
accesses the object representing this Element in the DOM.
573 574 575 576 |
# File 'lib/vapir-common/element.rb', line 573 def element_object assert_exists @element_object end |
#exists? ⇒ Boolean Also known as: exist?
Returns whether this element actually exists.
278 279 280 281 282 |
# File 'lib/vapir-common/element.rb', line 278 def exists? handling_existence_failure(:handle => proc { return false }, :assert_exists => false) do return !!locate end end |
#flash(options = {}) ⇒ Object
Flash the element the specified number of times. Defaults to 10 flashes.
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
# File 'lib/vapir-common/element.rb', line 410 def flash(={}) if .is_a?(Fixnum) ={:count => } if config.warn_deprecated Kernel.warn_with_caller "DEPRECATION WARNING: #{self.class.name}\#flash takes an options hash - passing a number is deprecated. Please use #{self.class.name}\#flash(:count => #{[:count]})" end end ={:count => 10, :sleep => 0.05}.merge() #options=handle_options(options, {:count => 10, :sleep => 0.05}, [:color]) assert_exists do [:count].times do with_highlight() do sleep [:sleep] end sleep [:sleep] end end nil end |
#html ⇒ Object
111 112 113 |
# File 'lib/vapir-common/element.rb', line 111 def html outer_html end |
#inspect ⇒ Object
619 620 621 622 623 |
# File 'lib/vapir-common/element.rb', line 619 def inspect "\#<#{self.class.name}:0x#{"%.8x"%(self.hash*2)}"+.map do |attr| " "+attr.first+'='+attr.last.inspect end.join('') + ">" end |
#locate ⇒ Object
locates the element object for this element
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/vapir-common/element.rb', line 189 def locate if element_object_exists? return @element_object end new_element_object= begin case @how when :element_object assert_no_index if @element_object # if @element_object is already set, it must not exist, since we check #element_object_exists? above. raise Vapir::Exception::UnableToRelocateException, "This #{self.class.name} has stopped existing. Tried to relocate it, but it was specified using #{how.inspect} and cannot be relocated." else @what end when :xpath assert_container_exists unless @container.respond_to?(:element_object_by_xpath) raise Vapir::Exception::MissingWayOfFindingObjectException, "Locating by xpath is not supported on the container #{@container.inspect}" end # todo/fix: implement index for this, using element_objects_by_xpath ? assert_no_index by_xpath=@container.element_object_by_xpath(@what) match_candidates(by_xpath ? [by_xpath] : [], self.class.specifiers, self.class.all_dom_attr_aliases).first when :css assert_container_exists candidate_match_at_index(@index, method(:match_candidates), @container.containing_object.querySelectorAll(@what), self.class.specifiers, self.class.all_dom_attr_aliases) when :label assert_no_index unless document_object raise "No document object found for this #{self.inspect} - needed to search by id for label from #{@container.inspect}" end label_element = case @what when Label @what.locate! @what when String, Regexp page_container.label(:text, @what) else raise Vapir::Exception::MissingWayOfFindingObjectException, "This #{self.class} was specified as 'how'=:label; 'what' was expected to be a Label element or a String or Regexp to match label text. Given 'what'=#{@what.inspect} (#{@what.class})" end if label_element.exists? by_label=document_object.getElementById(label_element.for) end match_candidates(by_label ? [by_label] : [], self.class.specifiers, self.class.all_dom_attr_aliases).first when :attributes assert_container_exists specified_attributes=@what specifiers=self.class.specifiers.map{|spec| spec.merge(specified_attributes)} candidate_match_at_index(@index, method(:matched_candidates), specifiers, self.class.all_dom_attr_aliases) when nil assert_container_exists unless @what.nil? raise ArgumentError, "'what' was specified, but 'how' was not given (is nil)" end candidate_match_at_index(@index, method(:matched_candidates), self.class.specifiers, self.class.all_dom_attr_aliases) when :custom assert_container_exists # this allows a proc to be given as 'what', which is called yielding candidates, each being # an instanted Element of this class. this might seem a bit odd - instantiating a bunch # of elements in order to figure out which element_object to use in locating this one. # the purpose is so that this Element can be relocated if we lose the element_object. # the Elements that are yielded are instantiated by :element object which cannot be # relocated. # # this integrates with ElementCollection, where Enumerable methods #detect, # #select, and #reject are overridden to use it. # # the proc should return true (that is, not false or nil) when it likes the given Element - # when it matches what it expects of this Element. candidate_match_at_index(@index, method(:matched_candidates), self.class.specifiers, self.class.all_dom_attr_aliases) do |candidate| what.call(self.class.new(:element_object, candidate, @container.extra_for_contained)) end else raise Vapir::Exception::MissingWayOfFindingObjectException, "Unknown 'how' given: #{@how.inspect} (#{@how.class}). 'what' was #{@what.inspect} (#{@what.class})" end end @element_object=new_element_object end |
#locate! ⇒ Object
267 268 269 270 271 272 273 274 |
# File 'lib/vapir-common/element.rb', line 267 def locate! locate || begin klass=self.is_a?(Frame) ? Vapir::Exception::UnknownFrameException : Vapir::Exception::UnknownObjectException ="Unable to locate #{self.class}, using #{@how}"+(@what ? ": "+@what.inspect : '')+(@index ? ", index #{@index}" : "") +="\non container: #{@container.inspect}" if @container raise(klass, ) end end |
#parent(options = {}) ⇒ Object
Return the element immediately containing this element. returns nil if there is no parent, or if the parent is the document.
this is cached; call parent(:reload => true) if you wish to uncache it.
434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/vapir-common/element.rb', line 434 def parent(={}) @parent=nil if [:reload] @parent||=begin parentNode=element_object.parentNode if parentNode && parentNode != document_object # don't ascend up to the document. #TODO/Fix - for IE, comparing WIN32OLEs doesn't really work, this comparison is pointless. base_element_class.factory(parentNode, extra_for_contained) # this is a little weird, passing extra_for_contained so that this is the container of its parent. else nil end end end |
#pretty_print(pp) ⇒ Object
632 633 634 635 636 637 638 639 640 641 642 643 644 |
# File 'lib/vapir-common/element.rb', line 632 def pretty_print(pp) pp.object_address_group(self) do pp.seplist(, lambda { pp.text ',' }) do |attr| pp.breakable ' ' pp.group(0) do pp.text attr.first pp.text ':' pp.breakable pp.pp attr.last end end end end |
#screen_center ⇒ Object
returns a two-element Vector containing the current position of the center of this element on the screen. intended to be used with mouse events’ screenX and screenY. developer.mozilla.org/en/DOM/event.screenX developer.mozilla.org/en/DOM/event.screenY
not yet implemented.
558 559 560 |
# File 'lib/vapir-common/element.rb', line 558 def screen_center screen_offset+dimensions.map{|dim| dim/2} end |
#screen_offset ⇒ Object
returns a two-element Vector containing the position of this element on the screen. see also #screen_center not yet implemented.
547 548 549 |
# File 'lib/vapir-common/element.rb', line 547 def screen_offset raise NotImplementedError end |
#scroll_offset ⇒ Object
returns a two-element Vector containing the current scroll offset of this element relative to any scrolling parents. this is basically stolen from prototype - see www.prototypejs.org/api/element/cumulativescrolloffset
532 533 534 535 536 537 538 539 540 541 542 |
# File 'lib/vapir-common/element.rb', line 532 def scroll_offset xy=Vector[0,0] el=element_object begin if el.respond_to?(:scrollLeft) && el.respond_to?(:scrollTop) && (scroll_left=el.scrollLeft).is_a?(Numeric) && (scroll_top=el.scrollTop).is_a?(Numeric) xy+=Vector[scroll_left, scroll_top] end el=el.parentNode end while el xy end |
#text_nodes ⇒ Object
returns an array of all text nodes below this element in the DOM heirarchy
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 |
# File 'lib/vapir-common/element.rb', line 482 def text_nodes # TODO: needs tests assert_exists do recurse_text_nodes=proc do |rproc, e_obj| case e_obj.nodeType when 1 # TODO: name a constant ELEMENT_NODE, rather than magic number object_collection_to_enumerable(e_obj.childNodes).inject([]) do |result, c_obj| result + rproc.call(rproc, c_obj) end when 3 # TODO: name a constant TEXT_NODE, rather than magic number [e_obj.data] else #Kernel.warn("ignoring node of type #{e_obj.nodeType}") [] end end recurse_text_nodes.call(recurse_text_nodes, element_object) end end |
#to_s ⇒ Object
624 625 626 627 628 629 630 |
# File 'lib/vapir-common/element.rb', line 624 def to_s attrs= longest_label=attrs.inject(0) {|max, attr| [max, attr.first.size].max } "#{self.class.name}:0x#{"%.8x"%(self.hash*2)}\n"+attrs.map do |attr| (attr.first+": ").ljust(longest_label+2)+attr.last.inspect+"\n" end.join('') end |
#to_subtype ⇒ Object Also known as: to_factory
returns an Element that represents the same object as self, but is an instance of the most-specific class < self.class that can represent that object.
For example, if we have a table, get its first element, and call #to_factory on it:
a_table=browser.tables.first
=> #<Vapir::IE::Table:0x071bc70c index=:first tagName="TABLE">
a_element=a_table.elements.first
=> #<Vapir::IE::Element:0x071b856c index=:first tagName="TBODY" id="">
a_element.to_factory
=> #<Vapir::IE::TableBody:0x071af78c index=:first tagName="TBODY" id="">
we get back a Vapir::TableBody.
318 319 320 |
# File 'lib/vapir-common/element.rb', line 318 def to_subtype self.class.factory(element_object, @extra, @how, @what) end |
#visible? ⇒ Boolean
Checks this element and its parents for display: none or visibility: hidden, these are the most common methods to hide an html element. Returns false if this seems to be hidden or a parent is hidden.
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
# File 'lib/vapir-common/element.rb', line 449 def visible? assert_exists do element_to_check=element_object #nsIDOMDocument=jssh_socket.Components.interfaces.nsIDOMDocument really_visible=nil while element_to_check #&& !element_to_check.instanceof(nsIDOMDocument) if (style=element_object_style(element_to_check, document_object)) # only pay attention to the innermost definition that really defines visibility - one of 'hidden', 'collapse' (only for table elements), # or 'visible'. ignore 'inherit'; keep looking upward. # this makes it so that if we encounter an explicit 'visible', we don't pay attention to any 'hidden' further up. # this style is inherited - may be pointless for firefox, but IE uses the 'inherited' value. not sure if/when ff does. if really_visible==nil && (visibility=style.invoke('visibility')) visibility=visibility.strip.downcase if visibility=='hidden' || visibility=='collapse' really_visible=false return false # don't need to continue knowing it's not visible. elsif visibility=='visible' really_visible=true # we don't return true yet because a parent with display of 'none' can override end end # check for display property. this is not inherited, and a parent with display of 'none' overrides an immediate visibility='visible' display=style.invoke('display') if display && display.strip.downcase=='none' return false end end element_to_check=element_to_check.parentNode end end return true end |
#with_highlight(options = {}) ⇒ Object
takes a block. sets highlight on this element; calls the block; clears the highlight. the clear is in an ensure block so that you can call return from the given block.
takes an options hash; every argument is ignored except :highlight, which defaults to true; if set to false then the highlighting won’t actually happen, the block will just be called and its value returned.
also, you can nest these safely; it checks if you’re already highlighting before trying to set and subsequently clear the highlight.
the block is called within an #assert_exists block, so methods that highlight don’t need to also check existence as that’d be redundant.
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/vapir-common/element.rb', line 335 def with_highlight(={}) assert_exists do # yeah, this line is an unreadable mess, but I have to skip over it so many times debugging that it's worth just sticking it on one line (={:highlight => true}.merge()); (was_highlighting=@highlighting); (set_highlight() if !@highlighting && [:highlight]); (@highlighting=true) begin; result=yield ensure @highlighting=was_highlighting if !@highlighting && [:highlight] handling_existence_failure do clear_highlight() end end end result end end |