redsquirrel-safariwatir 0.3.5
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/README.rdoc +46 -0
- data/Rakefile +13 -0
- data/lib/safariwatir/core_ext.rb +31 -0
- data/lib/safariwatir/scripter.rb +585 -0
- data/lib/safariwatir.rb +547 -0
- data/lib/watir/exceptions.rb +42 -0
- data/safariwatir.gemspec +34 -0
- data/safariwatir_example.rb +151 -0
- metadata +79 -0
data/lib/safariwatir.rb
ADDED
@@ -0,0 +1,547 @@
|
|
1
|
+
require 'watir/exceptions'
|
2
|
+
require 'safariwatir/scripter'
|
3
|
+
require 'safariwatir/core_ext'
|
4
|
+
|
5
|
+
module Watir
|
6
|
+
include Watir::Exception
|
7
|
+
|
8
|
+
module PageContainer
|
9
|
+
def html
|
10
|
+
@scripter.document_html
|
11
|
+
end
|
12
|
+
|
13
|
+
def text
|
14
|
+
@scripter.document_text
|
15
|
+
end
|
16
|
+
|
17
|
+
def title
|
18
|
+
@scripter.document_title
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module Container
|
23
|
+
attr_reader :scripter
|
24
|
+
private :scripter
|
25
|
+
|
26
|
+
DEFAULT_TYPING_LAG = 0.08
|
27
|
+
|
28
|
+
def set_fast_speed
|
29
|
+
@scripter.typing_lag = 0
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_slow_speed
|
33
|
+
@scripter.typing_lag = DEFAULT_TYPING_LAG
|
34
|
+
end
|
35
|
+
|
36
|
+
def speed=(how_fast)
|
37
|
+
case how_fast
|
38
|
+
when :fast : set_fast_speed
|
39
|
+
when :slow : set_slow_speed
|
40
|
+
else
|
41
|
+
raise ArgumentError, "Invalid speed: #{how_fast}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module Clickable
|
46
|
+
def click
|
47
|
+
@scripter.highlight(self) do
|
48
|
+
click_element
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class AlertWindow
|
54
|
+
def_init :scripter
|
55
|
+
|
56
|
+
def click
|
57
|
+
@scripter.click_alert
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class SecurityWarningWindow
|
62
|
+
def initialize(scripter, url)
|
63
|
+
@scripter = scripter
|
64
|
+
@url = url
|
65
|
+
end
|
66
|
+
|
67
|
+
def continue
|
68
|
+
handle_click("Continue")
|
69
|
+
end
|
70
|
+
|
71
|
+
def cancel
|
72
|
+
handle_click("Cancel")
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
def handle_click(button)
|
77
|
+
if @url
|
78
|
+
@scripter.navigate_to(@url) do
|
79
|
+
@scripter.click_security_warning(button)
|
80
|
+
end
|
81
|
+
else
|
82
|
+
@scripter.click_security_warning(button)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Frame
|
88
|
+
include Container
|
89
|
+
include PageContainer
|
90
|
+
|
91
|
+
attr_reader :name
|
92
|
+
|
93
|
+
def initialize(scripter, name)
|
94
|
+
@name = name
|
95
|
+
@scripter = scripter.for_frame(self)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class HtmlElement
|
100
|
+
def_init :scripter, :how, :what
|
101
|
+
attr_reader :how, :what
|
102
|
+
|
103
|
+
# overridden in derivitives
|
104
|
+
def tag
|
105
|
+
raise RuntimeError, "tag not provided for #{name}"
|
106
|
+
end
|
107
|
+
|
108
|
+
# overridden in derivitives
|
109
|
+
def speak
|
110
|
+
@scripter.speak("#{name}'s don't know how to speak.")
|
111
|
+
end
|
112
|
+
|
113
|
+
def exists?
|
114
|
+
@scripter.element_exists?(self)
|
115
|
+
end
|
116
|
+
alias :exist? :exists?
|
117
|
+
|
118
|
+
def name
|
119
|
+
self.class.name.split("::").last
|
120
|
+
end
|
121
|
+
|
122
|
+
def operate(&block)
|
123
|
+
scripter_suffix = OPERATIONS[how]
|
124
|
+
if scripter_suffix.kind_of? Hash
|
125
|
+
scripter_suffix = scripter_suffix[name]
|
126
|
+
end
|
127
|
+
if scripter_suffix.nil?
|
128
|
+
raise "SafariWatir does not currently support finding by #{how}"
|
129
|
+
end
|
130
|
+
@scripter.send("operate_#{scripter_suffix}", self, &block)
|
131
|
+
end
|
132
|
+
|
133
|
+
OPERATIONS = {
|
134
|
+
:id => "by_id",
|
135
|
+
:index => "by_index",
|
136
|
+
:class => "by_class",
|
137
|
+
:name => "by_name",
|
138
|
+
:text => { "Link" => "on_link", "Label" => "by_text" },
|
139
|
+
:url => "on_link",
|
140
|
+
:value => "by_input_value",
|
141
|
+
:caption => "by_input_value",
|
142
|
+
:src => "by_src",
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
class Form < HtmlElement
|
147
|
+
def_init :scripter, :how, :what
|
148
|
+
|
149
|
+
def submit
|
150
|
+
@scripter.submit_form(self)
|
151
|
+
end
|
152
|
+
|
153
|
+
def tag; "FORM"; end
|
154
|
+
end
|
155
|
+
|
156
|
+
class InputElement < HtmlElement
|
157
|
+
include Clickable
|
158
|
+
|
159
|
+
def speak
|
160
|
+
@scripter.speak_value_of(self)
|
161
|
+
end
|
162
|
+
|
163
|
+
def tag; "INPUT"; end
|
164
|
+
|
165
|
+
# Hook for derivitives
|
166
|
+
def by_value; end
|
167
|
+
end
|
168
|
+
|
169
|
+
class ContentElement < HtmlElement
|
170
|
+
include Clickable
|
171
|
+
include Container
|
172
|
+
|
173
|
+
def html
|
174
|
+
@scripter.get_html_for(self)
|
175
|
+
end
|
176
|
+
|
177
|
+
def text
|
178
|
+
@scripter.get_text_for(self)
|
179
|
+
end
|
180
|
+
|
181
|
+
def speak
|
182
|
+
@scripter.speak_text_of(self)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
class Image < HtmlElement
|
187
|
+
include Clickable
|
188
|
+
|
189
|
+
def tag; "IMG"; end
|
190
|
+
end
|
191
|
+
|
192
|
+
class Button < InputElement
|
193
|
+
end
|
194
|
+
|
195
|
+
class Checkbox < InputElement
|
196
|
+
def_init :scripter, :how, :what, :value
|
197
|
+
|
198
|
+
def by_value
|
199
|
+
@value
|
200
|
+
end
|
201
|
+
|
202
|
+
# Contributed by Kyle Campos
|
203
|
+
def checked?
|
204
|
+
@scripter.checkbox_is_checked?(self)
|
205
|
+
end
|
206
|
+
|
207
|
+
def set(check_it = true)
|
208
|
+
return if check_it && checked?
|
209
|
+
return if !check_it && !checked?
|
210
|
+
click
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
class Div < ContentElement
|
215
|
+
def tag; "DIV"; end
|
216
|
+
end
|
217
|
+
|
218
|
+
class Label < ContentElement
|
219
|
+
def tag; "LABEL"; end
|
220
|
+
end
|
221
|
+
|
222
|
+
class Link < ContentElement
|
223
|
+
def click
|
224
|
+
@scripter.highlight(self) do
|
225
|
+
click_link
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def tag; "A"; end
|
230
|
+
end
|
231
|
+
|
232
|
+
class Radio < Checkbox
|
233
|
+
end
|
234
|
+
|
235
|
+
class SelectList < InputElement
|
236
|
+
def select(label)
|
237
|
+
option(:text, label).select
|
238
|
+
end
|
239
|
+
|
240
|
+
def select_value(value)
|
241
|
+
option(:value, value).select
|
242
|
+
end
|
243
|
+
|
244
|
+
def option(how, what)
|
245
|
+
Option.new(@scripter, self, how, what)
|
246
|
+
end
|
247
|
+
|
248
|
+
def selected_values
|
249
|
+
values = []
|
250
|
+
index = 1
|
251
|
+
loop do
|
252
|
+
option = option(:index, index)
|
253
|
+
break unless option.exists?
|
254
|
+
values << option if option.selected?
|
255
|
+
index += 1
|
256
|
+
end
|
257
|
+
values.map {|o| o.text } #TODO?
|
258
|
+
end
|
259
|
+
|
260
|
+
def selected_value
|
261
|
+
selected_values.first
|
262
|
+
end
|
263
|
+
|
264
|
+
def speak
|
265
|
+
@scripter.speak_options_for(self)
|
266
|
+
end
|
267
|
+
|
268
|
+
def tag; "SELECT"; end
|
269
|
+
end
|
270
|
+
|
271
|
+
class Option < InputElement
|
272
|
+
def_init :scripter, :select_list, :how, :what
|
273
|
+
|
274
|
+
def select
|
275
|
+
@scripter.highlight(self) do
|
276
|
+
select_option
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def operate(&block)
|
281
|
+
@select_list.operate(&block)
|
282
|
+
end
|
283
|
+
|
284
|
+
def exists?
|
285
|
+
@scripter.option_exists?(self)
|
286
|
+
end
|
287
|
+
alias :exist? :exists?
|
288
|
+
|
289
|
+
def selected?
|
290
|
+
@scripter.option_selected?(self)
|
291
|
+
end
|
292
|
+
|
293
|
+
def text
|
294
|
+
@scripter.get_text_for(self)
|
295
|
+
end
|
296
|
+
|
297
|
+
def tag; "OPTION"; end
|
298
|
+
end
|
299
|
+
|
300
|
+
class Span < ContentElement
|
301
|
+
def tag; "SPAN"; end
|
302
|
+
end
|
303
|
+
|
304
|
+
class Table
|
305
|
+
def_init :scripter, :how, :what
|
306
|
+
attr_reader :how, :what
|
307
|
+
|
308
|
+
def each
|
309
|
+
# TODO
|
310
|
+
end
|
311
|
+
|
312
|
+
def [](index)
|
313
|
+
TableRow.new(@scripter, :index, index, self)
|
314
|
+
end
|
315
|
+
|
316
|
+
def row_count
|
317
|
+
# TODO
|
318
|
+
end
|
319
|
+
|
320
|
+
def column_count
|
321
|
+
# TODO
|
322
|
+
end
|
323
|
+
|
324
|
+
def tag; "TABLE"; end
|
325
|
+
end
|
326
|
+
|
327
|
+
class TableRow
|
328
|
+
def initialize(scripter, how, what, table = nil)
|
329
|
+
@scripter = scripter
|
330
|
+
@how = how
|
331
|
+
@what = what
|
332
|
+
@table = table
|
333
|
+
end
|
334
|
+
|
335
|
+
attr_reader :table, :how, :what
|
336
|
+
|
337
|
+
def each
|
338
|
+
# TODO
|
339
|
+
end
|
340
|
+
|
341
|
+
def [](index)
|
342
|
+
TableCell.new(@scripter, :index, index, self)
|
343
|
+
end
|
344
|
+
|
345
|
+
def column_count
|
346
|
+
# TODO
|
347
|
+
end
|
348
|
+
|
349
|
+
def tag; "TR"; end
|
350
|
+
end
|
351
|
+
|
352
|
+
class TableCell < ContentElement
|
353
|
+
def initialize(scripter, how, what, row = nil)
|
354
|
+
@scripter = scripter.for_table(self)
|
355
|
+
set_slow_speed # TODO: Need to inherit this somehow
|
356
|
+
|
357
|
+
@how = how
|
358
|
+
@what = what
|
359
|
+
@row = row
|
360
|
+
end
|
361
|
+
|
362
|
+
attr_reader :how, :what, :row
|
363
|
+
|
364
|
+
def operate(&block)
|
365
|
+
@scripter.operate_by_table_cell(self, &block)
|
366
|
+
end
|
367
|
+
|
368
|
+
def tag; "TD"; end
|
369
|
+
end
|
370
|
+
|
371
|
+
class TextField < InputElement
|
372
|
+
def set(value)
|
373
|
+
value = value.to_s
|
374
|
+
@scripter.focus(self)
|
375
|
+
@scripter.highlight(self) do
|
376
|
+
clear_text_input
|
377
|
+
value.length.times do |i|
|
378
|
+
append_text_input(value[i, 1])
|
379
|
+
end
|
380
|
+
end
|
381
|
+
@scripter.blur(self)
|
382
|
+
end
|
383
|
+
|
384
|
+
def getContents
|
385
|
+
@scripter.get_value_for(self)
|
386
|
+
end
|
387
|
+
|
388
|
+
def verify_contains(expected)
|
389
|
+
actual = getContents
|
390
|
+
case expected
|
391
|
+
when Regexp
|
392
|
+
actual.match(expected) != nil
|
393
|
+
else
|
394
|
+
expected == actual
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
class Password < TextField
|
400
|
+
end
|
401
|
+
|
402
|
+
|
403
|
+
# Elements
|
404
|
+
|
405
|
+
def button(how, what)
|
406
|
+
Button.new(scripter, how, what)
|
407
|
+
end
|
408
|
+
|
409
|
+
def cell(how, what)
|
410
|
+
TableCell.new(scripter, how, what)
|
411
|
+
end
|
412
|
+
|
413
|
+
def checkbox(how, what, value = nil)
|
414
|
+
Checkbox.new(scripter, how, what, value)
|
415
|
+
end
|
416
|
+
|
417
|
+
def div(how, what)
|
418
|
+
Div.new(scripter, how, what)
|
419
|
+
end
|
420
|
+
|
421
|
+
def form(how, what)
|
422
|
+
Form.new(scripter, how, what)
|
423
|
+
end
|
424
|
+
|
425
|
+
def frame(name)
|
426
|
+
Frame.new(scripter, name)
|
427
|
+
end
|
428
|
+
|
429
|
+
def image(how, what)
|
430
|
+
Image.new(scripter, how, what)
|
431
|
+
end
|
432
|
+
|
433
|
+
def label(how, what)
|
434
|
+
Label.new(scripter, how, what)
|
435
|
+
end
|
436
|
+
|
437
|
+
def link(how, what)
|
438
|
+
Link.new(scripter, how, what)
|
439
|
+
end
|
440
|
+
|
441
|
+
def password(how, what)
|
442
|
+
Password.new(scripter, how, what)
|
443
|
+
end
|
444
|
+
|
445
|
+
def radio(how, what, value = nil)
|
446
|
+
Radio.new(scripter, how, what, value)
|
447
|
+
end
|
448
|
+
|
449
|
+
def row(how, what)
|
450
|
+
TableRow.new(scripter, how, what)
|
451
|
+
end
|
452
|
+
|
453
|
+
def select_list(how, what)
|
454
|
+
SelectList.new(scripter, how, what)
|
455
|
+
end
|
456
|
+
|
457
|
+
def span(how, what)
|
458
|
+
Span.new(scripter, how, what)
|
459
|
+
end
|
460
|
+
|
461
|
+
def table(how, what)
|
462
|
+
Table.new(scripter, how, what)
|
463
|
+
end
|
464
|
+
|
465
|
+
def text_field(how, what)
|
466
|
+
TextField.new(scripter, how, what)
|
467
|
+
end
|
468
|
+
|
469
|
+
def contains_text(what)
|
470
|
+
case what
|
471
|
+
when Regexp:
|
472
|
+
text =~ what
|
473
|
+
when String:
|
474
|
+
text.index(what)
|
475
|
+
else
|
476
|
+
raise MissingWayOfFindingObjectException
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
class Safari
|
482
|
+
include Container
|
483
|
+
include PageContainer
|
484
|
+
|
485
|
+
def self.start(url = nil)
|
486
|
+
safari = new
|
487
|
+
safari.goto(url) if url
|
488
|
+
safari
|
489
|
+
end
|
490
|
+
|
491
|
+
def initialize
|
492
|
+
@scripter = AppleScripter.new(JavaScripter.new)
|
493
|
+
@scripter.ensure_window_ready
|
494
|
+
set_slow_speed
|
495
|
+
end
|
496
|
+
|
497
|
+
# URL of page
|
498
|
+
def url
|
499
|
+
scripter.url
|
500
|
+
end
|
501
|
+
|
502
|
+
# Hide's Safari
|
503
|
+
def hide
|
504
|
+
scripter.hide
|
505
|
+
end
|
506
|
+
|
507
|
+
def close
|
508
|
+
scripter.close
|
509
|
+
end
|
510
|
+
|
511
|
+
def quit
|
512
|
+
scripter.quit
|
513
|
+
end
|
514
|
+
|
515
|
+
def alert
|
516
|
+
AlertWindow.new(scripter)
|
517
|
+
end
|
518
|
+
|
519
|
+
def security_warning
|
520
|
+
SecurityWarningWindow.new(scripter)
|
521
|
+
end
|
522
|
+
|
523
|
+
def security_warning_at(url)
|
524
|
+
SecurityWarningWindow.new(scripter, url)
|
525
|
+
end
|
526
|
+
|
527
|
+
def goto(url)
|
528
|
+
scripter.navigate_to(url)
|
529
|
+
end
|
530
|
+
|
531
|
+
# Reloads the page
|
532
|
+
def reload
|
533
|
+
scripter.reload
|
534
|
+
end
|
535
|
+
alias :refresh :reload
|
536
|
+
end # class Safari
|
537
|
+
|
538
|
+
|
539
|
+
class WebKit < Safari
|
540
|
+
def initialize
|
541
|
+
@scripter = AppleScripter.new(JavaScripter.new, :appname => "WebKit")
|
542
|
+
@scripter.ensure_window_ready
|
543
|
+
set_slow_speed
|
544
|
+
end
|
545
|
+
end # class WebKit
|
546
|
+
|
547
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Watir
|
2
|
+
module Exception
|
3
|
+
|
4
|
+
# Root class for all Watir Exceptions
|
5
|
+
class WatirException < RuntimeError
|
6
|
+
def initialize(message="")
|
7
|
+
super(message)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# This exception is thrown if an attempt is made to access an object that doesn't exist
|
12
|
+
class UnknownObjectException < WatirException; end
|
13
|
+
# This exception is thrown if an attempt is made to access an object that is in a disabled state
|
14
|
+
class ObjectDisabledException < WatirException; end
|
15
|
+
# This exception is thrown if an attempt is made to access a frame that cannot be found
|
16
|
+
class UnknownFrameException< WatirException; end
|
17
|
+
# This exception is thrown if an attempt is made to access a form that cannot be found
|
18
|
+
class UnknownFormException< WatirException; end
|
19
|
+
# This exception is thrown if an attempt is made to access an object that is in a read only state
|
20
|
+
class ObjectReadOnlyException < WatirException; end
|
21
|
+
# This exception is thrown if an attempt is made to access an object when the specified value cannot be found
|
22
|
+
class NoValueFoundException < WatirException; end
|
23
|
+
# This exception gets raised if part of finding an object is missing
|
24
|
+
class MissingWayOfFindingObjectException < WatirException; end
|
25
|
+
# this exception is raised if an attempt is made to access a table cell that doesnt exist
|
26
|
+
class UnknownCellException < WatirException; end
|
27
|
+
# This exception is thrown if the window cannot be found
|
28
|
+
class NoMatchingWindowFoundException < WatirException; end
|
29
|
+
# This exception is thrown if an attemp is made to acces the status bar of the browser when it doesnt exist
|
30
|
+
class NoStatusBarException < WatirException; end
|
31
|
+
# This exception is thrown if an http error, such as a 404, 500 etc is encountered while navigating
|
32
|
+
class NavigationException < WatirException; end
|
33
|
+
# This exception is raised if a timeout is exceeded
|
34
|
+
class TimeOutException < WatirException
|
35
|
+
def initialize(duration, timeout)
|
36
|
+
@duration, @timeout = duration, timeout
|
37
|
+
end
|
38
|
+
attr_reader :duration, :timeout
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/safariwatir.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{safariwatir}
|
5
|
+
s.version = "0.3.5"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Dave Hoover"]
|
9
|
+
s.date = %q{2009-02-18}
|
10
|
+
s.description = %q{WATIR stands for "Web Application Testing in Ruby". See WATIR project for more information. This is a Safari-version of the original IE-only WATIR.}
|
11
|
+
s.email = %q{dave@obtiva.com}
|
12
|
+
s.extra_rdoc_files = ["lib/safariwatir/core_ext.rb", "lib/safariwatir/scripter.rb", "lib/safariwatir.rb", "lib/watir/exceptions.rb", "README.rdoc"]
|
13
|
+
s.files = ["lib/safariwatir/core_ext.rb", "lib/safariwatir/scripter.rb", "lib/safariwatir.rb", "lib/watir/exceptions.rb", "Manifest", "Rakefile", "README.rdoc", "safariwatir.gemspec", "safariwatir_example.rb"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{http://safariwatir.rubyforge.org/}
|
16
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Safariwatir", "--main", "README.rdoc"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{safariwatir}
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
20
|
+
s.summary = %q{Automated testing tool for web applications.}
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 2
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_development_dependency(%q<rb-appscript>, [">= 0"])
|
28
|
+
else
|
29
|
+
s.add_dependency(%q<rb-appscript>, [">= 0"])
|
30
|
+
end
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<rb-appscript>, [">= 0"])
|
33
|
+
end
|
34
|
+
end
|