ayanko-watir-webdriver 0.1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|