safariwatir 0.1.0
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/safariwatir.rb +213 -0
- data/safariwatir/core_ext.rb +13 -0
- data/safariwatir/scripter.rb +215 -0
- data/safariwatir_script.rb +76 -0
- data/watir/exceptions.rb +42 -0
- metadata +50 -0
data/safariwatir.rb
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'safariwatir/scripter'
|
2
|
+
require 'safariwatir/core_ext'
|
3
|
+
require 'watir/exceptions'
|
4
|
+
|
5
|
+
module Watir
|
6
|
+
include Watir::Exception
|
7
|
+
|
8
|
+
module Elements
|
9
|
+
class AlertWindow
|
10
|
+
def initialize(scripter)
|
11
|
+
@scripter = scripter
|
12
|
+
end
|
13
|
+
|
14
|
+
def click
|
15
|
+
@scripter.click_alert_ok
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class HtmlElement
|
20
|
+
def_init :scripter, :how, :what
|
21
|
+
attr_reader :how, :what
|
22
|
+
|
23
|
+
# Hooks for subclasses
|
24
|
+
def tag; end
|
25
|
+
|
26
|
+
def operate(&block)
|
27
|
+
send("operate_by_" + how.to_s, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def operate_by_id(&block)
|
33
|
+
@scripter.operate_by_id(self, &block)
|
34
|
+
end
|
35
|
+
def operate_by_index(&block)
|
36
|
+
@scripter.operate_by_index(self, &block)
|
37
|
+
end
|
38
|
+
def operate_by_name(&block)
|
39
|
+
@scripter.operate_on_form_element(self, &block)
|
40
|
+
end
|
41
|
+
def operate_by_text(&block)
|
42
|
+
@scripter.operate_on_link(self, &block)
|
43
|
+
end
|
44
|
+
def operate_by_url(&block)
|
45
|
+
@scripter.operate_on_link(self, &block)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Form < HtmlElement
|
50
|
+
def_init :scripter, :how, :what
|
51
|
+
|
52
|
+
def submit
|
53
|
+
@scripter.submit_form(self)
|
54
|
+
end
|
55
|
+
|
56
|
+
def tag; "FORM"; end
|
57
|
+
end
|
58
|
+
|
59
|
+
class ClickableElement < HtmlElement
|
60
|
+
def click
|
61
|
+
@scripter.highlight(self) do
|
62
|
+
click_element
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Hooks for subclasses
|
67
|
+
def by_value; end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Button < ClickableElement
|
71
|
+
end
|
72
|
+
|
73
|
+
class Checkbox < ClickableElement
|
74
|
+
alias :set :click
|
75
|
+
end
|
76
|
+
|
77
|
+
class Label < ClickableElement
|
78
|
+
protected
|
79
|
+
|
80
|
+
def operate_by_text(&block)
|
81
|
+
@scripter.operate_on_label(self, &block)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Link < ClickableElement
|
86
|
+
def click
|
87
|
+
@scripter.highlight(self) do
|
88
|
+
click_link
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class Radio < ClickableElement
|
94
|
+
def_init :scripter, :how, :what, :value
|
95
|
+
def by_value
|
96
|
+
@value
|
97
|
+
end
|
98
|
+
alias :set :click
|
99
|
+
end
|
100
|
+
|
101
|
+
class SelectList < ClickableElement
|
102
|
+
def select(label)
|
103
|
+
@scripter.highlight(self) do
|
104
|
+
select_option("text", label)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def select_value(value)
|
109
|
+
@scripter.highlight(self) do
|
110
|
+
select_option("value", value)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class TextField < ClickableElement
|
116
|
+
def set(value)
|
117
|
+
@scripter.highlight(self) do
|
118
|
+
clear_text_input
|
119
|
+
value.length.times do |i|
|
120
|
+
append_text_input(value[i, 1])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class Password < TextField
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class Safari
|
131
|
+
include Elements
|
132
|
+
|
133
|
+
attr_reader :scripter
|
134
|
+
|
135
|
+
def self.start(url = nil)
|
136
|
+
safari = new
|
137
|
+
safari.goto(url) if url
|
138
|
+
safari
|
139
|
+
end
|
140
|
+
|
141
|
+
def initialize
|
142
|
+
@scripter = AppleScripter.new
|
143
|
+
end
|
144
|
+
|
145
|
+
def close
|
146
|
+
scripter.close
|
147
|
+
end
|
148
|
+
|
149
|
+
def quit
|
150
|
+
scripter.quit
|
151
|
+
end
|
152
|
+
|
153
|
+
def alert
|
154
|
+
AlertWindow.new(scripter)
|
155
|
+
end
|
156
|
+
|
157
|
+
def goto(url)
|
158
|
+
scripter.navigate_to(url)
|
159
|
+
end
|
160
|
+
|
161
|
+
def button(how, what)
|
162
|
+
Button.new(scripter, how, what)
|
163
|
+
end
|
164
|
+
|
165
|
+
def checkbox(how, what)
|
166
|
+
Checkbox.new(scripter, how, what)
|
167
|
+
end
|
168
|
+
|
169
|
+
def form(how, what)
|
170
|
+
Form.new(scripter, how, what)
|
171
|
+
end
|
172
|
+
|
173
|
+
def image(how, what)
|
174
|
+
Button.new(scripter, how, what)
|
175
|
+
end
|
176
|
+
|
177
|
+
def label(how, what)
|
178
|
+
Label.new(scripter, how, what)
|
179
|
+
end
|
180
|
+
|
181
|
+
def link(how, what)
|
182
|
+
Link.new(scripter, how, what)
|
183
|
+
end
|
184
|
+
|
185
|
+
def password(how, what)
|
186
|
+
Password.new(scripter, how, what)
|
187
|
+
end
|
188
|
+
|
189
|
+
def radio(how, what, value = nil)
|
190
|
+
Radio.new(scripter, how, what, value)
|
191
|
+
end
|
192
|
+
|
193
|
+
def select_list(how, what)
|
194
|
+
SelectList.new(scripter, how, what)
|
195
|
+
end
|
196
|
+
|
197
|
+
def text_field(how, what)
|
198
|
+
TextField.new(scripter, how, what)
|
199
|
+
end
|
200
|
+
|
201
|
+
def contains_text(what)
|
202
|
+
text = scripter.document_text
|
203
|
+
case what
|
204
|
+
when Regexp:
|
205
|
+
text =~ what
|
206
|
+
when String:
|
207
|
+
text.index(what)
|
208
|
+
else
|
209
|
+
raise MissingWayOfFindingObjectException
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end # class Safari
|
213
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Why shouldn't this be in core Ruby?
|
2
|
+
class Class
|
3
|
+
def def_init(*attrs)
|
4
|
+
constructor = %|def initialize(|
|
5
|
+
constructor << attrs.map{|a| a.to_s }.join(",")
|
6
|
+
constructor << ")\n"
|
7
|
+
attrs.each do |attribute|
|
8
|
+
constructor << "@#{attribute} = #{attribute}\n"
|
9
|
+
end
|
10
|
+
constructor << "end"
|
11
|
+
class_eval(constructor)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
module Watir
|
2
|
+
class AppleScripter
|
3
|
+
|
4
|
+
@@timeout = 10
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
ensure_window_ready
|
8
|
+
end
|
9
|
+
|
10
|
+
def close
|
11
|
+
execute(%|close document 1|)
|
12
|
+
end
|
13
|
+
|
14
|
+
def quit
|
15
|
+
execute(%|quit|)
|
16
|
+
end
|
17
|
+
|
18
|
+
def navigate_to(url)
|
19
|
+
execute_and_wait(%|set URL in document 1 to "#{url}"|)
|
20
|
+
end
|
21
|
+
|
22
|
+
def document_text
|
23
|
+
execute(%|do Javascript "document.getElementsByTagName('BODY').item(0).innerText;" in document 1|)
|
24
|
+
end
|
25
|
+
|
26
|
+
def highlight(element, &block)
|
27
|
+
execute(element.operate do
|
28
|
+
%|element.focus();
|
29
|
+
element.originalColor = element.style.backgroundColor;
|
30
|
+
element.style.backgroundColor = 'yellow';|
|
31
|
+
end)
|
32
|
+
|
33
|
+
@element = element
|
34
|
+
instance_eval(&block)
|
35
|
+
@element = nil
|
36
|
+
|
37
|
+
execute(element.operate { %|element.style.backgroundColor = element.originalColor;| })
|
38
|
+
end
|
39
|
+
|
40
|
+
def select_option(option_how, option_what, element = @element)
|
41
|
+
execute(element.operate do
|
42
|
+
%|for (var i = 0; i < element.options.length; i++) {
|
43
|
+
if (element.options[i].#{option_how} == '#{option_what}') {
|
44
|
+
element.options[i].selected = true;
|
45
|
+
}
|
46
|
+
}|
|
47
|
+
end)
|
48
|
+
end
|
49
|
+
|
50
|
+
def clear_text_input(element = @element)
|
51
|
+
execute(element.operate { %|element.value = '';| })
|
52
|
+
end
|
53
|
+
|
54
|
+
def append_text_input(value, element = @element)
|
55
|
+
execute(element.operate do
|
56
|
+
%|element.value += '#{value}';
|
57
|
+
element.setSelectionRange(element.value.length, element.value.length);|
|
58
|
+
end)
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO need a better approach for "waiting"
|
62
|
+
def click_element(element = @element)
|
63
|
+
execute_and_wait(element.operate { %|element.click();| })
|
64
|
+
end
|
65
|
+
|
66
|
+
def click_link(element = @element)
|
67
|
+
click = find_link(element) do
|
68
|
+
%|var click = document.createEvent('HTMLEvents');
|
69
|
+
click.initEvent('click', true, true);
|
70
|
+
if (element.onclick) {
|
71
|
+
if (false != element.onclick(click)) {
|
72
|
+
return element.href;
|
73
|
+
}
|
74
|
+
} else {
|
75
|
+
return element.href;
|
76
|
+
}|
|
77
|
+
end
|
78
|
+
execute_and_wait(%|set target to do JavaScript "#{click}" in document 1
|
79
|
+
set URL in document 1 to target|)
|
80
|
+
end
|
81
|
+
|
82
|
+
def operate_on_link(element, &block)
|
83
|
+
%|do JavaScript "#{find_link(element, &block)}" in document 1|
|
84
|
+
end
|
85
|
+
|
86
|
+
def find_link(element)
|
87
|
+
%|for (var i = 0; i < document.links.length; i++) {
|
88
|
+
if (document.links[i].#{handle_match(element)}) {
|
89
|
+
var element = document.links[i];
|
90
|
+
#{yield}
|
91
|
+
break;
|
92
|
+
}
|
93
|
+
}|
|
94
|
+
end
|
95
|
+
private :find_link
|
96
|
+
|
97
|
+
def handle_match(element)
|
98
|
+
how = {:text => "text", :url => "href"}[element.how]
|
99
|
+
case element.what
|
100
|
+
when Regexp:
|
101
|
+
%|#{how}.match(/#{element.what.source}/)|
|
102
|
+
when String:
|
103
|
+
%|#{how} == '#{element.what}'|
|
104
|
+
end
|
105
|
+
end
|
106
|
+
private :handle_match
|
107
|
+
|
108
|
+
def operate_on_form_element(element)
|
109
|
+
%|do JavaScript "
|
110
|
+
var elements = document.getElementsByName('#{element.what}');
|
111
|
+
var element;
|
112
|
+
for (var i = 0; i < elements.length; i++) {
|
113
|
+
if (elements[i].tagName != 'META') {
|
114
|
+
#{handle_form_element_name_match(element)}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
#{yield}
|
118
|
+
" in document 1|
|
119
|
+
end
|
120
|
+
|
121
|
+
def handle_form_element_name_match(element)
|
122
|
+
element_capture = %|element = elements[i];break;|
|
123
|
+
if element.by_value
|
124
|
+
%|if (elements[i].value == '#{element.by_value}') {
|
125
|
+
#{element_capture}
|
126
|
+
}|
|
127
|
+
else
|
128
|
+
element_capture
|
129
|
+
end
|
130
|
+
end
|
131
|
+
private :handle_form_element_name_match
|
132
|
+
|
133
|
+
def operate_by_id(element)
|
134
|
+
%|do JavaScript "
|
135
|
+
var element = document.getElementById('#{element.what}');
|
136
|
+
#{yield}" in document 1|
|
137
|
+
end
|
138
|
+
|
139
|
+
def operate_by_index(element)
|
140
|
+
%|do JavaScript "
|
141
|
+
var elements = document.getElementsByTagName('#{element.tag}');
|
142
|
+
var element = elements[#{element.what}];
|
143
|
+
#{yield}" in document 1|
|
144
|
+
end
|
145
|
+
|
146
|
+
def operate_on_label(element)
|
147
|
+
%|do JavaScript "
|
148
|
+
var elements = document.getElementsByTagName('LABEL');
|
149
|
+
var element;
|
150
|
+
for (var i = 0; i < elements.length; i++) {
|
151
|
+
if (elements[i].textContent == '#{element.what}') {
|
152
|
+
element = elements[i];
|
153
|
+
#{yield}
|
154
|
+
break;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
" in document 1|
|
158
|
+
end
|
159
|
+
|
160
|
+
def submit_form(element)
|
161
|
+
execute_and_wait(element.operate { %|element.submit();| })
|
162
|
+
end
|
163
|
+
|
164
|
+
def click_link_ok
|
165
|
+
execute_system_events(%|
|
166
|
+
tell window 1
|
167
|
+
if button named "OK" exists then
|
168
|
+
click button named "OK"
|
169
|
+
end if
|
170
|
+
end tell|)
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
def execute(script)
|
176
|
+
# puts script
|
177
|
+
`osascript <<SCRIPT
|
178
|
+
tell application "Safari"
|
179
|
+
#{script}
|
180
|
+
end tell
|
181
|
+
SCRIPT`
|
182
|
+
end
|
183
|
+
|
184
|
+
# Must have "Enable access for assistive devices" checked in System Preferences > Universal Access
|
185
|
+
def execute_system_events(script)
|
186
|
+
`osascript <<SCRIPT
|
187
|
+
tell application "System Events" to tell process "Safari"
|
188
|
+
#{script}
|
189
|
+
end tell
|
190
|
+
SCRIPT`
|
191
|
+
end
|
192
|
+
|
193
|
+
def execute_and_wait(script)
|
194
|
+
execute(%|
|
195
|
+
#{script}
|
196
|
+
delay 2
|
197
|
+
repeat with i from 1 to #{@@timeout}
|
198
|
+
if (do JavaScript "document.readyState" in document 1) is "complete" then
|
199
|
+
exit repeat
|
200
|
+
else
|
201
|
+
delay 1
|
202
|
+
end if
|
203
|
+
end repeat|)
|
204
|
+
end
|
205
|
+
|
206
|
+
def ensure_window_ready
|
207
|
+
execute(%|
|
208
|
+
activate
|
209
|
+
set document_list to every document
|
210
|
+
if length of document_list is 0 then
|
211
|
+
make new document
|
212
|
+
end if|)
|
213
|
+
end
|
214
|
+
end # class AppleScripter
|
215
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'safari'
|
2
|
+
|
3
|
+
# TODO
|
4
|
+
# Need to give feedback when browser dies or when elements are not found
|
5
|
+
# Be more attached to the Safari window, if a different window is selected, the AppleScript executes against it
|
6
|
+
# Verify onclick is working for buttons and links
|
7
|
+
|
8
|
+
# Unsupported Elements: Textarea, Div, UL/OL, Span
|
9
|
+
# Use dynamic properties for Javascript optimization?
|
10
|
+
# Will I need to push more functionality into AppleScript to speed things up?
|
11
|
+
# Angrez is looking into the Ruby/AppleScript binding
|
12
|
+
# Watir Rails Plugin needed
|
13
|
+
|
14
|
+
# SAFARI ISSUES
|
15
|
+
# Labels are not clickable
|
16
|
+
# No known way to programatically click a <button>
|
17
|
+
# Links with href="javascript:foo()"
|
18
|
+
|
19
|
+
safari = Watir::Safari.new
|
20
|
+
|
21
|
+
def safari.google_to_prag
|
22
|
+
goto("http://google.com")
|
23
|
+
text_field(:name, "q").set("pickaxe")
|
24
|
+
button(:name, "btnG").click
|
25
|
+
link(:text, "The Pragmatic Programmers, LLC: Programming Ruby").click
|
26
|
+
link(:url, "http://www.pragmaticprogrammer.com/titles/ruby/code/index.html").click
|
27
|
+
link(:text, "Catalog").click
|
28
|
+
link(:text, "All Books").click
|
29
|
+
link(:text, /Agile Retrospectives/).click
|
30
|
+
puts "FAILURE prag" unless contains_text("Dave Hoover")
|
31
|
+
end
|
32
|
+
|
33
|
+
def safari.ala
|
34
|
+
goto("http://alistapart.com/")
|
35
|
+
text_field(:id, "search").set("grail")
|
36
|
+
checkbox(:id, "incdisc").set
|
37
|
+
button(:id, "submit").click
|
38
|
+
puts "FAILURE ala" unless contains_text('Search Results for “grail”')
|
39
|
+
end
|
40
|
+
|
41
|
+
def safari.amazon
|
42
|
+
goto("http://amazon.com")
|
43
|
+
select_list(:name, "url").select("Toys")
|
44
|
+
select_list(:name, "url").select_value("index=software")
|
45
|
+
text_field(:name, "keywords").set("Orion")
|
46
|
+
image(:name, "Go").click
|
47
|
+
puts "FAILURE amazon" unless contains_text("Master of Orion (Original Release) (PC)")
|
48
|
+
end
|
49
|
+
|
50
|
+
def safari.google_advanced
|
51
|
+
goto("http://www.google.com/advanced_search")
|
52
|
+
radio(:name, "safe", "active").set
|
53
|
+
radio(:name, "safe", "images").set
|
54
|
+
# Safari doesn't support label clicking ... perhaps I should raise an Exception
|
55
|
+
label(:text, "No filtering").click
|
56
|
+
radio(:id, "ss").set
|
57
|
+
text_field(:name, "as_q").set("obtiva")
|
58
|
+
button(:name, "btnG").click
|
59
|
+
puts "FAILURE google" unless contains_text("RailsConf Facebook")
|
60
|
+
end
|
61
|
+
|
62
|
+
def safari.reddit
|
63
|
+
goto("http://reddit.com/")
|
64
|
+
text_field(:name, "user").set("foo")
|
65
|
+
password(:name, "passwd").set("bar")
|
66
|
+
form(:index, 1).submit
|
67
|
+
puts "FAILURE reddit" unless contains_text("foo") and contains_text("logout")
|
68
|
+
end
|
69
|
+
|
70
|
+
safari.google_to_prag
|
71
|
+
safari.ala
|
72
|
+
safari.amazon
|
73
|
+
safari.google_advanced
|
74
|
+
safari.reddit
|
75
|
+
|
76
|
+
safari.close
|
data/watir/exceptions.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Watir
|
2
|
+
module Exception
|
3
|
+
|
4
|
+
# Root class for all Watir Exceptions
|
5
|
+
class WatirException < RuntimeError
|
6
|
+
def initialize(message="")
|
7
|
+
super(message)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# This exception is thrown if an attempt is made to access an object that doesn't exist
|
12
|
+
class UnknownObjectException < WatirException; end
|
13
|
+
# This exception is thrown if an attempt is made to access an object that is in a disabled state
|
14
|
+
class ObjectDisabledException < WatirException; end
|
15
|
+
# This exception is thrown if an attempt is made to access a frame that cannot be found
|
16
|
+
class UnknownFrameException< WatirException; end
|
17
|
+
# This exception is thrown if an attempt is made to access a form that cannot be found
|
18
|
+
class UnknownFormException< WatirException; end
|
19
|
+
# This exception is thrown if an attempt is made to access an object that is in a read only state
|
20
|
+
class ObjectReadOnlyException < WatirException; end
|
21
|
+
# This exception is thrown if an attempt is made to access an object when the specified value cannot be found
|
22
|
+
class NoValueFoundException < WatirException; end
|
23
|
+
# This exception gets raised if part of finding an object is missing
|
24
|
+
class MissingWayOfFindingObjectException < WatirException; end
|
25
|
+
# this exception is raised if an attempt is made to access a table cell that doesnt exist
|
26
|
+
class UnknownCellException < WatirException; end
|
27
|
+
# This exception is thrown if the window cannot be found
|
28
|
+
class NoMatchingWindowFoundException < WatirException; end
|
29
|
+
# This exception is thrown if an attemp is made to acces the status bar of the browser when it doesnt exist
|
30
|
+
class NoStatusBarException < WatirException; end
|
31
|
+
# This exception is thrown if an http error, such as a 404, 500 etc is encountered while navigating
|
32
|
+
class NavigationException < WatirException; end
|
33
|
+
# This exception is raised if a timeout is exceeded
|
34
|
+
class TimeOutException < WatirException
|
35
|
+
def initialize(duration, timeout)
|
36
|
+
@duration, @timeout = duration, timeout
|
37
|
+
end
|
38
|
+
attr_reader :duration, :timeout
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: safariwatir
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2006-07-21 00:00:00 -05:00
|
8
|
+
summary: Automated testing tool for web applications.
|
9
|
+
require_paths:
|
10
|
+
- .
|
11
|
+
email: dave.hoover@gmail.com
|
12
|
+
homepage: http://safariwatir.rubyforge.org/
|
13
|
+
rubyforge_project: safariwatir
|
14
|
+
description: WATIR stands for "Web Application Testing in Ruby". See WATIR project for more information. This is simply a Safari-version of the original IE-only WATIR.
|
15
|
+
autorequire: safariwatir
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors:
|
29
|
+
- Dave Hoover
|
30
|
+
files:
|
31
|
+
- safariwatir.rb
|
32
|
+
- safariwatir_script.rb
|
33
|
+
- safariwatir/core_ext.rb
|
34
|
+
- safariwatir/scripter.rb
|
35
|
+
- watir/exceptions.rb
|
36
|
+
test_files: []
|
37
|
+
|
38
|
+
rdoc_options: []
|
39
|
+
|
40
|
+
extra_rdoc_files: []
|
41
|
+
|
42
|
+
executables: []
|
43
|
+
|
44
|
+
extensions: []
|
45
|
+
|
46
|
+
requirements:
|
47
|
+
- Mac OS X running Safari
|
48
|
+
- Some features require you to turn on "Enable access for assistive devices" in System Preferences > Universal Access
|
49
|
+
dependencies: []
|
50
|
+
|