watir 5.0.0 → 6.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.document +5 -0
- data/.gitignore +21 -0
- data/.gitmodules +3 -0
- data/.travis.yml +35 -0
- data/CHANGES.md +1756 -0
- data/Gemfile +12 -0
- data/LICENSE +23 -0
- data/README.md +92 -0
- data/Rakefile +161 -32
- data/lib/watir.rb +127 -1
- data/lib/watir/after_hooks.rb +132 -0
- data/lib/watir/alert.rb +104 -0
- data/lib/watir/aliases.rb +6 -0
- data/lib/watir/atoms.rb +24 -0
- data/lib/watir/atoms/README +3 -0
- data/lib/watir/atoms/fireEvent.js +29 -0
- data/lib/watir/atoms/getAttribute.js +18 -0
- data/lib/watir/atoms/getInnerHtml.js +18 -0
- data/lib/watir/atoms/getOuterHtml.js +18 -0
- data/lib/watir/atoms/getParentElement.js +17 -0
- data/lib/watir/atoms/selectText.js +61 -0
- data/lib/watir/attribute_helper.rb +98 -0
- data/lib/watir/browser.rb +346 -0
- data/lib/watir/cell_container.rb +25 -0
- data/lib/watir/container.rb +51 -0
- data/lib/watir/cookies.rb +132 -0
- data/lib/watir/element_collection.rb +126 -0
- data/lib/watir/elements/area.rb +12 -0
- data/lib/watir/elements/button.rb +37 -0
- data/lib/watir/elements/cell.rb +17 -0
- data/lib/watir/elements/checkbox.rb +54 -0
- data/lib/watir/elements/dlist.rb +12 -0
- data/lib/watir/elements/element.rb +646 -0
- data/lib/watir/elements/file_field.rb +41 -0
- data/lib/watir/elements/font.rb +11 -0
- data/lib/watir/elements/form.rb +17 -0
- data/lib/watir/elements/hidden.rb +20 -0
- data/lib/watir/elements/html_elements.rb +2063 -0
- data/lib/watir/elements/iframe.rb +163 -0
- data/lib/watir/elements/image.rb +62 -0
- data/lib/watir/elements/input.rb +7 -0
- data/lib/watir/elements/link.rb +18 -0
- data/lib/watir/elements/option.rb +74 -0
- data/lib/watir/elements/radio.rb +42 -0
- data/lib/watir/elements/row.rb +17 -0
- data/lib/watir/elements/select.rb +238 -0
- data/lib/watir/elements/svg_elements.rb +667 -0
- data/lib/watir/elements/table.rb +42 -0
- data/lib/watir/elements/table_cell.rb +6 -0
- data/lib/watir/elements/table_row.rb +15 -0
- data/lib/watir/elements/table_section.rb +15 -0
- data/lib/watir/elements/text_area.rb +5 -0
- data/lib/watir/elements/text_field.rb +37 -0
- data/lib/watir/exception.rb +17 -0
- data/lib/watir/extensions/nokogiri.rb +14 -0
- data/lib/watir/extensions/select_text.rb +10 -0
- data/lib/watir/generator.rb +3 -0
- data/lib/watir/generator/base.rb +11 -0
- data/lib/watir/generator/base/generator.rb +115 -0
- data/lib/watir/generator/base/idl_sorter.rb +47 -0
- data/lib/watir/generator/base/spec_extractor.rb +138 -0
- data/lib/watir/generator/base/util.rb +21 -0
- data/lib/watir/generator/base/visitor.rb +157 -0
- data/lib/watir/generator/html.rb +15 -0
- data/lib/watir/generator/html/generator.rb +36 -0
- data/lib/watir/generator/html/spec_extractor.rb +50 -0
- data/lib/watir/generator/html/visitor.rb +21 -0
- data/lib/watir/generator/svg.rb +7 -0
- data/lib/watir/generator/svg/generator.rb +38 -0
- data/lib/watir/generator/svg/spec_extractor.rb +46 -0
- data/lib/watir/generator/svg/visitor.rb +21 -0
- data/lib/watir/has_window.rb +53 -0
- data/lib/watir/locators.rb +22 -0
- data/lib/watir/locators/button/locator.rb +38 -0
- data/lib/watir/locators/button/selector_builder.rb +27 -0
- data/lib/watir/locators/button/selector_builder/xpath.rb +29 -0
- data/lib/watir/locators/button/validator.rb +15 -0
- data/lib/watir/locators/cell/locator.rb +17 -0
- data/lib/watir/locators/cell/selector_builder.rb +24 -0
- data/lib/watir/locators/element/locator.rb +249 -0
- data/lib/watir/locators/element/selector_builder.rb +147 -0
- data/lib/watir/locators/element/selector_builder/css.rb +65 -0
- data/lib/watir/locators/element/selector_builder/xpath.rb +72 -0
- data/lib/watir/locators/element/validator.rb +23 -0
- data/lib/watir/locators/row/locator.rb +17 -0
- data/lib/watir/locators/row/selector_builder.rb +29 -0
- data/lib/watir/locators/text_area/locator.rb +13 -0
- data/lib/watir/locators/text_area/selector_builder.rb +22 -0
- data/lib/watir/locators/text_field/locator.rb +44 -0
- data/lib/watir/locators/text_field/selector_builder.rb +34 -0
- data/lib/watir/locators/text_field/selector_builder/xpath.rb +19 -0
- data/lib/watir/locators/text_field/validator.rb +20 -0
- data/lib/watir/row_container.rb +36 -0
- data/lib/watir/screenshot.rb +50 -0
- data/lib/watir/user_editable.rb +38 -0
- data/lib/watir/version.rb +3 -3
- data/lib/watir/wait.rb +250 -0
- data/lib/watir/wait/timer.rb +19 -0
- data/lib/watir/window.rb +244 -0
- data/lib/watir/xpath_support.rb +20 -0
- data/spec/always_locate_spec.rb +43 -0
- data/spec/browser_spec.rb +130 -0
- data/spec/click_spec.rb +19 -0
- data/spec/container_spec.rb +34 -0
- data/spec/element_locator_spec.rb +532 -0
- data/spec/element_spec.rb +136 -0
- data/spec/implementation.rb +216 -0
- data/spec/input_spec.rb +14 -0
- data/spec/locator_spec_helper.rb +57 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/special_chars_spec.rb +13 -0
- data/support/doctest_helper.rb +78 -0
- data/support/travis.sh +44 -0
- data/support/version_differ.rb +59 -0
- data/watir.gemspec +37 -25
- metadata +288 -23
- data/lib/watir/loader.rb +0 -64
@@ -0,0 +1,65 @@
|
|
1
|
+
module Watir
|
2
|
+
module Locators
|
3
|
+
class Element
|
4
|
+
class SelectorBuilder
|
5
|
+
class CSS
|
6
|
+
def build(selectors)
|
7
|
+
return unless use_css?(selectors)
|
8
|
+
|
9
|
+
if selectors.empty?
|
10
|
+
css = '*'
|
11
|
+
else
|
12
|
+
css = ''
|
13
|
+
css << (selectors.delete(:tag_name) || '')
|
14
|
+
|
15
|
+
klass = selectors.delete(:class)
|
16
|
+
if klass
|
17
|
+
if klass.include? ' '
|
18
|
+
css << %([class="#{css_escape klass}"])
|
19
|
+
else
|
20
|
+
css << ".#{klass}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
href = selectors.delete(:href)
|
25
|
+
if href
|
26
|
+
css << %([href~="#{css_escape href}"])
|
27
|
+
end
|
28
|
+
|
29
|
+
selectors.each do |key, value|
|
30
|
+
key = key.to_s.tr("_", "-")
|
31
|
+
css << %([#{key}="#{css_escape value}"]) # TODO: proper escaping
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
[:css, css]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def use_css?(selectors)
|
41
|
+
return false unless Watir.prefer_css?
|
42
|
+
|
43
|
+
if selectors.key?(:text) || selectors.key?(:label) || selectors.key?(:index)
|
44
|
+
return false
|
45
|
+
end
|
46
|
+
|
47
|
+
if selectors[:tag_name] == 'input' && selectors.key?(:type)
|
48
|
+
return false
|
49
|
+
end
|
50
|
+
|
51
|
+
if selectors.key?(:class) && selectors[:class] !~ /^[\w-]+$/ui
|
52
|
+
return false
|
53
|
+
end
|
54
|
+
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def css_escape(str)
|
59
|
+
str.gsub('"', '\\"')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Watir
|
2
|
+
module Locators
|
3
|
+
class Element
|
4
|
+
class SelectorBuilder
|
5
|
+
class XPath
|
6
|
+
def initialize(should_use_label_element)
|
7
|
+
@should_use_label_element = should_use_label_element
|
8
|
+
end
|
9
|
+
|
10
|
+
def build(selectors)
|
11
|
+
xpath = ".//"
|
12
|
+
xpath << (selectors.delete(:tag_name) || '*').to_s
|
13
|
+
|
14
|
+
selectors.delete :index
|
15
|
+
|
16
|
+
# the remaining entries should be attributes
|
17
|
+
unless selectors.empty?
|
18
|
+
xpath << "[" << attribute_expression(nil, selectors) << "]"
|
19
|
+
end
|
20
|
+
|
21
|
+
p xpath: xpath, selectors: selectors if $DEBUG
|
22
|
+
|
23
|
+
[:xpath, xpath]
|
24
|
+
end
|
25
|
+
|
26
|
+
# @todo Get rid of building
|
27
|
+
def attribute_expression(building, selectors)
|
28
|
+
f = selectors.map do |key, val|
|
29
|
+
if val.is_a?(Array)
|
30
|
+
"(" + val.map { |v| equal_pair(building, key, v) }.join(" or ") + ")"
|
31
|
+
else
|
32
|
+
equal_pair(building, key, val)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
f.join(" and ")
|
36
|
+
end
|
37
|
+
|
38
|
+
# @todo Get rid of building
|
39
|
+
def equal_pair(building, key, value)
|
40
|
+
if key == :class
|
41
|
+
klass = XpathSupport.escape " #{value} "
|
42
|
+
"contains(concat(' ', @class, ' '), #{klass})"
|
43
|
+
elsif key == :label && @should_use_label_element
|
44
|
+
# we assume :label means a corresponding label element, not the attribute
|
45
|
+
text = "normalize-space()=#{XpathSupport.escape value}"
|
46
|
+
"(@id=//label[#{text}]/@for or parent::label[#{text}])"
|
47
|
+
else
|
48
|
+
"#{lhs_for(building, key)}=#{XpathSupport.escape value}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @todo Get rid of building
|
53
|
+
def lhs_for(_building, key)
|
54
|
+
case key
|
55
|
+
when :text, 'text'
|
56
|
+
'normalize-space()'
|
57
|
+
when :href
|
58
|
+
# TODO: change this behaviour?
|
59
|
+
'normalize-space(@href)'
|
60
|
+
when :type
|
61
|
+
# type attributes can be upper case - downcase them
|
62
|
+
# https://github.com/watir/watir/issues/72
|
63
|
+
XpathSupport.downcase('@type')
|
64
|
+
else
|
65
|
+
"@#{key.to_s.tr("_", "-")}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Watir
|
2
|
+
module Locators
|
3
|
+
class Element
|
4
|
+
class Validator
|
5
|
+
def validate(element, selector)
|
6
|
+
selector_tag_name = selector[:tag_name]
|
7
|
+
element_tag_name = element.tag_name.downcase
|
8
|
+
|
9
|
+
if selector_tag_name
|
10
|
+
return unless selector_tag_name === element_tag_name
|
11
|
+
end
|
12
|
+
|
13
|
+
if element_tag_name == 'input'
|
14
|
+
# TODO - Verify this is desired behavior based on https://bugzilla.mozilla.org/show_bug.cgi?id=1290963
|
15
|
+
return if selector[:type] && selector[:type] != element.attribute(:type).downcase
|
16
|
+
end
|
17
|
+
|
18
|
+
element
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Watir
|
2
|
+
module Locators
|
3
|
+
class Row
|
4
|
+
class SelectorBuilder < Element::SelectorBuilder
|
5
|
+
def build_wd_selector(selectors)
|
6
|
+
return if selectors.values.any? { |e| e.kind_of? Regexp }
|
7
|
+
selectors.delete(:tag_name) || raise("internal error: no tag_name?!")
|
8
|
+
|
9
|
+
expressions = %w[./tr]
|
10
|
+
unless %w[tbody tfoot thead].include?(@parent.tag_name.downcase)
|
11
|
+
expressions += %w[./tbody/tr ./thead/tr ./tfoot/tr]
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_expr = xpath_builder.attribute_expression(nil, selectors)
|
15
|
+
|
16
|
+
unless attr_expr.empty?
|
17
|
+
expressions.map! { |e| "#{e}[#{attr_expr}]" }
|
18
|
+
end
|
19
|
+
|
20
|
+
xpath = expressions.join(" | ")
|
21
|
+
|
22
|
+
p build_wd_selector: xpath if $DEBUG
|
23
|
+
|
24
|
+
[:xpath, xpath]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Watir
|
2
|
+
module Locators
|
3
|
+
class TextArea
|
4
|
+
class SelectorBuilder < Element::SelectorBuilder
|
5
|
+
private
|
6
|
+
|
7
|
+
def normalize_selector(how, what)
|
8
|
+
# We need to iterate through located elements and fetch
|
9
|
+
# attribute value using Selenium because XPath doesn't understand
|
10
|
+
# difference between IDL vs content attribute.
|
11
|
+
# Current Element design doesn't allow to do that in any
|
12
|
+
# obvious way except to use regular expression.
|
13
|
+
if how == :value && what.kind_of?(String)
|
14
|
+
[how, Regexp.new('^' + Regexp.escape(what) + '$')]
|
15
|
+
else
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Watir
|
2
|
+
module Locators
|
3
|
+
class TextField
|
4
|
+
class Locator < Element::Locator
|
5
|
+
def locate_all
|
6
|
+
find_all_by_multiple
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def wd_find_first_by(how, what)
|
12
|
+
how, what = selector_builder.build_wd_selector(how => what) if how == :tag_name
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def matches_selector?(element, rx_selector)
|
17
|
+
rx_selector = rx_selector.dup
|
18
|
+
|
19
|
+
tag_name = element.tag_name.downcase
|
20
|
+
|
21
|
+
[:text, :value, :label].each do |key|
|
22
|
+
if rx_selector.key?(key)
|
23
|
+
correct_key = tag_name == 'input' ? :value : :text
|
24
|
+
rx_selector[correct_key] = rx_selector.delete(key)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def by_id
|
32
|
+
element = super
|
33
|
+
|
34
|
+
if element && !Watir::TextField::NON_TEXT_TYPES.include?(element.attribute(:type))
|
35
|
+
if element.tag_name.downcase == 'textarea'
|
36
|
+
warn "Locating textareas with '#text_field' is deprecated. Please, use '#textarea' method instead."
|
37
|
+
end
|
38
|
+
element
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Watir
|
2
|
+
module Locators
|
3
|
+
class TextField
|
4
|
+
class SelectorBuilder < Element::SelectorBuilder
|
5
|
+
def build_wd_selector(selectors)
|
6
|
+
return if selectors.values.any? { |e| e.kind_of? Regexp }
|
7
|
+
|
8
|
+
selectors.delete(:tag_name)
|
9
|
+
|
10
|
+
textarea_attr_exp = xpath_builder.attribute_expression(:textarea, selectors)
|
11
|
+
input_attr_exp = xpath_builder.attribute_expression(:input, selectors)
|
12
|
+
|
13
|
+
xpath = ".//input[(not(@type) or (#{negative_type_expr}))"
|
14
|
+
xpath << " and #{input_attr_exp}" unless input_attr_exp.empty?
|
15
|
+
xpath << "] "
|
16
|
+
xpath << "| .//textarea"
|
17
|
+
xpath << "[#{textarea_attr_exp}]" unless textarea_attr_exp.empty?
|
18
|
+
|
19
|
+
p build_wd_selector: xpath if $DEBUG
|
20
|
+
|
21
|
+
[:xpath, xpath]
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def negative_type_expr
|
27
|
+
Watir::TextField::NON_TEXT_TYPES.map do |type|
|
28
|
+
"%s!=%s" % [XpathSupport.downcase('@type'), type.inspect]
|
29
|
+
end.join(' and ')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Watir
|
2
|
+
module Locators
|
3
|
+
class TextField
|
4
|
+
class SelectorBuilder
|
5
|
+
class XPath < Element::SelectorBuilder::XPath
|
6
|
+
def lhs_for(building, key)
|
7
|
+
if building == :input && key == :text
|
8
|
+
"@value"
|
9
|
+
elsif building == :textarea && key == :value
|
10
|
+
"text()"
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Watir
|
2
|
+
module Locators
|
3
|
+
class TextField
|
4
|
+
class Validator < Element::Validator
|
5
|
+
VALID_TEXT_FIELD_TAGS = %w[input textarea]
|
6
|
+
|
7
|
+
def validate(element, selector)
|
8
|
+
element_tag_name = element.tag_name.downcase
|
9
|
+
|
10
|
+
if element.tag_name.downcase == 'textarea'
|
11
|
+
warn "Locating textareas with '#text_field' is deprecated. Please, use '#textarea' method instead."
|
12
|
+
end
|
13
|
+
return unless VALID_TEXT_FIELD_TAGS.include?(element_tag_name)
|
14
|
+
|
15
|
+
element
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Watir
|
2
|
+
module RowContainer
|
3
|
+
|
4
|
+
#
|
5
|
+
# Returns table row.
|
6
|
+
#
|
7
|
+
|
8
|
+
def row(*args)
|
9
|
+
Row.new(self, extract_selector(args).merge(tag_name: "tr"))
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Returns table rows collection.
|
14
|
+
#
|
15
|
+
|
16
|
+
def rows(*args)
|
17
|
+
RowCollection.new(self, extract_selector(args).merge(tag_name: "tr"))
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# The table as a 2D Array of strings with the text of each cell.
|
22
|
+
#
|
23
|
+
# @return [Array<Array<String>>]
|
24
|
+
#
|
25
|
+
|
26
|
+
def strings
|
27
|
+
assert_exists
|
28
|
+
|
29
|
+
rows.inject [] do |res, row|
|
30
|
+
res << row.cells.map(&:text)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias_method :to_a, :strings
|
34
|
+
|
35
|
+
end # RowContainer
|
36
|
+
end # Watir
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Watir
|
2
|
+
class Screenshot
|
3
|
+
|
4
|
+
def initialize(driver)
|
5
|
+
@driver = driver
|
6
|
+
end
|
7
|
+
|
8
|
+
#
|
9
|
+
# Saves screenshot to given path.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# browser.screenshot.save "screenshot.png"
|
13
|
+
#
|
14
|
+
# @param [String] path
|
15
|
+
#
|
16
|
+
|
17
|
+
def save(path)
|
18
|
+
@driver.save_screenshot(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Represents screenshot as PNG image string.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# browser.screenshot.png
|
26
|
+
# #=> '\x95\xC7\x8C@1\xC07\x1C(Edb\x15\xB2\vL'
|
27
|
+
#
|
28
|
+
# @return [String]
|
29
|
+
#
|
30
|
+
|
31
|
+
def png
|
32
|
+
@driver.screenshot_as(:png)
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Represents screenshot as Base64 encoded string.
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# browser.screenshot.base64
|
40
|
+
# #=> '7HWJ43tZDscPleeUuPW6HhN3x+z7vU/lufmH0qNTtTum94IBWMT46evImci1vnFGT'
|
41
|
+
#
|
42
|
+
# @return [String]
|
43
|
+
#
|
44
|
+
|
45
|
+
def base64
|
46
|
+
@driver.screenshot_as(:base64)
|
47
|
+
end
|
48
|
+
|
49
|
+
end # Screenshot
|
50
|
+
end # Watir
|