safariwatir 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.operate_on_form_element(self, &block)
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 ClickableElement < HtmlElement
60
- def click
61
- @scripter.highlight(self) do
62
- click_element
63
- end
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 Button < ClickableElement
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 < ClickableElement
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 Label < ClickableElement
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 < ClickableElement
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 < ClickableElement
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 < ClickableElement
131
+ class SelectList < InputElement
102
132
  def select(label)
103
133
  @scripter.highlight(self) do
104
- select_option("text", label)
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("value", value)
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 TextField < ClickableElement
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
@@ -10,4 +10,10 @@ class Class
10
10
  constructor << "end"
11
11
  class_eval(constructor)
12
12
  end
13
+ end
14
+
15
+ class String
16
+ def quote_safe
17
+ gsub(/"/, '\"')
18
+ end
13
19
  end
@@ -1,7 +1,28 @@
1
1
  module Watir
2
- class AppleScripter
2
+ NOT_FOUND = "__safari_watir_unfound__"
3
+ NO_RESPONSE = "__safari_watir_no_response__"
3
4
 
4
- @@timeout = 10
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(%|do Javascript "document.getElementsByTagName('BODY').item(0).innerText;" in document 1|)
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
- execute(element.operate { %|element.style.backgroundColor = element.originalColor;| })
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) do
68
- %|var click = document.createEvent('HTMLEvents');
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, &block)
83
- %|do JavaScript "#{find_link(element, &block)}" in document 1|
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
- %|for (var i = 0; i < document.links.length; i++) {
136
+ %|var element = undefined;
137
+ for (var i = 0; i < document.links.length; i++) {
88
138
  if (document.links[i].#{handle_match(element)}) {
89
- var element = document.links[i];
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 operate_on_form_element(element)
109
- %|do JavaScript "
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
- %|do JavaScript "
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
- %|do JavaScript "
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
- %|do JavaScript "
148
- var elements = document.getElementsByTagName('LABEL');
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].textContent == '#{element.what}') {
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 click_link_ok
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
- # puts script
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 #{@@timeout}
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
@@ -1,15 +1,17 @@
1
- require 'safari'
1
+ require 'rubygems'
2
+ require 'safariwatir'
2
3
 
3
4
  # TODO
4
- # Need to give feedback when browser dies or when elements are not found
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: Textarea, Div, UL/OL, Span
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.0
7
- date: 2006-07-21 00:00:00 -05:00
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
  - .