safariwatir 0.1.0

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