safariwatir 0.1.0 → 0.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/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
|
- .
|