safariwatir 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/safariwatir.rb +77 -23
- data/safariwatir/core_ext.rb +6 -0
- data/safariwatir/scripter.rb +125 -45
- data/safariwatir_script.rb +31 -4
- metadata +2 -2
data/safariwatir.rb
CHANGED
@@ -1,10 +1,18 @@
|
|
1
|
+
require 'watir/exceptions'
|
1
2
|
require 'safariwatir/scripter'
|
2
3
|
require 'safariwatir/core_ext'
|
3
|
-
require 'watir/exceptions'
|
4
4
|
|
5
5
|
module Watir
|
6
6
|
include Watir::Exception
|
7
7
|
|
8
|
+
module Clickable
|
9
|
+
def click
|
10
|
+
@scripter.highlight(self) do
|
11
|
+
click_element
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
8
16
|
module Elements
|
9
17
|
class AlertWindow
|
10
18
|
def initialize(scripter)
|
@@ -22,6 +30,11 @@ module Watir
|
|
22
30
|
|
23
31
|
# Hooks for subclasses
|
24
32
|
def tag; end
|
33
|
+
def speak; end
|
34
|
+
|
35
|
+
def name
|
36
|
+
self.class.name.split("::").last
|
37
|
+
end
|
25
38
|
|
26
39
|
def operate(&block)
|
27
40
|
send("operate_by_" + how.to_s, &block)
|
@@ -36,7 +49,7 @@ module Watir
|
|
36
49
|
@scripter.operate_by_index(self, &block)
|
37
50
|
end
|
38
51
|
def operate_by_name(&block)
|
39
|
-
@scripter.
|
52
|
+
@scripter.operate_by_name(self, &block)
|
40
53
|
end
|
41
54
|
def operate_by_text(&block)
|
42
55
|
@scripter.operate_on_link(self, &block)
|
@@ -44,6 +57,9 @@ module Watir
|
|
44
57
|
def operate_by_url(&block)
|
45
58
|
@scripter.operate_on_link(self, &block)
|
46
59
|
end
|
60
|
+
def operate_by_value(&block)
|
61
|
+
@scripter.operate_by_input_value(self, &block)
|
62
|
+
end
|
47
63
|
end
|
48
64
|
|
49
65
|
class Form < HtmlElement
|
@@ -56,25 +72,44 @@ module Watir
|
|
56
72
|
def tag; "FORM"; end
|
57
73
|
end
|
58
74
|
|
59
|
-
class
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
75
|
+
class InputElement < HtmlElement
|
76
|
+
include Clickable
|
77
|
+
|
78
|
+
def speak
|
79
|
+
@scripter.speak_value_of(self)
|
64
80
|
end
|
65
81
|
|
66
82
|
# Hooks for subclasses
|
67
83
|
def by_value; end
|
68
84
|
end
|
69
85
|
|
70
|
-
class
|
86
|
+
class ContentElement < HtmlElement
|
87
|
+
include Clickable
|
88
|
+
|
89
|
+
def text
|
90
|
+
@scripter.get_text_for(self)
|
91
|
+
end
|
92
|
+
|
93
|
+
def speak
|
94
|
+
@scripter.speak_text_of(self)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class Button < InputElement
|
71
99
|
end
|
72
100
|
|
73
|
-
class Checkbox <
|
101
|
+
class Checkbox < InputElement
|
102
|
+
def_init :scripter, :how, :what, :value
|
103
|
+
def by_value
|
104
|
+
@value
|
105
|
+
end
|
74
106
|
alias :set :click
|
75
107
|
end
|
76
108
|
|
77
|
-
class
|
109
|
+
class Div < ContentElement
|
110
|
+
end
|
111
|
+
|
112
|
+
class Label < ContentElement
|
78
113
|
protected
|
79
114
|
|
80
115
|
def operate_by_text(&block)
|
@@ -82,7 +117,7 @@ module Watir
|
|
82
117
|
end
|
83
118
|
end
|
84
119
|
|
85
|
-
class Link <
|
120
|
+
class Link < InputElement
|
86
121
|
def click
|
87
122
|
@scripter.highlight(self) do
|
88
123
|
click_link
|
@@ -90,29 +125,31 @@ module Watir
|
|
90
125
|
end
|
91
126
|
end
|
92
127
|
|
93
|
-
class Radio <
|
94
|
-
def_init :scripter, :how, :what, :value
|
95
|
-
def by_value
|
96
|
-
@value
|
97
|
-
end
|
98
|
-
alias :set :click
|
128
|
+
class Radio < Checkbox
|
99
129
|
end
|
100
130
|
|
101
|
-
class SelectList <
|
131
|
+
class SelectList < InputElement
|
102
132
|
def select(label)
|
103
133
|
@scripter.highlight(self) do
|
104
|
-
select_option(
|
134
|
+
select_option(:text, label)
|
105
135
|
end
|
106
136
|
end
|
107
137
|
|
108
138
|
def select_value(value)
|
109
139
|
@scripter.highlight(self) do
|
110
|
-
select_option(
|
140
|
+
select_option(:value, value)
|
111
141
|
end
|
112
142
|
end
|
143
|
+
|
144
|
+
def speak
|
145
|
+
@scripter.speak_options_for(self)
|
146
|
+
end
|
113
147
|
end
|
114
148
|
|
115
|
-
class
|
149
|
+
class Span < ContentElement
|
150
|
+
end
|
151
|
+
|
152
|
+
class TextField < InputElement
|
116
153
|
def set(value)
|
117
154
|
@scripter.highlight(self) do
|
118
155
|
clear_text_input
|
@@ -121,6 +158,15 @@ module Watir
|
|
121
158
|
end
|
122
159
|
end
|
123
160
|
end
|
161
|
+
|
162
|
+
def getContents
|
163
|
+
@scripter.get_value_for(self)
|
164
|
+
end
|
165
|
+
|
166
|
+
def verify_contains(expected)
|
167
|
+
actual = getContents
|
168
|
+
expected == actual
|
169
|
+
end
|
124
170
|
end
|
125
171
|
|
126
172
|
class Password < TextField
|
@@ -162,8 +208,12 @@ module Watir
|
|
162
208
|
Button.new(scripter, how, what)
|
163
209
|
end
|
164
210
|
|
165
|
-
def checkbox(how, what)
|
166
|
-
Checkbox.new(scripter, how, what)
|
211
|
+
def checkbox(how, what, value = nil)
|
212
|
+
Checkbox.new(scripter, how, what, value)
|
213
|
+
end
|
214
|
+
|
215
|
+
def div(how, what)
|
216
|
+
Div.new(scripter, how, what)
|
167
217
|
end
|
168
218
|
|
169
219
|
def form(how, what)
|
@@ -194,6 +244,10 @@ module Watir
|
|
194
244
|
SelectList.new(scripter, how, what)
|
195
245
|
end
|
196
246
|
|
247
|
+
def span(how, what)
|
248
|
+
Span.new(scripter, how, what)
|
249
|
+
end
|
250
|
+
|
197
251
|
def text_field(how, what)
|
198
252
|
TextField.new(scripter, how, what)
|
199
253
|
end
|
data/safariwatir/core_ext.rb
CHANGED
data/safariwatir/scripter.rb
CHANGED
@@ -1,7 +1,28 @@
|
|
1
1
|
module Watir
|
2
|
-
|
2
|
+
NOT_FOUND = "__safari_watir_unfound__"
|
3
|
+
NO_RESPONSE = "__safari_watir_no_response__"
|
3
4
|
|
4
|
-
|
5
|
+
module JavaScripter
|
6
|
+
def js_operation(locator, operation)
|
7
|
+
js_wrapper(%|
|
8
|
+
#{locator}
|
9
|
+
if (element) {
|
10
|
+
#{operation}
|
11
|
+
} else {
|
12
|
+
return '#{NOT_FOUND}';
|
13
|
+
}|)
|
14
|
+
end
|
15
|
+
|
16
|
+
def js_wrapper(script)
|
17
|
+
%|set response to do JavaScript "#{script}" in document 1|
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class AppleScripter
|
22
|
+
include JavaScripter
|
23
|
+
include Watir::Exception
|
24
|
+
|
25
|
+
TIMEOUT = 10
|
5
26
|
|
6
27
|
def initialize
|
7
28
|
ensure_window_ready
|
@@ -18,9 +39,25 @@ module Watir
|
|
18
39
|
def navigate_to(url)
|
19
40
|
execute_and_wait(%|set URL in document 1 to "#{url}"|)
|
20
41
|
end
|
42
|
+
|
43
|
+
def speak_value_of(element = @element)
|
44
|
+
speak(get_value_for(element))
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_text_for(element = @element)
|
48
|
+
execute(element.operate { %|return element.innerText| }, element)
|
49
|
+
end
|
50
|
+
|
51
|
+
def speak_text_of(element = @element)
|
52
|
+
speak(element.text)
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_value_for(element = @element)
|
56
|
+
execute(element.operate { %|return element.value;| }, element)
|
57
|
+
end
|
21
58
|
|
22
59
|
def document_text
|
23
|
-
execute(%|
|
60
|
+
execute(js_wrapper(%|document.getElementsByTagName('BODY').item(0).innerText;|))
|
24
61
|
end
|
25
62
|
|
26
63
|
def highlight(element, &block)
|
@@ -28,13 +65,26 @@ module Watir
|
|
28
65
|
%|element.focus();
|
29
66
|
element.originalColor = element.style.backgroundColor;
|
30
67
|
element.style.backgroundColor = 'yellow';|
|
31
|
-
end)
|
68
|
+
end, element)
|
32
69
|
|
33
70
|
@element = element
|
34
71
|
instance_eval(&block)
|
35
72
|
@element = nil
|
36
73
|
|
37
|
-
|
74
|
+
execute_and_ignore(element.operate { %|element.style.backgroundColor = element.originalColor;| })
|
75
|
+
end
|
76
|
+
|
77
|
+
def speak_options_for(element = @element)
|
78
|
+
values = execute(element.operate do
|
79
|
+
%|var values = '';
|
80
|
+
for (var i = 0; i < element.options.length; i++) {
|
81
|
+
if (element.options[i].selected == true) {
|
82
|
+
values += ' ' + element.options[i].text;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
return values|
|
86
|
+
end, element)
|
87
|
+
speak(values)
|
38
88
|
end
|
39
89
|
|
40
90
|
def select_option(option_how, option_what, element = @element)
|
@@ -44,18 +94,18 @@ element.style.backgroundColor = 'yellow';|
|
|
44
94
|
element.options[i].selected = true;
|
45
95
|
}
|
46
96
|
}|
|
47
|
-
end)
|
97
|
+
end, element)
|
48
98
|
end
|
49
|
-
|
99
|
+
|
50
100
|
def clear_text_input(element = @element)
|
51
|
-
execute(element.operate { %|element.value = '';| })
|
101
|
+
execute(element.operate { %|element.value = '';| }, element)
|
52
102
|
end
|
53
103
|
|
54
104
|
def append_text_input(value, element = @element)
|
55
105
|
execute(element.operate do
|
56
106
|
%|element.value += '#{value}';
|
57
107
|
element.setSelectionRange(element.value.length, element.value.length);|
|
58
|
-
end)
|
108
|
+
end, element)
|
59
109
|
end
|
60
110
|
|
61
111
|
# TODO need a better approach for "waiting"
|
@@ -64,8 +114,8 @@ element.setSelectionRange(element.value.length, element.value.length);|
|
|
64
114
|
end
|
65
115
|
|
66
116
|
def click_link(element = @element)
|
67
|
-
click = find_link(element)
|
68
|
-
|
117
|
+
click = find_link(element) + %|
|
118
|
+
var click = document.createEvent('HTMLEvents');
|
69
119
|
click.initEvent('click', true, true);
|
70
120
|
if (element.onclick) {
|
71
121
|
if (false != element.onclick(click)) {
|
@@ -74,23 +124,22 @@ if (element.onclick) {
|
|
74
124
|
} else {
|
75
125
|
return element.href;
|
76
126
|
}|
|
77
|
-
end
|
78
127
|
execute_and_wait(%|set target to do JavaScript "#{click}" in document 1
|
79
128
|
set URL in document 1 to target|)
|
80
129
|
end
|
81
130
|
|
82
|
-
def operate_on_link(element
|
83
|
-
|
131
|
+
def operate_on_link(element)
|
132
|
+
js_operation(find_link(element), yield)
|
84
133
|
end
|
85
134
|
|
86
135
|
def find_link(element)
|
87
|
-
%|
|
136
|
+
%|var element = undefined;
|
137
|
+
for (var i = 0; i < document.links.length; i++) {
|
88
138
|
if (document.links[i].#{handle_match(element)}) {
|
89
|
-
|
90
|
-
#{yield}
|
139
|
+
element = document.links[i];
|
91
140
|
break;
|
92
141
|
}
|
93
|
-
}|
|
142
|
+
}|
|
94
143
|
end
|
95
144
|
private :find_link
|
96
145
|
|
@@ -105,17 +154,29 @@ set URL in document 1 to target|)
|
|
105
154
|
end
|
106
155
|
private :handle_match
|
107
156
|
|
108
|
-
def
|
109
|
-
%|
|
157
|
+
def operate_by_input_value(element)
|
158
|
+
js_operation(%|
|
159
|
+
var elements = document.getElementsByTagName('INPUT');
|
160
|
+
var element = undefined;
|
161
|
+
for (var i = 0; i < elements.length; i++) {
|
162
|
+
if (elements[i].tagName != 'META') {
|
163
|
+
if (elements[i].value == '#{element.what}') {
|
164
|
+
element = elements[i];
|
165
|
+
break;
|
166
|
+
}
|
167
|
+
}
|
168
|
+
}|, yield)
|
169
|
+
end
|
170
|
+
|
171
|
+
def operate_by_name(element)
|
172
|
+
js_operation(%|
|
110
173
|
var elements = document.getElementsByName('#{element.what}');
|
111
|
-
var element;
|
174
|
+
var element = undefined;
|
112
175
|
for (var i = 0; i < elements.length; i++) {
|
113
176
|
if (elements[i].tagName != 'META') {
|
114
177
|
#{handle_form_element_name_match(element)}
|
115
178
|
}
|
116
|
-
}
|
117
|
-
#{yield}
|
118
|
-
" in document 1|
|
179
|
+
}|, yield)
|
119
180
|
end
|
120
181
|
|
121
182
|
def handle_form_element_name_match(element)
|
@@ -131,37 +192,29 @@ for (var i = 0; i < elements.length; i++) {
|
|
131
192
|
private :handle_form_element_name_match
|
132
193
|
|
133
194
|
def operate_by_id(element)
|
134
|
-
|
135
|
-
var element = document.getElementById('#{element.what}');
|
136
|
-
#{yield}" in document 1|
|
195
|
+
js_operation("var element = document.getElementById('#{element.what}');", yield)
|
137
196
|
end
|
138
197
|
|
139
198
|
def operate_by_index(element)
|
140
|
-
%|
|
141
|
-
var elements = document.getElementsByTagName('#{element.tag}');
|
142
|
-
var element = elements[#{element.what}];
|
143
|
-
#{yield}" in document 1|
|
199
|
+
js_operation(%|var element = document.getElementsByTagName('#{element.tag}')[#{element.what}];|, yield)
|
144
200
|
end
|
145
201
|
|
146
202
|
def operate_on_label(element)
|
147
|
-
%|
|
148
|
-
var
|
149
|
-
var element;
|
203
|
+
js_operation(%|var elements = document.getElementsByTagName('LABEL');
|
204
|
+
var element = undefined;
|
150
205
|
for (var i = 0; i < elements.length; i++) {
|
151
|
-
if (elements[i].
|
206
|
+
if (elements[i].innerText == '#{element.what}') {
|
152
207
|
element = elements[i];
|
153
|
-
#{yield}
|
154
208
|
break;
|
155
209
|
}
|
156
|
-
}
|
157
|
-
" in document 1|
|
210
|
+
}|, yield)
|
158
211
|
end
|
159
212
|
|
160
213
|
def submit_form(element)
|
161
214
|
execute_and_wait(element.operate { %|element.submit();| })
|
162
215
|
end
|
163
216
|
|
164
|
-
def
|
217
|
+
def click_alert_ok
|
165
218
|
execute_system_events(%|
|
166
219
|
tell window 1
|
167
220
|
if button named "OK" exists then
|
@@ -172,13 +225,32 @@ end tell|)
|
|
172
225
|
|
173
226
|
private
|
174
227
|
|
175
|
-
def execute(script)
|
176
|
-
|
228
|
+
def execute!(script)
|
229
|
+
# puts script
|
177
230
|
`osascript <<SCRIPT
|
178
231
|
tell application "Safari"
|
232
|
+
set response to "#{NO_RESPONSE}"
|
179
233
|
#{script}
|
234
|
+
response
|
180
235
|
end tell
|
181
|
-
SCRIPT
|
236
|
+
SCRIPT`.chomp
|
237
|
+
end
|
238
|
+
|
239
|
+
def execute(script, element = nil)
|
240
|
+
response = execute! script
|
241
|
+
case response
|
242
|
+
when NOT_FOUND:
|
243
|
+
raise UnknownObjectException, "Unable to locate #{element.name} element with #{element.how} of #{element.what}."
|
244
|
+
when NO_RESPONSE:
|
245
|
+
nil
|
246
|
+
else
|
247
|
+
response
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def execute_and_ignore(script)
|
252
|
+
execute! script
|
253
|
+
nil
|
182
254
|
end
|
183
255
|
|
184
256
|
# Must have "Enable access for assistive devices" checked in System Preferences > Universal Access
|
@@ -188,19 +260,20 @@ tell application "System Events" to tell process "Safari"
|
|
188
260
|
#{script}
|
189
261
|
end tell
|
190
262
|
SCRIPT`
|
263
|
+
nil
|
191
264
|
end
|
192
265
|
|
193
|
-
def execute_and_wait(script)
|
266
|
+
def execute_and_wait(script, element = nil)
|
194
267
|
execute(%|
|
195
268
|
#{script}
|
196
269
|
delay 2
|
197
|
-
repeat with i from 1 to #{
|
270
|
+
repeat with i from 1 to #{TIMEOUT}
|
198
271
|
if (do JavaScript "document.readyState" in document 1) is "complete" then
|
199
272
|
exit repeat
|
200
273
|
else
|
201
274
|
delay 1
|
202
275
|
end if
|
203
|
-
end repeat
|
276
|
+
end repeat|, element)
|
204
277
|
end
|
205
278
|
|
206
279
|
def ensure_window_ready
|
@@ -211,5 +284,12 @@ if length of document_list is 0 then
|
|
211
284
|
make new document
|
212
285
|
end if|)
|
213
286
|
end
|
287
|
+
|
288
|
+
def speak(string)
|
289
|
+
`osascript <<SCRIPT
|
290
|
+
say "#{string.quote_safe}"
|
291
|
+
SCRIPT`
|
292
|
+
nil
|
293
|
+
end
|
214
294
|
end # class AppleScripter
|
215
295
|
end
|
data/safariwatir_script.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
-
require '
|
1
|
+
require 'rubygems'
|
2
|
+
require 'safariwatir'
|
2
3
|
|
3
4
|
# TODO
|
4
|
-
# Need to give feedback when browser dies
|
5
|
+
# Need to give feedback when browser dies
|
5
6
|
# Be more attached to the Safari window, if a different window is selected, the AppleScript executes against it
|
6
7
|
# Verify onclick is working for buttons and links
|
8
|
+
# TextFields should not respond to button method, etc.
|
7
9
|
|
8
|
-
# Unsupported Elements:
|
10
|
+
# Unsupported Elements: UL/OL
|
9
11
|
# Use dynamic properties for Javascript optimization?
|
10
12
|
# Will I need to push more functionality into AppleScript to speed things up?
|
11
13
|
# Angrez is looking into the Ruby/AppleScript binding
|
12
|
-
# Watir Rails Plugin needed
|
14
|
+
# Watir Rails Plugin needed -> Watir test generator, fixtures and AR in-test, Browser Factory
|
13
15
|
|
14
16
|
# SAFARI ISSUES
|
15
17
|
# Labels are not clickable
|
@@ -67,10 +69,35 @@ def safari.reddit
|
|
67
69
|
puts "FAILURE reddit" unless contains_text("foo") and contains_text("logout")
|
68
70
|
end
|
69
71
|
|
72
|
+
def safari.colbert
|
73
|
+
goto("http://www.colbertnation.com/cn/contact.php")
|
74
|
+
text_field(:name, "formmessage").set("Beware the Bear")
|
75
|
+
button(:value, "Send Email").click
|
76
|
+
puts "FAILURE colbert" unless text_field(:name, "formmessage").verify_contains("Enter message")
|
77
|
+
end
|
78
|
+
|
79
|
+
def safari.redsquirrel
|
80
|
+
goto("http://redsquirrel.com/")
|
81
|
+
begin
|
82
|
+
text_field(:id, "not_there").set("imaginary")
|
83
|
+
puts "FAILURE squirrel text no e"
|
84
|
+
rescue Watir::UnknownObjectException => e
|
85
|
+
puts "FAILURE squirrel text bad e" unless e.message =~ /not_there/
|
86
|
+
end
|
87
|
+
begin
|
88
|
+
link(:text, "no_where").click
|
89
|
+
puts "FAILURE squirrel link no e"
|
90
|
+
rescue Watir::UnknownObjectException => e
|
91
|
+
puts "FAILURE squirrel link bad e" unless e.message =~ /no_where/
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
70
95
|
safari.google_to_prag
|
71
96
|
safari.ala
|
72
97
|
safari.amazon
|
73
98
|
safari.google_advanced
|
74
99
|
safari.reddit
|
100
|
+
safari.colbert
|
101
|
+
safari.redsquirrel
|
75
102
|
|
76
103
|
safari.close
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
|
|
3
3
|
specification_version: 1
|
4
4
|
name: safariwatir
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date: 2006-07-
|
6
|
+
version: 0.1.1
|
7
|
+
date: 2006-07-23 00:00:00 -05:00
|
8
8
|
summary: Automated testing tool for web applications.
|
9
9
|
require_paths:
|
10
10
|
- .
|