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.
@@ -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
  - .