ayanko-watir-webdriver 0.1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/.gitmodules +3 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.rdoc +55 -0
- data/Rakefile +139 -0
- data/VERSION +1 -0
- data/lib/watir-webdriver.rb +71 -0
- data/lib/watir-webdriver/attribute_helper.rb +128 -0
- data/lib/watir-webdriver/browser.rb +164 -0
- data/lib/watir-webdriver/browserbot.js +49 -0
- data/lib/watir-webdriver/cell_container.rb +19 -0
- data/lib/watir-webdriver/container.rb +40 -0
- data/lib/watir-webdriver/core_ext/string.rb +22 -0
- data/lib/watir-webdriver/element_collection.rb +96 -0
- data/lib/watir-webdriver/elements/button.rb +75 -0
- data/lib/watir-webdriver/elements/checkbox.rb +73 -0
- data/lib/watir-webdriver/elements/element.rb +265 -0
- data/lib/watir-webdriver/elements/file_field.rb +69 -0
- data/lib/watir-webdriver/elements/font.rb +11 -0
- data/lib/watir-webdriver/elements/form.rb +17 -0
- data/lib/watir-webdriver/elements/frame.rb +110 -0
- data/lib/watir-webdriver/elements/generated.rb +2541 -0
- data/lib/watir-webdriver/elements/hidden.rb +24 -0
- data/lib/watir-webdriver/elements/image.rb +51 -0
- data/lib/watir-webdriver/elements/input.rb +42 -0
- data/lib/watir-webdriver/elements/link.rb +7 -0
- data/lib/watir-webdriver/elements/option.rb +55 -0
- data/lib/watir-webdriver/elements/radio.rb +49 -0
- data/lib/watir-webdriver/elements/select.rb +216 -0
- data/lib/watir-webdriver/elements/table.rb +37 -0
- data/lib/watir-webdriver/elements/table_cell.rb +36 -0
- data/lib/watir-webdriver/elements/table_row.rb +45 -0
- data/lib/watir-webdriver/elements/table_section.rb +9 -0
- data/lib/watir-webdriver/elements/text_field.rb +97 -0
- data/lib/watir-webdriver/exception.rb +21 -0
- data/lib/watir-webdriver/extensions/alerts.rb +69 -0
- data/lib/watir-webdriver/extensions/cookies.rb +39 -0
- data/lib/watir-webdriver/extensions/firefox/webdriver.xpi +0 -0
- data/lib/watir-webdriver/extensions/nokogiri.rb +14 -0
- data/lib/watir-webdriver/extensions/performance.rb +54 -0
- data/lib/watir-webdriver/extensions/wait.rb +141 -0
- data/lib/watir-webdriver/html.rb +19 -0
- data/lib/watir-webdriver/html/generator.rb +112 -0
- data/lib/watir-webdriver/html/idl_sorter.rb +49 -0
- data/lib/watir-webdriver/html/spec_extractor.rb +111 -0
- data/lib/watir-webdriver/html/util.rb +22 -0
- data/lib/watir-webdriver/html/visitor.rb +174 -0
- data/lib/watir-webdriver/locators/button_locator.rb +74 -0
- data/lib/watir-webdriver/locators/child_cell_locator.rb +32 -0
- data/lib/watir-webdriver/locators/child_row_locator.rb +37 -0
- data/lib/watir-webdriver/locators/element_locator.rb +352 -0
- data/lib/watir-webdriver/locators/text_field_locator.rb +65 -0
- data/lib/watir-webdriver/row_container.rb +34 -0
- data/lib/watir-webdriver/window_switching.rb +105 -0
- data/lib/watir-webdriver/xpath_support.rb +28 -0
- data/lib/yard/handlers/watir.rb +57 -0
- data/spec/alert_spec.rb +49 -0
- data/spec/browser_spec.rb +42 -0
- data/spec/container_spec.rb +42 -0
- data/spec/element_locator_spec.rb +304 -0
- data/spec/element_spec.rb +13 -0
- data/spec/html/alerts.html +11 -0
- data/spec/html/keylogger.html +15 -0
- data/spec/html/wait.html +27 -0
- data/spec/implementation.rb +17 -0
- data/spec/input_spec.rb +39 -0
- data/spec/locator_spec_helper.rb +51 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/wait_spec.rb +98 -0
- data/support/html5.html +90243 -0
- data/watir-webdriver.gemspec +59 -0
- metadata +238 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
require "open-uri"
|
3
|
+
require "pp"
|
4
|
+
require "webidl"
|
5
|
+
require "active_support/inflector"
|
6
|
+
|
7
|
+
ActiveSupport::Inflector.inflections do |inflect|
|
8
|
+
inflect.plural 'body', 'bodys'
|
9
|
+
inflect.plural 'tbody', 'tbodys'
|
10
|
+
inflect.plural 'canvas', 'canvases'
|
11
|
+
inflect.plural 'ins', 'inses'
|
12
|
+
inflect.plural /^s$/, 'ss'
|
13
|
+
end
|
14
|
+
|
15
|
+
require "watir-webdriver/html/util"
|
16
|
+
require "watir-webdriver/html/visitor"
|
17
|
+
require "watir-webdriver/html/idl_sorter"
|
18
|
+
require "watir-webdriver/html/spec_extractor"
|
19
|
+
require "watir-webdriver/html/generator"
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Watir
|
4
|
+
module HTML
|
5
|
+
class Generator
|
6
|
+
|
7
|
+
def generate(spec_url, io = StringIO.new)
|
8
|
+
@spec_url, @io = spec_url, io
|
9
|
+
|
10
|
+
extract_spec
|
11
|
+
cleanup_spec
|
12
|
+
|
13
|
+
write_header
|
14
|
+
write_class_defs
|
15
|
+
write_container_methods
|
16
|
+
write_footer
|
17
|
+
|
18
|
+
io
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def generator
|
24
|
+
@generator ||= WebIDL::Generator.new(visitor)
|
25
|
+
end
|
26
|
+
|
27
|
+
def visitor
|
28
|
+
@visitor ||= Visitor.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def extractor
|
32
|
+
@extractor ||= SpecExtractor.new(@spec_url)
|
33
|
+
end
|
34
|
+
|
35
|
+
def extract_spec
|
36
|
+
@tag2interfaces = extractor.process
|
37
|
+
@sorted_interfaces = extractor.sorted_interfaces
|
38
|
+
|
39
|
+
if extractor.errors.any?
|
40
|
+
raise "error extracting spec: #{extractor.errors.join("\n")}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def cleanup_spec
|
45
|
+
# ignore the link element for now
|
46
|
+
@tag2interfaces.delete("link")
|
47
|
+
@sorted_interfaces.reject! { |intf| intf.name == "HTMLLinkElement" }
|
48
|
+
end
|
49
|
+
|
50
|
+
def write_header
|
51
|
+
@io.puts "# Autogenerated from the HTML5 specification. Edits may be lost."
|
52
|
+
@io.puts "module Watir"
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_class_defs
|
56
|
+
@sorted_interfaces.each do |interface|
|
57
|
+
@io.puts indent(generator.generate(interface))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def write_container_methods
|
63
|
+
@io.puts indent("module Container")
|
64
|
+
|
65
|
+
@tag2interfaces.sort.each do |tag, interfaces|
|
66
|
+
raise "multiple interfaces for tag #{tag.inspect}" unless interfaces.map { |e| e.name }.uniq.size == 1
|
67
|
+
|
68
|
+
tag_string = tag.inspect
|
69
|
+
singular = Util.paramify(tag)
|
70
|
+
plural = singular.pluralize
|
71
|
+
element_class = Util.classify(interfaces.first.name)
|
72
|
+
collection_class = "#{element_class}Collection"
|
73
|
+
|
74
|
+
# visitor.visit_tag(tag, interfaces.first.name) !?
|
75
|
+
@io.puts indent(<<-CODE, 3)
|
76
|
+
#
|
77
|
+
# @return [#{element_class}]
|
78
|
+
#
|
79
|
+
|
80
|
+
def #{singular}(*args)
|
81
|
+
#{element_class}.new(self, extract_selector(args).merge(:tag_name => #{tag_string}))
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# @return [#{collection_class}]
|
86
|
+
#
|
87
|
+
|
88
|
+
def #{plural}(*args)
|
89
|
+
#{collection_class}.new(self, extract_selector(args).merge(:tag_name => #{tag_string}))
|
90
|
+
end
|
91
|
+
|
92
|
+
Watir.tag_to_class[#{tag.to_sym.inspect}] = #{element_class}
|
93
|
+
|
94
|
+
CODE
|
95
|
+
end
|
96
|
+
|
97
|
+
@io.puts indent("end # Container")
|
98
|
+
end
|
99
|
+
|
100
|
+
def write_footer
|
101
|
+
@io.puts "end # Watir"
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def indent(code, indent = 1)
|
106
|
+
indent_string = " "*indent
|
107
|
+
code.split("\n").map { |line| line.empty? ? line : indent_string + line }.join("\n")
|
108
|
+
end
|
109
|
+
|
110
|
+
end # Generator
|
111
|
+
end # HTML5
|
112
|
+
end # Watir
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'tsort'
|
4
|
+
|
5
|
+
module Watir
|
6
|
+
module HTML
|
7
|
+
class IDLSorter
|
8
|
+
include TSort
|
9
|
+
|
10
|
+
def initialize(interfaces)
|
11
|
+
@interfaces = {}
|
12
|
+
|
13
|
+
interfaces.each do |interface|
|
14
|
+
@interfaces[interface.name] ||= []
|
15
|
+
interface.inherits.each do |inherit|
|
16
|
+
(@interfaces[inherit.name] ||= []) << interface.name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def print
|
22
|
+
@visited = []
|
23
|
+
sort.each { |node| print_node(node) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def sort
|
27
|
+
tsort.reverse
|
28
|
+
end
|
29
|
+
|
30
|
+
def tsort_each_node(&blk)
|
31
|
+
@interfaces.each_key(&blk)
|
32
|
+
end
|
33
|
+
|
34
|
+
def tsort_each_child(node, &blk)
|
35
|
+
@interfaces[node].each(&blk)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def print_node(node, indent = 0)
|
41
|
+
return if @visited.include?(node)
|
42
|
+
@visited << node
|
43
|
+
puts " "*indent + node
|
44
|
+
tsort_each_child(node) { |child| print_node(child, indent + 2)}
|
45
|
+
end
|
46
|
+
|
47
|
+
end # IDLSorter
|
48
|
+
end # HTML
|
49
|
+
end # Watir
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Watir
|
4
|
+
module HTML
|
5
|
+
class SpecExtractor
|
6
|
+
def initialize(uri)
|
7
|
+
@uri = uri
|
8
|
+
end
|
9
|
+
|
10
|
+
def process
|
11
|
+
download_and_parse
|
12
|
+
extract_idl_parts
|
13
|
+
extract_interface_map
|
14
|
+
build_result
|
15
|
+
end
|
16
|
+
|
17
|
+
def errors
|
18
|
+
@errors ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# returns a topoligically sorted array of WebIDL::Ast::Interface objects
|
23
|
+
#
|
24
|
+
|
25
|
+
def sorted_interfaces
|
26
|
+
process if @interfaces.nil?
|
27
|
+
|
28
|
+
sorter.sort.map { |name|
|
29
|
+
@interfaces_by_name[name] or puts "ignoring interface: #{name}"
|
30
|
+
}.flatten.compact
|
31
|
+
end
|
32
|
+
|
33
|
+
def print_hierarchy
|
34
|
+
process if @interfaces.nil?
|
35
|
+
sorter.print
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def download_and_parse
|
41
|
+
open(@uri) { |io| @doc = Nokogiri.HTML(io) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def extract_idl_parts
|
45
|
+
parsed = @doc.search("//pre[@class='idl']").map { |e| parse_idl(e.inner_text) }.compact
|
46
|
+
|
47
|
+
@interfaces = parsed.map { |elements|
|
48
|
+
elements.select { |e| e.kind_of? WebIDL::Ast::Interface }
|
49
|
+
}.flatten
|
50
|
+
|
51
|
+
@interfaces_by_name = @interfaces.group_by { |i| i.name }
|
52
|
+
end
|
53
|
+
|
54
|
+
def extract_interface_map
|
55
|
+
table = @doc.search("//h3[@id='elements-1']/following-sibling::table[1]").first
|
56
|
+
table or raise "could not find elements-1 table"
|
57
|
+
|
58
|
+
@interface_map = {}
|
59
|
+
|
60
|
+
parse_table(table).each do |row|
|
61
|
+
row['Element'].split(", ").each { |tag| @interface_map[tag] = row['Interface'] }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_result
|
66
|
+
# tag name => Interface instance(s)
|
67
|
+
result = {}
|
68
|
+
|
69
|
+
@interface_map.each do |tag, interface|
|
70
|
+
result[tag] = @interfaces_by_name[interface] or raise "#{interface} not found in IDL"
|
71
|
+
end
|
72
|
+
|
73
|
+
result
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse_table(table)
|
77
|
+
headers = table.css("thead th").map { |e| e.inner_text.strip }
|
78
|
+
|
79
|
+
table.css("tbody tr").map do |row|
|
80
|
+
result = {}
|
81
|
+
|
82
|
+
row.css("th, td").each_with_index do |node, idx|
|
83
|
+
result[headers[idx]] = node.inner_text.strip
|
84
|
+
end
|
85
|
+
|
86
|
+
result
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_idl(str)
|
91
|
+
result = idl_parser.parse(str)
|
92
|
+
|
93
|
+
if result
|
94
|
+
result.build
|
95
|
+
else
|
96
|
+
errors << idl_parser.failure_reason
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def idl_parser
|
102
|
+
@idl_parser ||= WebIDL::Parser::IDLParser.new
|
103
|
+
end
|
104
|
+
|
105
|
+
def sorter
|
106
|
+
@idl_sroter ||= IDLSorter.new(@interfaces)
|
107
|
+
end
|
108
|
+
|
109
|
+
end # SpecExtractor
|
110
|
+
end # HTML
|
111
|
+
end # Watir
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Watir
|
2
|
+
module HTML
|
3
|
+
module Util
|
4
|
+
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def classify(name)
|
8
|
+
if name =~ /^HTML(.+)Element$/
|
9
|
+
$1
|
10
|
+
else
|
11
|
+
name
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def paramify(str)
|
16
|
+
classify(str).snake_case
|
17
|
+
end
|
18
|
+
|
19
|
+
end # Util
|
20
|
+
end # HTML
|
21
|
+
end # Watir
|
22
|
+
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Watir
|
4
|
+
module HTML
|
5
|
+
class Visitor < WebIDL::RubySexpVisitor
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
super
|
9
|
+
|
10
|
+
# When an interface has multiple IDL definitions in the spec, the inheritance is sometimes
|
11
|
+
# not repeated. So we'll keep track ourselves.
|
12
|
+
@inheritance_map = {}
|
13
|
+
|
14
|
+
@already_defined = []
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# WebIDL visitor interface
|
19
|
+
#
|
20
|
+
|
21
|
+
def visit_interface(interface)
|
22
|
+
name = interface.name
|
23
|
+
parent = interface.inherits.first
|
24
|
+
|
25
|
+
$stderr.puts name
|
26
|
+
return unless name =~ /^HTML/ && name !~ /(Collection|Document)$/
|
27
|
+
|
28
|
+
if name == "HTMLElement"
|
29
|
+
parent = 'Element'
|
30
|
+
elsif parent
|
31
|
+
@inheritance_map[name] ||= parent.name
|
32
|
+
parent = parent.name
|
33
|
+
else
|
34
|
+
parent = @inheritance_map[name] || return
|
35
|
+
end
|
36
|
+
|
37
|
+
[ :scope,
|
38
|
+
[:block,
|
39
|
+
element_class(interface.name, interface.members.select { |e| e.kind_of?(WebIDL::Ast::Attribute) }, parent),
|
40
|
+
collection_class(interface.name)
|
41
|
+
]
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
def visit_module(mod)
|
46
|
+
# ignored
|
47
|
+
end
|
48
|
+
|
49
|
+
def visit_implements_statement(stmt)
|
50
|
+
# ignored
|
51
|
+
end
|
52
|
+
|
53
|
+
# TODO: do everything in the visitor somehow?
|
54
|
+
# problem is we lack the tag name info while walking the interface AST
|
55
|
+
# #
|
56
|
+
# # Watir generator visitor interface
|
57
|
+
# #
|
58
|
+
#
|
59
|
+
# def visit_tag(tag_name, interface_name)
|
60
|
+
# tag_string = tag.inspect
|
61
|
+
# singular = Util.paramify(tag)
|
62
|
+
# plural = singular.pluralize
|
63
|
+
# element_class = Util.classify(interfaces.first.name)
|
64
|
+
# collection_class = "#{element_class}Collection"
|
65
|
+
#
|
66
|
+
# [:defn,
|
67
|
+
# :a,
|
68
|
+
# [:args, :"*args"],
|
69
|
+
# [:scope,
|
70
|
+
# [:block,
|
71
|
+
# [:call,
|
72
|
+
# [:const, :Anchor],
|
73
|
+
# :new,
|
74
|
+
# [:arglist,
|
75
|
+
# [:self],
|
76
|
+
# [:call,
|
77
|
+
# [:call, nil, :extract_selector, [:arglist, [:lvar, :args]]],
|
78
|
+
# :merge,
|
79
|
+
# [:arglist, [:hash, [:lit, :tag_name], [:str, "a"]]]]]]]]]
|
80
|
+
# end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def element_class(name, attributes, parent)
|
85
|
+
[:class, Util.classify(name), [:const, Util.classify(parent)],
|
86
|
+
[:scope,
|
87
|
+
[:block, attributes_call(attributes)]
|
88
|
+
]
|
89
|
+
]
|
90
|
+
end
|
91
|
+
|
92
|
+
def collection_class(name)
|
93
|
+
return if @already_defined.include?(name)
|
94
|
+
@already_defined << name
|
95
|
+
name = Util.classify(name)
|
96
|
+
|
97
|
+
[:class, "#{name}Collection", [:const, :ElementCollection],
|
98
|
+
[:scope,
|
99
|
+
[:defn, :element_class,
|
100
|
+
[:args],
|
101
|
+
[:scope,
|
102
|
+
[:block, [:const, name]]
|
103
|
+
]
|
104
|
+
]
|
105
|
+
]
|
106
|
+
]
|
107
|
+
end
|
108
|
+
|
109
|
+
def attributes_call(attributes)
|
110
|
+
return if attributes.empty?
|
111
|
+
|
112
|
+
attrs = Hash.new { |hash, key| hash[key] = [] }
|
113
|
+
attributes.each do |a|
|
114
|
+
attrs[ruby_type_for(a.type)] << a.name.snake_case.to_sym
|
115
|
+
end
|
116
|
+
|
117
|
+
call :attributes, [literal_hash(attrs)]
|
118
|
+
end
|
119
|
+
|
120
|
+
def literal_hash(hash)
|
121
|
+
[:hash] + hash.map { |k, v| [[:lit, k.to_sym], [:lit, v]] }.flatten(1)
|
122
|
+
end
|
123
|
+
|
124
|
+
def literal_array(arr)
|
125
|
+
[:array] + arr.map { |e| [:lit, e.to_sym] }
|
126
|
+
end
|
127
|
+
|
128
|
+
def call(name, args)
|
129
|
+
[:call, nil, name.to_sym, [:arglist] + args]
|
130
|
+
end
|
131
|
+
|
132
|
+
def ruby_type_for(type)
|
133
|
+
case type.name.to_s
|
134
|
+
when 'DOMString', 'any'
|
135
|
+
:string
|
136
|
+
when 'UnsignedLong', 'Long', 'Integer', 'Short', 'UnsignedShort'
|
137
|
+
:int
|
138
|
+
when 'Float', 'Double'
|
139
|
+
:float
|
140
|
+
when 'Function'
|
141
|
+
:function
|
142
|
+
when 'Boolean'
|
143
|
+
:bool
|
144
|
+
when 'Document'
|
145
|
+
:document
|
146
|
+
when 'DOMTokenList', 'DOMSettableTokenList'
|
147
|
+
:token_list
|
148
|
+
when 'DOMStringMap'
|
149
|
+
:string_map
|
150
|
+
when 'HTMLPropertiesCollection'
|
151
|
+
:properties_collection
|
152
|
+
when /HTML(.*)Element/
|
153
|
+
:html_element
|
154
|
+
when /HTML(.*)Collection/
|
155
|
+
:html_collection
|
156
|
+
when 'CSSStyleDeclaration'
|
157
|
+
:style
|
158
|
+
when /.+List$/
|
159
|
+
:list
|
160
|
+
when 'Date'
|
161
|
+
:date
|
162
|
+
when 'Element'
|
163
|
+
:element
|
164
|
+
when 'WindowProxy', 'ValidityState', 'MediaError', 'TimeRanges', 'Location', 'Any', 'TimedTrackArray', 'TimedTrack'
|
165
|
+
# probably completely wrong.
|
166
|
+
:string
|
167
|
+
else
|
168
|
+
raise "unknown type: #{type.name}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end # Visitor
|
173
|
+
end # Support
|
174
|
+
end # Watir
|