safariwatir 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,213 @@
1
+ require 'safariwatir/scripter'
2
+ require 'safariwatir/core_ext'
3
+ require 'watir/exceptions'
4
+
5
+ module Watir
6
+ include Watir::Exception
7
+
8
+ module Elements
9
+ class AlertWindow
10
+ def initialize(scripter)
11
+ @scripter = scripter
12
+ end
13
+
14
+ def click
15
+ @scripter.click_alert_ok
16
+ end
17
+ end
18
+
19
+ class HtmlElement
20
+ def_init :scripter, :how, :what
21
+ attr_reader :how, :what
22
+
23
+ # Hooks for subclasses
24
+ def tag; end
25
+
26
+ def operate(&block)
27
+ send("operate_by_" + how.to_s, &block)
28
+ end
29
+
30
+ protected
31
+
32
+ def operate_by_id(&block)
33
+ @scripter.operate_by_id(self, &block)
34
+ end
35
+ def operate_by_index(&block)
36
+ @scripter.operate_by_index(self, &block)
37
+ end
38
+ def operate_by_name(&block)
39
+ @scripter.operate_on_form_element(self, &block)
40
+ end
41
+ def operate_by_text(&block)
42
+ @scripter.operate_on_link(self, &block)
43
+ end
44
+ def operate_by_url(&block)
45
+ @scripter.operate_on_link(self, &block)
46
+ end
47
+ end
48
+
49
+ class Form < HtmlElement
50
+ def_init :scripter, :how, :what
51
+
52
+ def submit
53
+ @scripter.submit_form(self)
54
+ end
55
+
56
+ def tag; "FORM"; end
57
+ end
58
+
59
+ class ClickableElement < HtmlElement
60
+ def click
61
+ @scripter.highlight(self) do
62
+ click_element
63
+ end
64
+ end
65
+
66
+ # Hooks for subclasses
67
+ def by_value; end
68
+ end
69
+
70
+ class Button < ClickableElement
71
+ end
72
+
73
+ class Checkbox < ClickableElement
74
+ alias :set :click
75
+ end
76
+
77
+ class Label < ClickableElement
78
+ protected
79
+
80
+ def operate_by_text(&block)
81
+ @scripter.operate_on_label(self, &block)
82
+ end
83
+ end
84
+
85
+ class Link < ClickableElement
86
+ def click
87
+ @scripter.highlight(self) do
88
+ click_link
89
+ end
90
+ end
91
+ end
92
+
93
+ class Radio < ClickableElement
94
+ def_init :scripter, :how, :what, :value
95
+ def by_value
96
+ @value
97
+ end
98
+ alias :set :click
99
+ end
100
+
101
+ class SelectList < ClickableElement
102
+ def select(label)
103
+ @scripter.highlight(self) do
104
+ select_option("text", label)
105
+ end
106
+ end
107
+
108
+ def select_value(value)
109
+ @scripter.highlight(self) do
110
+ select_option("value", value)
111
+ end
112
+ end
113
+ end
114
+
115
+ class TextField < ClickableElement
116
+ def set(value)
117
+ @scripter.highlight(self) do
118
+ clear_text_input
119
+ value.length.times do |i|
120
+ append_text_input(value[i, 1])
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ class Password < TextField
127
+ end
128
+ end
129
+
130
+ class Safari
131
+ include Elements
132
+
133
+ attr_reader :scripter
134
+
135
+ def self.start(url = nil)
136
+ safari = new
137
+ safari.goto(url) if url
138
+ safari
139
+ end
140
+
141
+ def initialize
142
+ @scripter = AppleScripter.new
143
+ end
144
+
145
+ def close
146
+ scripter.close
147
+ end
148
+
149
+ def quit
150
+ scripter.quit
151
+ end
152
+
153
+ def alert
154
+ AlertWindow.new(scripter)
155
+ end
156
+
157
+ def goto(url)
158
+ scripter.navigate_to(url)
159
+ end
160
+
161
+ def button(how, what)
162
+ Button.new(scripter, how, what)
163
+ end
164
+
165
+ def checkbox(how, what)
166
+ Checkbox.new(scripter, how, what)
167
+ end
168
+
169
+ def form(how, what)
170
+ Form.new(scripter, how, what)
171
+ end
172
+
173
+ def image(how, what)
174
+ Button.new(scripter, how, what)
175
+ end
176
+
177
+ def label(how, what)
178
+ Label.new(scripter, how, what)
179
+ end
180
+
181
+ def link(how, what)
182
+ Link.new(scripter, how, what)
183
+ end
184
+
185
+ def password(how, what)
186
+ Password.new(scripter, how, what)
187
+ end
188
+
189
+ def radio(how, what, value = nil)
190
+ Radio.new(scripter, how, what, value)
191
+ end
192
+
193
+ def select_list(how, what)
194
+ SelectList.new(scripter, how, what)
195
+ end
196
+
197
+ def text_field(how, what)
198
+ TextField.new(scripter, how, what)
199
+ end
200
+
201
+ def contains_text(what)
202
+ text = scripter.document_text
203
+ case what
204
+ when Regexp:
205
+ text =~ what
206
+ when String:
207
+ text.index(what)
208
+ else
209
+ raise MissingWayOfFindingObjectException
210
+ end
211
+ end
212
+ end # class Safari
213
+ end
@@ -0,0 +1,13 @@
1
+ # Why shouldn't this be in core Ruby?
2
+ class Class
3
+ def def_init(*attrs)
4
+ constructor = %|def initialize(|
5
+ constructor << attrs.map{|a| a.to_s }.join(",")
6
+ constructor << ")\n"
7
+ attrs.each do |attribute|
8
+ constructor << "@#{attribute} = #{attribute}\n"
9
+ end
10
+ constructor << "end"
11
+ class_eval(constructor)
12
+ end
13
+ end
@@ -0,0 +1,215 @@
1
+ module Watir
2
+ class AppleScripter
3
+
4
+ @@timeout = 10
5
+
6
+ def initialize
7
+ ensure_window_ready
8
+ end
9
+
10
+ def close
11
+ execute(%|close document 1|)
12
+ end
13
+
14
+ def quit
15
+ execute(%|quit|)
16
+ end
17
+
18
+ def navigate_to(url)
19
+ execute_and_wait(%|set URL in document 1 to "#{url}"|)
20
+ end
21
+
22
+ def document_text
23
+ execute(%|do Javascript "document.getElementsByTagName('BODY').item(0).innerText;" in document 1|)
24
+ end
25
+
26
+ def highlight(element, &block)
27
+ execute(element.operate do
28
+ %|element.focus();
29
+ element.originalColor = element.style.backgroundColor;
30
+ element.style.backgroundColor = 'yellow';|
31
+ end)
32
+
33
+ @element = element
34
+ instance_eval(&block)
35
+ @element = nil
36
+
37
+ execute(element.operate { %|element.style.backgroundColor = element.originalColor;| })
38
+ end
39
+
40
+ def select_option(option_how, option_what, element = @element)
41
+ execute(element.operate do
42
+ %|for (var i = 0; i < element.options.length; i++) {
43
+ if (element.options[i].#{option_how} == '#{option_what}') {
44
+ element.options[i].selected = true;
45
+ }
46
+ }|
47
+ end)
48
+ end
49
+
50
+ def clear_text_input(element = @element)
51
+ execute(element.operate { %|element.value = '';| })
52
+ end
53
+
54
+ def append_text_input(value, element = @element)
55
+ execute(element.operate do
56
+ %|element.value += '#{value}';
57
+ element.setSelectionRange(element.value.length, element.value.length);|
58
+ end)
59
+ end
60
+
61
+ # TODO need a better approach for "waiting"
62
+ def click_element(element = @element)
63
+ execute_and_wait(element.operate { %|element.click();| })
64
+ end
65
+
66
+ def click_link(element = @element)
67
+ click = find_link(element) do
68
+ %|var click = document.createEvent('HTMLEvents');
69
+ click.initEvent('click', true, true);
70
+ if (element.onclick) {
71
+ if (false != element.onclick(click)) {
72
+ return element.href;
73
+ }
74
+ } else {
75
+ return element.href;
76
+ }|
77
+ end
78
+ execute_and_wait(%|set target to do JavaScript "#{click}" in document 1
79
+ set URL in document 1 to target|)
80
+ end
81
+
82
+ def operate_on_link(element, &block)
83
+ %|do JavaScript "#{find_link(element, &block)}" in document 1|
84
+ end
85
+
86
+ def find_link(element)
87
+ %|for (var i = 0; i < document.links.length; i++) {
88
+ if (document.links[i].#{handle_match(element)}) {
89
+ var element = document.links[i];
90
+ #{yield}
91
+ break;
92
+ }
93
+ }|
94
+ end
95
+ private :find_link
96
+
97
+ def handle_match(element)
98
+ how = {:text => "text", :url => "href"}[element.how]
99
+ case element.what
100
+ when Regexp:
101
+ %|#{how}.match(/#{element.what.source}/)|
102
+ when String:
103
+ %|#{how} == '#{element.what}'|
104
+ end
105
+ end
106
+ private :handle_match
107
+
108
+ def operate_on_form_element(element)
109
+ %|do JavaScript "
110
+ var elements = document.getElementsByName('#{element.what}');
111
+ var element;
112
+ for (var i = 0; i < elements.length; i++) {
113
+ if (elements[i].tagName != 'META') {
114
+ #{handle_form_element_name_match(element)}
115
+ }
116
+ }
117
+ #{yield}
118
+ " in document 1|
119
+ end
120
+
121
+ def handle_form_element_name_match(element)
122
+ element_capture = %|element = elements[i];break;|
123
+ if element.by_value
124
+ %|if (elements[i].value == '#{element.by_value}') {
125
+ #{element_capture}
126
+ }|
127
+ else
128
+ element_capture
129
+ end
130
+ end
131
+ private :handle_form_element_name_match
132
+
133
+ def operate_by_id(element)
134
+ %|do JavaScript "
135
+ var element = document.getElementById('#{element.what}');
136
+ #{yield}" in document 1|
137
+ end
138
+
139
+ 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|
144
+ end
145
+
146
+ def operate_on_label(element)
147
+ %|do JavaScript "
148
+ var elements = document.getElementsByTagName('LABEL');
149
+ var element;
150
+ for (var i = 0; i < elements.length; i++) {
151
+ if (elements[i].textContent == '#{element.what}') {
152
+ element = elements[i];
153
+ #{yield}
154
+ break;
155
+ }
156
+ }
157
+ " in document 1|
158
+ end
159
+
160
+ def submit_form(element)
161
+ execute_and_wait(element.operate { %|element.submit();| })
162
+ end
163
+
164
+ def click_link_ok
165
+ execute_system_events(%|
166
+ tell window 1
167
+ if button named "OK" exists then
168
+ click button named "OK"
169
+ end if
170
+ end tell|)
171
+ end
172
+
173
+ private
174
+
175
+ def execute(script)
176
+ # puts script
177
+ `osascript <<SCRIPT
178
+ tell application "Safari"
179
+ #{script}
180
+ end tell
181
+ SCRIPT`
182
+ end
183
+
184
+ # Must have "Enable access for assistive devices" checked in System Preferences > Universal Access
185
+ def execute_system_events(script)
186
+ `osascript <<SCRIPT
187
+ tell application "System Events" to tell process "Safari"
188
+ #{script}
189
+ end tell
190
+ SCRIPT`
191
+ end
192
+
193
+ def execute_and_wait(script)
194
+ execute(%|
195
+ #{script}
196
+ delay 2
197
+ repeat with i from 1 to #{@@timeout}
198
+ if (do JavaScript "document.readyState" in document 1) is "complete" then
199
+ exit repeat
200
+ else
201
+ delay 1
202
+ end if
203
+ end repeat|)
204
+ end
205
+
206
+ def ensure_window_ready
207
+ execute(%|
208
+ activate
209
+ set document_list to every document
210
+ if length of document_list is 0 then
211
+ make new document
212
+ end if|)
213
+ end
214
+ end # class AppleScripter
215
+ end
@@ -0,0 +1,76 @@
1
+ require 'safari'
2
+
3
+ # TODO
4
+ # Need to give feedback when browser dies or when elements are not found
5
+ # Be more attached to the Safari window, if a different window is selected, the AppleScript executes against it
6
+ # Verify onclick is working for buttons and links
7
+
8
+ # Unsupported Elements: Textarea, Div, UL/OL, Span
9
+ # Use dynamic properties for Javascript optimization?
10
+ # Will I need to push more functionality into AppleScript to speed things up?
11
+ # Angrez is looking into the Ruby/AppleScript binding
12
+ # Watir Rails Plugin needed
13
+
14
+ # SAFARI ISSUES
15
+ # Labels are not clickable
16
+ # No known way to programatically click a <button>
17
+ # Links with href="javascript:foo()"
18
+
19
+ safari = Watir::Safari.new
20
+
21
+ def safari.google_to_prag
22
+ goto("http://google.com")
23
+ text_field(:name, "q").set("pickaxe")
24
+ button(:name, "btnG").click
25
+ link(:text, "The Pragmatic Programmers, LLC: Programming Ruby").click
26
+ link(:url, "http://www.pragmaticprogrammer.com/titles/ruby/code/index.html").click
27
+ link(:text, "Catalog").click
28
+ link(:text, "All Books").click
29
+ link(:text, /Agile Retrospectives/).click
30
+ puts "FAILURE prag" unless contains_text("Dave Hoover")
31
+ end
32
+
33
+ def safari.ala
34
+ goto("http://alistapart.com/")
35
+ text_field(:id, "search").set("grail")
36
+ checkbox(:id, "incdisc").set
37
+ button(:id, "submit").click
38
+ puts "FAILURE ala" unless contains_text('Search Results for “grail”')
39
+ end
40
+
41
+ def safari.amazon
42
+ goto("http://amazon.com")
43
+ select_list(:name, "url").select("Toys")
44
+ select_list(:name, "url").select_value("index=software")
45
+ text_field(:name, "keywords").set("Orion")
46
+ image(:name, "Go").click
47
+ puts "FAILURE amazon" unless contains_text("Master of Orion (Original Release) (PC)")
48
+ end
49
+
50
+ def safari.google_advanced
51
+ goto("http://www.google.com/advanced_search")
52
+ radio(:name, "safe", "active").set
53
+ radio(:name, "safe", "images").set
54
+ # Safari doesn't support label clicking ... perhaps I should raise an Exception
55
+ label(:text, "No filtering").click
56
+ radio(:id, "ss").set
57
+ text_field(:name, "as_q").set("obtiva")
58
+ button(:name, "btnG").click
59
+ puts "FAILURE google" unless contains_text("RailsConf Facebook")
60
+ end
61
+
62
+ def safari.reddit
63
+ goto("http://reddit.com/")
64
+ text_field(:name, "user").set("foo")
65
+ password(:name, "passwd").set("bar")
66
+ form(:index, 1).submit
67
+ puts "FAILURE reddit" unless contains_text("foo") and contains_text("logout")
68
+ end
69
+
70
+ safari.google_to_prag
71
+ safari.ala
72
+ safari.amazon
73
+ safari.google_advanced
74
+ safari.reddit
75
+
76
+ safari.close
@@ -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
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: safariwatir
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2006-07-21 00:00:00 -05:00
8
+ summary: Automated testing tool for web applications.
9
+ require_paths:
10
+ - .
11
+ email: dave.hoover@gmail.com
12
+ homepage: http://safariwatir.rubyforge.org/
13
+ rubyforge_project: safariwatir
14
+ description: WATIR stands for "Web Application Testing in Ruby". See WATIR project for more information. This is simply a Safari-version of the original IE-only WATIR.
15
+ autorequire: safariwatir
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: false
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - Dave Hoover
30
+ files:
31
+ - safariwatir.rb
32
+ - safariwatir_script.rb
33
+ - safariwatir/core_ext.rb
34
+ - safariwatir/scripter.rb
35
+ - watir/exceptions.rb
36
+ test_files: []
37
+
38
+ rdoc_options: []
39
+
40
+ extra_rdoc_files: []
41
+
42
+ executables: []
43
+
44
+ extensions: []
45
+
46
+ requirements:
47
+ - Mac OS X running Safari
48
+ - Some features require you to turn on "Enable access for assistive devices" in System Preferences > Universal Access
49
+ dependencies: []
50
+