safariwatir 0.1.1 → 0.1.2
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 +207 -60
- data/safariwatir/scripter.rb +216 -74
- data/safariwatir_script.rb +39 -7
- metadata +2 -2
data/safariwatir.rb
CHANGED
@@ -5,33 +5,67 @@ require 'safariwatir/core_ext'
|
|
5
5
|
module Watir
|
6
6
|
include Watir::Exception
|
7
7
|
|
8
|
-
module
|
9
|
-
def
|
10
|
-
@scripter.
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
module PageContainer
|
9
|
+
def html
|
10
|
+
@scripter.document_html
|
11
|
+
end
|
12
|
+
|
13
|
+
def text
|
14
|
+
@scripter.document_text
|
15
|
+
end
|
14
16
|
end
|
15
17
|
|
16
|
-
module
|
18
|
+
module Container
|
19
|
+
attr_reader :scripter
|
20
|
+
private :scripter
|
21
|
+
|
22
|
+
module Clickable
|
23
|
+
def click
|
24
|
+
@scripter.highlight(self) do
|
25
|
+
click_element
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
17
30
|
class AlertWindow
|
18
|
-
|
19
|
-
@scripter = scripter
|
20
|
-
end
|
31
|
+
def_init :scripter
|
21
32
|
|
22
33
|
def click
|
23
34
|
@scripter.click_alert_ok
|
24
35
|
end
|
25
36
|
end
|
26
37
|
|
38
|
+
class Frame
|
39
|
+
include Container
|
40
|
+
include PageContainer
|
41
|
+
|
42
|
+
attr_reader :name
|
43
|
+
|
44
|
+
def initialize(scripter, name)
|
45
|
+
@name = name
|
46
|
+
@scripter = scripter.for_frame(self)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
27
50
|
class HtmlElement
|
28
51
|
def_init :scripter, :how, :what
|
29
52
|
attr_reader :how, :what
|
30
53
|
|
31
|
-
#
|
32
|
-
def tag
|
33
|
-
|
54
|
+
# overridden in derivitives
|
55
|
+
def tag
|
56
|
+
raise RuntimeError, "tag not provided for #{name}"
|
57
|
+
end
|
34
58
|
|
59
|
+
# overridden in derivitives
|
60
|
+
def speak
|
61
|
+
@scripter.speak("#{name}'s don't know how to speak.")
|
62
|
+
end
|
63
|
+
|
64
|
+
def exists?
|
65
|
+
@scripter.element_exists?(self)
|
66
|
+
end
|
67
|
+
alias :exist? :exists?
|
68
|
+
|
35
69
|
def name
|
36
70
|
self.class.name.split("::").last
|
37
71
|
end
|
@@ -79,12 +113,15 @@ module Watir
|
|
79
113
|
@scripter.speak_value_of(self)
|
80
114
|
end
|
81
115
|
|
82
|
-
|
116
|
+
def tag; "INPUT"; end
|
117
|
+
|
118
|
+
# Hook for derivitives
|
83
119
|
def by_value; end
|
84
120
|
end
|
85
121
|
|
86
122
|
class ContentElement < HtmlElement
|
87
123
|
include Clickable
|
124
|
+
include Container
|
88
125
|
|
89
126
|
def text
|
90
127
|
@scripter.get_text_for(self)
|
@@ -107,9 +144,12 @@ module Watir
|
|
107
144
|
end
|
108
145
|
|
109
146
|
class Div < ContentElement
|
147
|
+
def tag; "DIV"; end
|
110
148
|
end
|
111
149
|
|
112
150
|
class Label < ContentElement
|
151
|
+
def tag; "LABEL"; end
|
152
|
+
|
113
153
|
protected
|
114
154
|
|
115
155
|
def operate_by_text(&block)
|
@@ -117,12 +157,14 @@ module Watir
|
|
117
157
|
end
|
118
158
|
end
|
119
159
|
|
120
|
-
class Link <
|
160
|
+
class Link < ContentElement
|
121
161
|
def click
|
122
162
|
@scripter.highlight(self) do
|
123
163
|
click_link
|
124
164
|
end
|
125
165
|
end
|
166
|
+
|
167
|
+
def tag; "A"; end
|
126
168
|
end
|
127
169
|
|
128
170
|
class Radio < Checkbox
|
@@ -130,23 +172,110 @@ module Watir
|
|
130
172
|
|
131
173
|
class SelectList < InputElement
|
132
174
|
def select(label)
|
133
|
-
|
134
|
-
select_option(:text, label)
|
135
|
-
end
|
175
|
+
option(:text, label).select
|
136
176
|
end
|
137
|
-
|
177
|
+
|
138
178
|
def select_value(value)
|
139
|
-
|
140
|
-
|
141
|
-
|
179
|
+
option(:value, value).select
|
180
|
+
end
|
181
|
+
|
182
|
+
def option(how, what)
|
183
|
+
Option.new(@scripter, self, how, what)
|
142
184
|
end
|
143
185
|
|
144
186
|
def speak
|
145
187
|
@scripter.speak_options_for(self)
|
146
188
|
end
|
189
|
+
|
190
|
+
def tag; "SELECT"; end
|
191
|
+
end
|
192
|
+
|
193
|
+
class Option < InputElement
|
194
|
+
def_init :scripter, :select_list, :how, :what
|
195
|
+
|
196
|
+
def select
|
197
|
+
@scripter.highlight(self) do
|
198
|
+
select_option
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def operate(&block)
|
203
|
+
@select_list.operate(&block)
|
204
|
+
end
|
205
|
+
|
206
|
+
def exists?
|
207
|
+
@scripter.option_exists?(self)
|
208
|
+
end
|
209
|
+
alias :exist? :exists?
|
210
|
+
|
211
|
+
def tag; "OPTION"; end
|
147
212
|
end
|
148
213
|
|
149
214
|
class Span < ContentElement
|
215
|
+
def tag; "SPAN"; end
|
216
|
+
end
|
217
|
+
|
218
|
+
class Table
|
219
|
+
def_init :scripter, :how, :what
|
220
|
+
attr_reader :how, :what
|
221
|
+
|
222
|
+
def each
|
223
|
+
# TODO
|
224
|
+
end
|
225
|
+
|
226
|
+
def [](index)
|
227
|
+
TableRow.new(@scripter, :index, index, self)
|
228
|
+
end
|
229
|
+
|
230
|
+
def row_count
|
231
|
+
# TODO
|
232
|
+
end
|
233
|
+
|
234
|
+
def column_count
|
235
|
+
# TODO
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class TableRow
|
240
|
+
def initialize(scripter, how, what, table = nil)
|
241
|
+
@scripter = scripter
|
242
|
+
@how = how
|
243
|
+
@what = what
|
244
|
+
@table = table
|
245
|
+
end
|
246
|
+
|
247
|
+
attr_reader :table, :how, :what
|
248
|
+
|
249
|
+
def each
|
250
|
+
# TODO
|
251
|
+
end
|
252
|
+
|
253
|
+
def [](index)
|
254
|
+
TableCell.new(@scripter, :index, index, self)
|
255
|
+
end
|
256
|
+
|
257
|
+
def column_count
|
258
|
+
# TODO
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
class TableCell < ContentElement
|
263
|
+
def initialize(scripter, how, what, row = nil)
|
264
|
+
@scripter = scripter.for_table(self)
|
265
|
+
@how = how
|
266
|
+
@what = what
|
267
|
+
@row = row
|
268
|
+
end
|
269
|
+
|
270
|
+
attr_reader :how, :what, :row
|
271
|
+
|
272
|
+
def operate_by_index(&block)
|
273
|
+
@scripter.operate_by_table_cell_index(self, &block)
|
274
|
+
end
|
275
|
+
|
276
|
+
def operate(&block)
|
277
|
+
@scripter.operate_by_table_cell(self, &block)
|
278
|
+
end
|
150
279
|
end
|
151
280
|
|
152
281
|
class TextField < InputElement
|
@@ -171,43 +300,18 @@ module Watir
|
|
171
300
|
|
172
301
|
class Password < TextField
|
173
302
|
end
|
174
|
-
end
|
175
|
-
|
176
|
-
class Safari
|
177
|
-
include Elements
|
178
303
|
|
179
|
-
attr_reader :scripter
|
180
304
|
|
181
|
-
|
182
|
-
safari = new
|
183
|
-
safari.goto(url) if url
|
184
|
-
safari
|
185
|
-
end
|
305
|
+
# Elements
|
186
306
|
|
187
|
-
def initialize
|
188
|
-
@scripter = AppleScripter.new
|
189
|
-
end
|
190
|
-
|
191
|
-
def close
|
192
|
-
scripter.close
|
193
|
-
end
|
194
|
-
|
195
|
-
def quit
|
196
|
-
scripter.quit
|
197
|
-
end
|
198
|
-
|
199
|
-
def alert
|
200
|
-
AlertWindow.new(scripter)
|
201
|
-
end
|
202
|
-
|
203
|
-
def goto(url)
|
204
|
-
scripter.navigate_to(url)
|
205
|
-
end
|
206
|
-
|
207
307
|
def button(how, what)
|
208
308
|
Button.new(scripter, how, what)
|
209
309
|
end
|
210
310
|
|
311
|
+
def cell(how, what)
|
312
|
+
TableCell.new(scripter, how, what)
|
313
|
+
end
|
314
|
+
|
211
315
|
def checkbox(how, what, value = nil)
|
212
316
|
Checkbox.new(scripter, how, what, value)
|
213
317
|
end
|
@@ -220,6 +324,10 @@ module Watir
|
|
220
324
|
Form.new(scripter, how, what)
|
221
325
|
end
|
222
326
|
|
327
|
+
def frame(name)
|
328
|
+
Frame.new(scripter, name)
|
329
|
+
end
|
330
|
+
|
223
331
|
def image(how, what)
|
224
332
|
Button.new(scripter, how, what)
|
225
333
|
end
|
@@ -240,6 +348,10 @@ module Watir
|
|
240
348
|
Radio.new(scripter, how, what, value)
|
241
349
|
end
|
242
350
|
|
351
|
+
def row(how, what)
|
352
|
+
TableRow.new(scripter, how, what)
|
353
|
+
end
|
354
|
+
|
243
355
|
def select_list(how, what)
|
244
356
|
SelectList.new(scripter, how, what)
|
245
357
|
end
|
@@ -247,21 +359,56 @@ module Watir
|
|
247
359
|
def span(how, what)
|
248
360
|
Span.new(scripter, how, what)
|
249
361
|
end
|
362
|
+
|
363
|
+
def table(how, what)
|
364
|
+
Table.new(scripter, how, what)
|
365
|
+
end
|
250
366
|
|
251
367
|
def text_field(how, what)
|
252
368
|
TextField.new(scripter, how, what)
|
253
369
|
end
|
254
370
|
|
255
371
|
def contains_text(what)
|
256
|
-
text = scripter.document_text
|
257
372
|
case what
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
373
|
+
when Regexp:
|
374
|
+
text =~ what
|
375
|
+
when String:
|
376
|
+
text.index(what)
|
377
|
+
else
|
378
|
+
raise MissingWayOfFindingObjectException
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
class Safari
|
384
|
+
include Container
|
385
|
+
include PageContainer
|
386
|
+
|
387
|
+
def self.start(url = nil)
|
388
|
+
safari = new
|
389
|
+
safari.goto(url) if url
|
390
|
+
safari
|
391
|
+
end
|
392
|
+
|
393
|
+
def initialize
|
394
|
+
@scripter = AppleScripter.new
|
395
|
+
@scripter.ensure_window_ready
|
396
|
+
end
|
397
|
+
|
398
|
+
def close
|
399
|
+
scripter.close
|
400
|
+
end
|
401
|
+
|
402
|
+
def quit
|
403
|
+
scripter.quit
|
404
|
+
end
|
405
|
+
|
406
|
+
def alert
|
407
|
+
AlertWindow.new(scripter)
|
408
|
+
end
|
409
|
+
|
410
|
+
def goto(url)
|
411
|
+
scripter.navigate_to(url)
|
265
412
|
end
|
266
413
|
end # class Safari
|
267
414
|
end
|
data/safariwatir/scripter.rb
CHANGED
@@ -1,38 +1,97 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/core_ext'
|
2
|
+
require File.dirname(__FILE__) + '/../watir/exceptions'
|
3
|
+
|
1
4
|
module Watir
|
2
|
-
|
5
|
+
ELEMENT_NOT_FOUND = "__safari_watir_element_unfound__"
|
6
|
+
FRAME_NOT_FOUND = "__safari_watir_frame_unfound__"
|
3
7
|
NO_RESPONSE = "__safari_watir_no_response__"
|
8
|
+
TABLE_CELL_NOT_FOUND = "__safari_watir_cell_unfound__"
|
4
9
|
|
5
|
-
|
6
|
-
def
|
7
|
-
|
10
|
+
class JavaScripter
|
11
|
+
def operate(locator, operation)
|
12
|
+
wrap(%|
|
8
13
|
#{locator}
|
9
14
|
if (element) {
|
10
15
|
#{operation}
|
11
16
|
} else {
|
12
|
-
return '#{
|
17
|
+
return '#{ELEMENT_NOT_FOUND}';
|
13
18
|
}|)
|
14
19
|
end
|
15
20
|
|
16
|
-
def
|
21
|
+
def wrap(script)
|
22
|
+
script.gsub! "DOCUMENT", "document"
|
17
23
|
%|set response to do JavaScript "#{script}" in document 1|
|
18
24
|
end
|
25
|
+
|
26
|
+
def find_cell(cell)
|
27
|
+
return %|getElementById('#{cell.what}')| if cell.how == :id
|
28
|
+
raise RuntimeError, "Unable to use #{cell.how} to find TableCell" unless cell.row
|
29
|
+
|
30
|
+
finder =
|
31
|
+
case cell.row.how
|
32
|
+
when :id:
|
33
|
+
%|getElementById('#{cell.row.what}')|
|
34
|
+
when :index:
|
35
|
+
case cell.row.table.how
|
36
|
+
when :id
|
37
|
+
%|getElementById('#{cell.row.table.what}').rows[#{cell.row.what-1}]|
|
38
|
+
when :index:
|
39
|
+
%|getElementsByTagName('TABLE')[#{cell.row.table.what-1}].rows[#{cell.row.what-1}]|
|
40
|
+
else
|
41
|
+
raise MissingWayOfFindingObjectException, "Table element does not support #{cell.row.table.how}"
|
42
|
+
end
|
43
|
+
else
|
44
|
+
raise MissingWayOfFindingObjectException, "TableRow element does not support #{cell.row.how}"
|
45
|
+
end
|
46
|
+
|
47
|
+
finder + %|.cells[#{cell.what-1}]|
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class FrameJavaScripter < JavaScripter
|
52
|
+
def initialize(frame)
|
53
|
+
@page_container = "parent.#{frame.name}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def wrap(script)
|
57
|
+
# add in frame name when referencing parent or document
|
58
|
+
script.gsub! "parent", "parent.#{@page_container}"
|
59
|
+
script.gsub! "document", "#{@page_container}.document"
|
60
|
+
super(script)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class TableJavaScripter < JavaScripter
|
65
|
+
def_init :cell
|
66
|
+
|
67
|
+
def wrap(script)
|
68
|
+
script.gsub! "document", "document." + find_cell(@cell)
|
69
|
+
super(script)
|
70
|
+
end
|
19
71
|
end
|
20
72
|
|
21
73
|
class AppleScripter
|
22
|
-
include JavaScripter
|
23
74
|
include Watir::Exception
|
24
75
|
|
76
|
+
attr_reader :js
|
77
|
+
private :js
|
78
|
+
|
25
79
|
TIMEOUT = 10
|
26
80
|
|
27
|
-
def initialize
|
28
|
-
|
81
|
+
def initialize(scripter = JavaScripter.new)
|
82
|
+
@js = scripter
|
29
83
|
end
|
30
|
-
|
31
|
-
def
|
32
|
-
execute(%|
|
84
|
+
|
85
|
+
def ensure_window_ready
|
86
|
+
execute(%|
|
87
|
+
activate
|
88
|
+
set document_list to every document
|
89
|
+
if length of document_list is 0 then
|
90
|
+
make new document
|
91
|
+
end if|)
|
33
92
|
end
|
34
93
|
|
35
|
-
def
|
94
|
+
def close
|
36
95
|
execute(%|quit|)
|
37
96
|
end
|
38
97
|
|
@@ -40,24 +99,25 @@ if (element) {
|
|
40
99
|
execute_and_wait(%|set URL in document 1 to "#{url}"|)
|
41
100
|
end
|
42
101
|
|
43
|
-
def speak_value_of(element = @element)
|
44
|
-
speak(get_value_for(element))
|
45
|
-
end
|
46
|
-
|
47
102
|
def get_text_for(element = @element)
|
48
103
|
execute(element.operate { %|return element.innerText| }, element)
|
49
104
|
end
|
50
105
|
|
51
|
-
def
|
52
|
-
|
106
|
+
def operate_by_table_cell(element = @element)
|
107
|
+
js.wrap(%|
|
108
|
+
var element = document;
|
109
|
+
if (element == undefined) {
|
110
|
+
return '#{TABLE_CELL_NOT_FOUND}';
|
111
|
+
}
|
112
|
+
#{yield}|)
|
53
113
|
end
|
54
|
-
|
114
|
+
|
55
115
|
def get_value_for(element = @element)
|
56
116
|
execute(element.operate { %|return element.value;| }, element)
|
57
117
|
end
|
58
118
|
|
59
119
|
def document_text
|
60
|
-
execute(
|
120
|
+
execute(js.wrap(%|document.getElementsByTagName('BODY').item(0).innerText;|))
|
61
121
|
end
|
62
122
|
|
63
123
|
def highlight(element, &block)
|
@@ -74,29 +134,38 @@ element.style.backgroundColor = 'yellow';|
|
|
74
134
|
execute_and_ignore(element.operate { %|element.style.backgroundColor = element.originalColor;| })
|
75
135
|
end
|
76
136
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
}
|
84
|
-
}
|
85
|
-
return values|
|
86
|
-
end, element)
|
87
|
-
speak(values)
|
137
|
+
def element_exists?(element = @element, &block)
|
138
|
+
block ||= Proc.new {}
|
139
|
+
execute(element.operate(&block), element)
|
140
|
+
return true
|
141
|
+
rescue UnknownObjectException
|
142
|
+
return false
|
88
143
|
end
|
89
144
|
|
90
|
-
def select_option(
|
145
|
+
def select_option(element = @element)
|
91
146
|
execute(element.operate do
|
92
|
-
|
93
|
-
if (element.options[i].#{option_how} == '#{option_what}') {
|
94
|
-
element.options[i].selected = true;
|
95
|
-
}
|
96
|
-
}|
|
147
|
+
handle_option(element, %|element.options[i].selected = true;|)
|
97
148
|
end, element)
|
98
149
|
end
|
99
150
|
|
151
|
+
def option_exists?(element = @element)
|
152
|
+
element_exists?(element) { handle_option(element) }
|
153
|
+
end
|
154
|
+
|
155
|
+
def handle_option(select_list, selection = nil)
|
156
|
+
%|var option_found = false;
|
157
|
+
for (var i = 0; i < element.options.length; i++) {
|
158
|
+
if (element.options[i].#{select_list.how} == '#{select_list.what}') {
|
159
|
+
#{selection}
|
160
|
+
option_found = true;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
if (!option_found) {
|
164
|
+
return '#{ELEMENT_NOT_FOUND}';
|
165
|
+
}|
|
166
|
+
end
|
167
|
+
private :handle_option
|
168
|
+
|
100
169
|
def clear_text_input(element = @element)
|
101
170
|
execute(element.operate { %|element.value = '';| }, element)
|
102
171
|
end
|
@@ -110,29 +179,69 @@ element.setSelectionRange(element.value.length, element.value.length);|
|
|
110
179
|
|
111
180
|
# TODO need a better approach for "waiting"
|
112
181
|
def click_element(element = @element)
|
113
|
-
execute_and_wait(element.operate { %|
|
182
|
+
execute_and_wait(element.operate { %|
|
183
|
+
if (element.click) {
|
184
|
+
element.click();
|
185
|
+
} else {
|
186
|
+
var event = document.createEvent('MouseEvents');
|
187
|
+
event.initEvent('click', true, true);
|
188
|
+
element.dispatchEvent(event);
|
189
|
+
|
190
|
+
if (element.onclick) {
|
191
|
+
var event = document.createEvent('HTMLEvents');
|
192
|
+
event.initEvent('click', true, true);
|
193
|
+
element.onclick(event);
|
194
|
+
}
|
195
|
+
}| })
|
114
196
|
end
|
115
197
|
|
116
198
|
def click_link(element = @element)
|
117
|
-
click =
|
118
|
-
|
199
|
+
click = %/
|
200
|
+
function baseTarget() {
|
201
|
+
var bases = document.getElementsByTagName('BASE');
|
202
|
+
if (bases.length > 0) {
|
203
|
+
return bases[0].target;
|
204
|
+
} else {
|
205
|
+
return;
|
206
|
+
}
|
207
|
+
}
|
208
|
+
function undefinedTarget(target) {
|
209
|
+
return target == undefined || target == '';
|
210
|
+
}
|
211
|
+
function topTarget(target) {
|
212
|
+
return undefinedTarget(target) || target == '_top';
|
213
|
+
}
|
214
|
+
function nextLocation(element) {
|
215
|
+
var target = element.target;
|
216
|
+
if (undefinedTarget(target) && baseTarget()) {
|
217
|
+
top[baseTarget()].location = element.href;
|
218
|
+
} else if (topTarget(target)) {
|
219
|
+
top.location = element.href;
|
220
|
+
} else {
|
221
|
+
top[target].location = element.href;
|
222
|
+
}
|
223
|
+
}
|
224
|
+
var click = DOCUMENT.createEvent('HTMLEvents');
|
119
225
|
click.initEvent('click', true, true);
|
120
226
|
if (element.onclick) {
|
121
227
|
if (false != element.onclick(click)) {
|
122
|
-
|
228
|
+
nextLocation(element);
|
123
229
|
}
|
124
230
|
} else {
|
125
|
-
|
126
|
-
}
|
127
|
-
execute_and_wait(
|
128
|
-
set URL in document 1 to target|)
|
231
|
+
nextLocation(element);
|
232
|
+
}/
|
233
|
+
execute_and_wait(js.operate(find_link(element), click))
|
129
234
|
end
|
130
235
|
|
131
236
|
def operate_on_link(element)
|
132
|
-
|
237
|
+
js.operate(find_link(element), yield)
|
133
238
|
end
|
134
239
|
|
135
240
|
def find_link(element)
|
241
|
+
case element.how
|
242
|
+
when :index:
|
243
|
+
%|var element = document.getElementsByTagName('A')[#{element.what-1}];|
|
244
|
+
else
|
136
245
|
%|var element = undefined;
|
137
246
|
for (var i = 0; i < document.links.length; i++) {
|
138
247
|
if (document.links[i].#{handle_match(element)}) {
|
@@ -140,6 +249,7 @@ for (var i = 0; i < document.links.length; i++) {
|
|
140
249
|
break;
|
141
250
|
}
|
142
251
|
}|
|
252
|
+
end
|
143
253
|
end
|
144
254
|
private :find_link
|
145
255
|
|
@@ -147,15 +257,17 @@ for (var i = 0; i < document.links.length; i++) {
|
|
147
257
|
how = {:text => "text", :url => "href"}[element.how]
|
148
258
|
case element.what
|
149
259
|
when Regexp:
|
150
|
-
%|#{how}.match(/#{element.what.source}
|
260
|
+
%|#{how}.match(/#{element.what.source}/#{element.what.casefold? ? "i":nil})|
|
151
261
|
when String:
|
152
262
|
%|#{how} == '#{element.what}'|
|
263
|
+
else
|
264
|
+
raise RuntimeError, "Unable to locate #{element.name} with #{element.how}"
|
153
265
|
end
|
154
266
|
end
|
155
267
|
private :handle_match
|
156
268
|
|
157
269
|
def operate_by_input_value(element)
|
158
|
-
|
270
|
+
js.operate(%|
|
159
271
|
var elements = document.getElementsByTagName('INPUT');
|
160
272
|
var element = undefined;
|
161
273
|
for (var i = 0; i < elements.length; i++) {
|
@@ -169,7 +281,7 @@ for (var i = 0; i < elements.length; i++) {
|
|
169
281
|
end
|
170
282
|
|
171
283
|
def operate_by_name(element)
|
172
|
-
|
284
|
+
js.operate(%|
|
173
285
|
var elements = document.getElementsByName('#{element.what}');
|
174
286
|
var element = undefined;
|
175
287
|
for (var i = 0; i < elements.length; i++) {
|
@@ -192,15 +304,15 @@ for (var i = 0; i < elements.length; i++) {
|
|
192
304
|
private :handle_form_element_name_match
|
193
305
|
|
194
306
|
def operate_by_id(element)
|
195
|
-
|
307
|
+
js.operate("var element = document.getElementById('#{element.what}');", yield)
|
196
308
|
end
|
197
309
|
|
198
310
|
def operate_by_index(element)
|
199
|
-
|
311
|
+
js.operate(%|var element = document.getElementsByTagName('#{element.tag}')[#{element.what-1}];|, yield)
|
200
312
|
end
|
201
313
|
|
202
314
|
def operate_on_label(element)
|
203
|
-
|
315
|
+
js.operate(%|var elements = document.getElementsByTagName('LABEL');
|
204
316
|
var element = undefined;
|
205
317
|
for (var i = 0; i < elements.length; i++) {
|
206
318
|
if (elements[i].innerText == '#{element.what}') {
|
@@ -223,10 +335,51 @@ tell window 1
|
|
223
335
|
end tell|)
|
224
336
|
end
|
225
337
|
|
338
|
+
def for_table(element)
|
339
|
+
AppleScripter.new(TableJavaScripter.new(element))
|
340
|
+
end
|
341
|
+
|
342
|
+
def for_frame(element)
|
343
|
+
# verify the frame exists
|
344
|
+
execute(js.wrap(%|
|
345
|
+
if (parent.#{element.name} == undefined) {
|
346
|
+
return '#{FRAME_NOT_FOUND}';
|
347
|
+
}|), element)
|
348
|
+
AppleScripter.new(FrameJavaScripter.new(element))
|
349
|
+
end
|
350
|
+
|
351
|
+
def speak_value_of(element = @element)
|
352
|
+
speak(get_value_for(element))
|
353
|
+
end
|
354
|
+
|
355
|
+
def speak_text_of(element = @element)
|
356
|
+
speak(element.text)
|
357
|
+
end
|
358
|
+
|
359
|
+
def speak_options_for(element = @element)
|
360
|
+
values = execute(element.operate do
|
361
|
+
%|var values = '';
|
362
|
+
for (var i = 0; i < element.options.length; i++) {
|
363
|
+
if (element.options[i].selected == true) {
|
364
|
+
values += ' ' + element.options[i].text;
|
365
|
+
}
|
366
|
+
}
|
367
|
+
return values|
|
368
|
+
end, element)
|
369
|
+
speak(values)
|
370
|
+
end
|
371
|
+
|
372
|
+
def speak(string)
|
373
|
+
`osascript <<SCRIPT
|
374
|
+
say "#{string.quote_safe}"
|
375
|
+
SCRIPT`
|
376
|
+
nil
|
377
|
+
end
|
378
|
+
|
379
|
+
|
226
380
|
private
|
227
381
|
|
228
382
|
def execute!(script)
|
229
|
-
# puts script
|
230
383
|
`osascript <<SCRIPT
|
231
384
|
tell application "Safari"
|
232
385
|
set response to "#{NO_RESPONSE}"
|
@@ -239,10 +392,14 @@ SCRIPT`.chomp
|
|
239
392
|
def execute(script, element = nil)
|
240
393
|
response = execute! script
|
241
394
|
case response
|
242
|
-
when NOT_FOUND:
|
243
|
-
raise UnknownObjectException, "Unable to locate #{element.name} element with #{element.how} of #{element.what}."
|
244
395
|
when NO_RESPONSE:
|
245
396
|
nil
|
397
|
+
when ELEMENT_NOT_FOUND:
|
398
|
+
raise UnknownObjectException, "Unable to locate #{element.name} element with #{element.how} of #{element.what}"
|
399
|
+
when TABLE_CELL_NOT_FOUND:
|
400
|
+
raise UnknownCellException, "Unable to locate a table cell with #{element.how} of #{element.what}"
|
401
|
+
when FRAME_NOT_FOUND:
|
402
|
+
raise UnknownFrameException, "Unable to locate a frame with name #{element.name}"
|
246
403
|
else
|
247
404
|
response
|
248
405
|
end
|
@@ -263,33 +420,18 @@ SCRIPT`
|
|
263
420
|
nil
|
264
421
|
end
|
265
422
|
|
266
|
-
def execute_and_wait(script, element = nil)
|
423
|
+
def execute_and_wait(script, element = nil)
|
267
424
|
execute(%|
|
268
425
|
#{script}
|
269
426
|
delay 2
|
270
427
|
repeat with i from 1 to #{TIMEOUT}
|
271
|
-
|
428
|
+
#{js.wrap("document.readyState")}
|
429
|
+
if (response) is "complete" then
|
272
430
|
exit repeat
|
273
431
|
else
|
274
432
|
delay 1
|
275
433
|
end if
|
276
434
|
end repeat|, element)
|
277
435
|
end
|
278
|
-
|
279
|
-
def ensure_window_ready
|
280
|
-
execute(%|
|
281
|
-
activate
|
282
|
-
set document_list to every document
|
283
|
-
if length of document_list is 0 then
|
284
|
-
make new document
|
285
|
-
end if|)
|
286
|
-
end
|
287
|
-
|
288
|
-
def speak(string)
|
289
|
-
`osascript <<SCRIPT
|
290
|
-
say "#{string.quote_safe}"
|
291
|
-
SCRIPT`
|
292
|
-
nil
|
293
|
-
end
|
294
436
|
end # class AppleScripter
|
295
437
|
end
|
data/safariwatir_script.rb
CHANGED
@@ -3,12 +3,12 @@ require 'safariwatir'
|
|
3
3
|
|
4
4
|
# TODO
|
5
5
|
# Need to give feedback when browser dies
|
6
|
-
# Be more attached to the Safari window, if a different window is selected, the AppleScript executes against it
|
6
|
+
# Be more attached to the Safari window. Currently, if a different window is selected, the AppleScript executes against it.
|
7
7
|
# Verify onclick is working for buttons and links
|
8
8
|
# TextFields should not respond to button method, etc.
|
9
9
|
|
10
|
-
# Unsupported Elements:
|
11
|
-
# Use dynamic properties for Javascript optimization?
|
10
|
+
# Unsupported Elements: Test that P/Div/Span/TD handle link, button, etc., Javascript confirm [OK/CANCEL], Javascript prompt, Javascript popup windows
|
11
|
+
# Use dynamic properties for Javascript optimization? Or use global lookup table?
|
12
12
|
# Will I need to push more functionality into AppleScript to speed things up?
|
13
13
|
# Angrez is looking into the Ruby/AppleScript binding
|
14
14
|
# Watir Rails Plugin needed -> Watir test generator, fixtures and AR in-test, Browser Factory
|
@@ -24,10 +24,12 @@ def safari.google_to_prag
|
|
24
24
|
goto("http://google.com")
|
25
25
|
text_field(:name, "q").set("pickaxe")
|
26
26
|
button(:name, "btnG").click
|
27
|
-
link(:text, "
|
27
|
+
link(:text, "Programming Ruby, 2nd Ed.").click
|
28
28
|
link(:url, "http://www.pragmaticprogrammer.com/titles/ruby/code/index.html").click
|
29
29
|
link(:text, "Catalog").click
|
30
|
-
|
30
|
+
# site was down
|
31
|
+
# link(:text, "All Books").click
|
32
|
+
goto("http://pragmaticprogrammer.com/bookshelf/") # workaround
|
31
33
|
link(:text, /Agile Retrospectives/).click
|
32
34
|
puts "FAILURE prag" unless contains_text("Dave Hoover")
|
33
35
|
end
|
@@ -46,7 +48,7 @@ def safari.amazon
|
|
46
48
|
select_list(:name, "url").select_value("index=software")
|
47
49
|
text_field(:name, "keywords").set("Orion")
|
48
50
|
image(:name, "Go").click
|
49
|
-
puts "FAILURE amazon" unless contains_text("Master of Orion
|
51
|
+
puts "FAILURE amazon" unless contains_text("Master of Orion")
|
50
52
|
end
|
51
53
|
|
52
54
|
def safari.google_advanced
|
@@ -65,7 +67,7 @@ def safari.reddit
|
|
65
67
|
goto("http://reddit.com/")
|
66
68
|
text_field(:name, "user").set("foo")
|
67
69
|
password(:name, "passwd").set("bar")
|
68
|
-
form(:index,
|
70
|
+
form(:index, 2).submit
|
69
71
|
puts "FAILURE reddit" unless contains_text("foo") and contains_text("logout")
|
70
72
|
end
|
71
73
|
|
@@ -92,6 +94,34 @@ def safari.redsquirrel
|
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
97
|
+
def safari.weinberg
|
98
|
+
goto("http://www.geraldmweinberg.com/")
|
99
|
+
puts "FAILURE weinberg menu" unless frame("menu").contains_text("Jerry Weinberg's Site")
|
100
|
+
frame("menu").link(:text, "Books").click
|
101
|
+
frame("menu").link(:text, /psychology/i).click
|
102
|
+
puts "FAILURE weinberg content" unless frame("content").contains_text("Silver Anniversary")
|
103
|
+
end
|
104
|
+
|
105
|
+
def safari.tables
|
106
|
+
goto("http://basecamphq.com/")
|
107
|
+
puts "FAILURE basecamp content" unless table(:index, 1)[1][2].text =~ /What is Basecamp\?/
|
108
|
+
|
109
|
+
goto("http://www.jimthatcher.com/webcourse9.htm")
|
110
|
+
puts "FAILURE thatcher" unless cell(:id, "c5").text == "subtotals"
|
111
|
+
|
112
|
+
goto("http://amazon.com/")
|
113
|
+
if contains_text("If you're not")
|
114
|
+
link(:text, "click here").click
|
115
|
+
end
|
116
|
+
|
117
|
+
puts "FAILURE amazon tr" unless row(:id, "twotabtop")[2].text =~ /Your\s+Amazon\.com/
|
118
|
+
row(:id, "twotabtop")[2].link(:index, 1).click
|
119
|
+
puts "FAILURE amazon link" unless contains_text("personalized recommendations")
|
120
|
+
|
121
|
+
goto("http://www.dreamweaverresources.com/tutorials/tableborder.htm")
|
122
|
+
puts "FAILURE dreamweaver" unless table(:id, "titletable")[1][1].text =~ /CSS/
|
123
|
+
end
|
124
|
+
|
95
125
|
safari.google_to_prag
|
96
126
|
safari.ala
|
97
127
|
safari.amazon
|
@@ -99,5 +129,7 @@ safari.google_advanced
|
|
99
129
|
safari.reddit
|
100
130
|
safari.colbert
|
101
131
|
safari.redsquirrel
|
132
|
+
safari.weinberg
|
133
|
+
safari.tables
|
102
134
|
|
103
135
|
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-
|
6
|
+
version: 0.1.2
|
7
|
+
date: 2006-10-12 00:00:00 -05:00
|
8
8
|
summary: Automated testing tool for web applications.
|
9
9
|
require_paths:
|
10
10
|
- .
|