rwebspec-mechanize 0.2.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.
@@ -0,0 +1,51 @@
1
+ module Spec
2
+ module Extensions
3
+ module Main
4
+
5
+ alias :spec :describe
6
+ alias :specification :describe
7
+ alias :test_suite :describe
8
+ alias :suite :describe
9
+
10
+ end
11
+ end
12
+ end
13
+
14
+ # For RSpec 1.1.12
15
+ module Spec
16
+ module DSL
17
+ module Main
18
+
19
+ alias :spec :describe
20
+ alias :specification :describe
21
+ alias :test_suite :describe
22
+ alias :suite :describe
23
+
24
+ end
25
+ end
26
+ end
27
+
28
+ # ZZ patches to RSpec 1.1.4
29
+ # - add to_s method to example_group
30
+ module Spec
31
+ module Example
32
+ class ExampleGroup
33
+ def to_s
34
+ @_defined_description
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ module Spec
41
+ module Example
42
+ module ExampleGroupMethods
43
+
44
+ alias_method :scenario, :it
45
+ alias_method :story, :it
46
+ alias_method :test_case, :it
47
+ alias_method :use_case, :it
48
+ alias_method :test, :it
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,85 @@
1
+ gem 'watir'
2
+ require 'fileutils'
3
+ require 'watir/container'
4
+ require 'watir/element_collections'
5
+ require 'watir/element'
6
+
7
+ module Watir
8
+ # Base class for html elements.
9
+ # This is not a class that users would normally access.
10
+ class Element
11
+
12
+ def method_missing(method_name, *args, &block)
13
+
14
+ if ($TESTWISE_DIR || $TESTWISE_BROWSER) && method_name.to_s =~ /(.*)_no_wait/ && self.respond_to?($1)
15
+ ruby_code = testwise_generate_ruby_code(self, $1, *args)
16
+ testwise_click_no_wait(ruby_code)
17
+
18
+ elsif method_name.to_s =~ /(.*)_no_wait/ && self.respond_to?($1)
19
+ puts "[Watir] handle it"
20
+ assert_exists
21
+ assert_enabled
22
+ highlight(:set)
23
+ ruby_code = generate_ruby_code(self, $1, *args)
24
+ system(spawned_no_wait_command(ruby_code))
25
+ highlight(:clear)
26
+ else
27
+ super
28
+ end
29
+
30
+ end
31
+
32
+
33
+ def testwise_generate_ruby_code(element, method_name, *args)
34
+ element = "#{self.class}.new(#{@page_container.attach_command}, :unique_number, #{self.unique_number})"
35
+ method = build_method(method_name, *args)
36
+ watir_load_path = []
37
+ watir_lib_path = nil
38
+ $LOAD_PATH.each do |x|
39
+ if x =~ /rautomation/ || x =~ /watir/
40
+ watir_load_path << x
41
+ if x =~ /\/gems\/watir-/
42
+ watir_lib_path = x
43
+ end
44
+ end
45
+ end
46
+ watir_load_path = $LOAD_PATH
47
+ ruby_code = "$:.unshift(#{watir_load_path.map {|p| "'#{p}'" }.join(").unshift(")});" <<
48
+ "require '#{watir_lib_path}/watir/core';#{element}.#{method};"
49
+ return ruby_code
50
+ end
51
+
52
+ # customiiation here
53
+ #
54
+ def testwise_click_no_wait(ruby_code)
55
+ begin
56
+ puts "[TestWise] I am handling it"
57
+ assert_exists
58
+ assert_enabled
59
+ highlight(:set)
60
+ current_path = File.expand_path(".")
61
+
62
+ # not necessary
63
+ # ruby_code.gsub("C:/Program Files/TestWise/vendor/bundle/ruby/1.8", "C:/rubyshell/ruby/lib/ruby/gems/1.8")
64
+
65
+ # Trick 1: need to set RUBYOPT, otherwise might get -F error
66
+ ENV["RUBYOPT"] = "-rubygems"
67
+
68
+ # Trick 2: need to wrap no-wait click operation in a thread
69
+ Thread.new do
70
+ # this will pop up Windows Command window
71
+ # system("ruby", "-e", ruby_code)
72
+ system("rubyw", "-e", ruby_code)
73
+ end
74
+ highlight(:clear)
75
+ rescue RuntimeError => re
76
+ puts re.backtrace
77
+
78
+ rescue => e
79
+ puts "Failed to click_no_wait: #{e.backtrace}"
80
+ raise e
81
+ end
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'watir'
3
+
4
+ # Used for calling javacript of VBScript
5
+ # Applies to IE only
6
+ #
7
+ # Ref: http://msdn.microsoft.com/en-us/library/aa741364%28VS.85%29.aspx
8
+ #
9
+ module Watir
10
+ class IE
11
+ def execute_script(scriptCode)
12
+ window.execScript(scriptCode)
13
+ end
14
+
15
+ def window
16
+ ie.Document.parentWindow
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,359 @@
1
+ require File.join(File.dirname(__FILE__), "plugins", "loadwise_plugin.rb")
2
+ require "timeout"
3
+
4
+ module RWebSpec
5
+ module Mechanize
6
+
7
+ module LoadTestHelper
8
+
9
+ include RWebSpec::Mechanize::Utils
10
+ include RWebSpec::Mechanize::Assert
11
+ include RWebSpec::Mechanize::LoadWisePlugin
12
+
13
+ MAX_VU = 1000
14
+
15
+ # only support firefox or Celerity
16
+ def open_browser(base_url = nil, options = {})
17
+ default_options = {:resynchronize => false, :firefox => false, :go => false }
18
+ options = default_options.merge(options)
19
+
20
+ base_url ||= $LOADWISE_PROJECT_BASE_URL
21
+ if RUBY_PLATFORM =~ /java/
22
+ base_url ||= ENV["LOADWISE_PROJECT_BASE_URL"] # pass to java
23
+ end
24
+ base_url ||= $BASE_URL
25
+ puts "[DEBUG] open |#{base_url}|#{$BASE_URL}| in browser"
26
+
27
+ mode_preview = ($LOADWISE_PREVIEW || ENV['LOADWISE_PREVIEW'])
28
+
29
+ unless mode_preview
30
+ RWebSpec::Mechanize::WebBrowser.new(base_url, nil, options)
31
+ else
32
+ if RUBY_PLATFORM =~ /mingw/
33
+ puts "loading RWEBSPEC..."
34
+ require 'rwebspec'
35
+ RWebSpec.framework = "Watir"
36
+ RWebSpec::WebBrowser.new(base_url, nil, options)
37
+ else
38
+ require 'rwebspec'
39
+ RWebSpec.framework = "Selenium-WebDriver"
40
+ if RUBY_PLATFORM =~ /darwin/
41
+ if File.exists?("/usr/bin/chromedriver") && File.exists?("/usr/local/bin/chromedriver")
42
+ options[:browser] = "chrome"
43
+ else
44
+ options[:browser] = "firefox"
45
+ end
46
+ else
47
+ options[:browser] = "firefox"
48
+ end
49
+ RWebSpec::WebBrowser.new(base_url, nil, options)
50
+ end
51
+ end
52
+ end
53
+
54
+ # maybe attach_browser
55
+
56
+ # Does not provide real function, other than make enhancing test syntax
57
+ #
58
+ # Example:
59
+ # allow { click_button('Register') }
60
+ def allow(&block)
61
+ yield
62
+ end
63
+ alias shall_allow allow
64
+ alias allowing allow
65
+
66
+ # try operation, ignore if errors occur
67
+ #
68
+ # Example:
69
+ # failsafe { click_link("Logout") } # try logout, but it still OK if not being able to (already logout))
70
+ def failsafe(&block)
71
+ begin
72
+ yield
73
+ rescue =>e
74
+ end
75
+ end
76
+ alias fail_safe failsafe
77
+
78
+ # Try the operation up to specified timeout (in seconds), and sleep given interval (in seconds).
79
+ # Error will be ignored until timeout
80
+ # Example
81
+ # try_for { click_link('waiting')}
82
+ # try_for(10, 2) { click_button('Search' } # try to click the 'Search' button upto 10 seconds, try every 2 seconds
83
+ # try_for { click_button('Search' }
84
+ def try_for(timeout = $testwise_polling_timeout, polling_interval = $testwise_polling_interval || 1, &block)
85
+ start_time = Time.now
86
+
87
+ last_error = nil
88
+ until (duration = Time.now - start_time) > timeout
89
+ begin
90
+ return if yield
91
+ last_error = nil
92
+ rescue => e
93
+ last_error = e
94
+ end
95
+ sleep polling_interval
96
+ end
97
+
98
+ raise "Timeout after #{duration.to_i} seconds with error: #{last_error}." if last_error
99
+ raise "Timeout after #{duration.to_i} seconds."
100
+ end
101
+ alias try_upto try_for
102
+ alias try_until try_for
103
+
104
+ ##
105
+ # Convert :first to 1, :second to 2, and so on...
106
+ def symbol_to_sequence(symb)
107
+ value = { :zero => 0,
108
+ :first => 1,
109
+ :second => 2,
110
+ :third => 3,
111
+ :fourth => 4,
112
+ :fifth => 5,
113
+ :sixth => 6,
114
+ :seventh => 7,
115
+ :eighth => 8,
116
+ :ninth => 9,
117
+ :tenth => 10 }[symb]
118
+ return value || symb.to_i
119
+ end
120
+
121
+ # monitor current execution using
122
+ #
123
+ # Usage
124
+ # log_time { browser.click_button('Confirm') }
125
+ def log_time(msg, &block)
126
+ start_time = Time.now
127
+ begin;
128
+ dump_caller_stack;
129
+ rescue;
130
+ end;
131
+
132
+ Thread.current[:log] ||= []
133
+ begin
134
+ yield
135
+
136
+ Thread.current[:log] << {:file => File.basename(__FILE__),
137
+ :message => msg,
138
+ :start_time => Time.now,
139
+ :duration => Time.now - start_time}
140
+
141
+ rescue => e
142
+
143
+ Thread.current[:log] << {:file => File.basename(__FILE__),
144
+ :message => msg + " => Failed",
145
+ :start_time => Time.now,
146
+ :duration => Time.now - start_time}
147
+ ensure
148
+ end_time = Time.now
149
+ format_date_time = start_time.strftime("%Y-%m-%d %H:%M:%S")
150
+ connect_to_loadwise(" LOAD", "#{Thread.current[:id]}|#{msg}|#{format_date_time}|#{Time.now - start_time}")
151
+ end
152
+
153
+ end
154
+
155
+ def init_memory_database(force = true)
156
+ # Open a database
157
+ require 'sqlite3'
158
+ if $memory_db.nil? || force
159
+ $memory_db = SQLite3::Database.new ":memory:"
160
+
161
+
162
+ # Create a database
163
+ rows = $memory_db.execute "
164
+ create table load_test_stats (
165
+ id integer PRIMARY KEY,
166
+ virtual_user varchar(64),
167
+ test_file varchar(256),
168
+ description varchar(256),
169
+ started_at integer,
170
+ duration decimal(6, 2),
171
+ ended_at integer
172
+ );"
173
+ end
174
+
175
+ end
176
+
177
+ #
178
+ # @started_at : Unix timestamp to get back: Time.at()
179
+ #
180
+ def append_test_result(vu, test_file, description, started_at, duration)
181
+ return if RUBY_PLATFORM =~ /java/
182
+ init_memory_database if $memory_db.nil?
183
+ begin
184
+ $memory_db.execute("insert into load_test_stats (virtual_user, test_file, description, started_at, duration, ended_at) values (?, ?, ?, ?, ?, ?)", vu, "", description, started_at, duration, Time.now.to_i)
185
+ rescue => e
186
+ puts "[WARN] Failed to append test results: #{vu}|#{test_file}|#{description}|#{started_at}|#{duration}\n#{e} => #{e.backtrace}"
187
+ end
188
+
189
+ end
190
+
191
+ ##
192
+ #
193
+ # How many virtual users should be running immedately
194
+ # :start_virtual_user_count
195
+ #
196
+ # How many threads should be running at peak load.
197
+ # :peak_virtual_user_count
198
+ #
199
+ # How many seconds to wait between starting threads.
200
+ # :delay_between_thread_start
201
+ #
202
+ #
203
+ # How many minutes the test should run with all threads active.
204
+ # TIME_AT_PEAK_QPS = 10 # minutes
205
+ #
206
+ # Defaults:
207
+ # :start_virtual_user_count => 1, :peak_virtual_user_count => 3, :delay_between_thread_start => 10
208
+ def run_with_virtual_users(opts = {}, &block)
209
+
210
+ init_memory_database unless RUBY_PLATFORM =~ /java/
211
+ $vu_error_printed = false
212
+
213
+ if $load_runtime_options
214
+ default_opts = $load_runtime_options
215
+ else
216
+ if RUBY_PLATFORM =~ /java/
217
+ default_opts = {}
218
+ default_opts[:start_virtual_user_count] = ENV["LOADWISE_START_VIRTUAL_USER_COUNT"].to_i if ENV["LOADWISE_START_VIRTUAL_USER_COUNT"]
219
+ default_opts[:peak_virtual_user_count] = ENV["LOADWISE_PEAK_VIRTUAL_USER_COUNT"].to_i if ENV["LOADWISE_PEAK_VIRTUAL_USER_COUNT"]
220
+ default_opts[:delay_between_thread_start] = ENV["LOADWISE_DELAY_BETWEEN_THREAD_START"].to_i if ENV["LOADWISE_DELAY_BETWEEN_THREAD_START"]
221
+ default_opts[:for_how_long] = ENV["LOADWISE_FOR_HOW_LONG"].to_i if ENV["LOADWISE_FOR_HOW_LONG"]
222
+ else
223
+ # default to performance testing
224
+ default_opts = {:start_virtual_user_count => 1,
225
+ :peak_virtual_user_count => 1,
226
+ :delay_between_thread_start => 0 }
227
+ end
228
+ end
229
+
230
+ opts = default_opts.merge(opts)
231
+ puts "[INFO] {LoadTestHelper} opts => #{opts.inspect}"
232
+ start_virtual_user_count = opts[:start_virtual_user_count] || 2
233
+ peak_virtual_user_count = opts[:peak_virtual_user_count] || 2
234
+ delay_between_thread_start = opts[:delay_between_thread_start] || 0
235
+ for_how_long = opts[:for_how_long]
236
+
237
+ # puts "DEBUG for_how_long => #{for_how_long}"
238
+ if opts[:virtual_user_count] then
239
+ start_virtual_user_count ||= opts[:virtual_user_count]
240
+ peak_virtual_user_count ||= opts[:virtual_user_count]
241
+ end
242
+
243
+ raise "too many virtual users" if peak_virtual_user_count > MAX_VU
244
+
245
+ if $LOADWISE_PREVIEW
246
+ start_virtual_user_count = peak_virtual_user_count = 1
247
+ end
248
+
249
+ connect_to_loadwise("VU_START", "")
250
+
251
+ if (peak_virtual_user_count <= 1)
252
+ yield
253
+ else
254
+ threads = []
255
+ vu_reports = {}
256
+
257
+ start_virtual_user_count.times do |idx|
258
+ threads[idx] = Thread.new do
259
+ Thread.current[:id] = idx
260
+ start_time = Time.now
261
+ vu_reports[idx] ||= []
262
+ begin
263
+ if for_how_long
264
+ Timeout::timeout(for_how_long) do
265
+ while(true)
266
+ yield
267
+ end
268
+ end
269
+ else
270
+ yield
271
+ end
272
+
273
+ vu_reports[idx] = Thread.current[:log]
274
+ rescue Timeout::Error
275
+ vu_reports[idx] = Thread.current[:log]
276
+ puts "Too Slow 2"
277
+ rescue => e
278
+ vu_reports[idx] = Thread.current[:log]
279
+ vu_reports[idx] ||= []
280
+ vu_reports[idx] << { :error => e }
281
+ # TODO
282
+ connect_to_loadwise("VU_ERROR", Thread.current[:id].to_s + "|" + e.to_s)
283
+ unless $vu_error_printed
284
+ puts "VU[#{idx}] Failed: " + e.backtrace.to_s
285
+ $vu_error_printed = true
286
+ end
287
+
288
+ end
289
+ vu_reports[idx] ||= []
290
+ vu_reports[idx] << { :message => "Total Duration (Initial)", :start_time => start_time, :duration => Time.now - start_time }
291
+ connect_to_loadwise("VU_END", Thread.current[:id].to_s)
292
+ puts "VU[#{idx+1}] #{Time.now - start_time}s"
293
+ end
294
+ end
295
+
296
+ (peak_virtual_user_count - start_virtual_user_count).times do |nidx|
297
+ sleep delay_between_thread_start
298
+ idx = nidx + start_virtual_user_count
299
+ threads[idx] = Thread.new do
300
+ start_time = Time.now
301
+ vu_reports[idx] ||= []
302
+ begin
303
+ if for_how_long
304
+ Timeout::timeout(for_how_long) do
305
+ while(true)
306
+ yield
307
+ end
308
+ end
309
+ else
310
+ yield
311
+ end
312
+ vu_reports[idx] = Thread.current[:log]
313
+ rescue Timeout::Error
314
+ vu_reports[idx] = Thread.current[:log]
315
+ puts "!!!Too Slow 2"
316
+ rescue => e
317
+ vu_reports[idx] = Thread.current[:log]
318
+ vu_reports[idx] ||= []
319
+ vu_reports[idx] << { :error => e }
320
+ connect_to_loadwise("VU_ERROR", Thread.current[:id].to_s + "|" + e.to_s)
321
+ unless $vu_error_printed
322
+ puts "VU[#{idx}] Failed: " + e.backtrace.to_s
323
+ $vu_error_printed = true
324
+ end
325
+ end
326
+ vu_reports[idx] ||= []
327
+ vu_reports[idx] << { :message => "Total Duration (Peak)", :start_time => start_time, :duration => Time.now - start_time }
328
+ puts "VU[#{idx+1}] #{Time.now - start_time}s"
329
+ end
330
+ end
331
+
332
+ threads.each {|t| t.join; }
333
+
334
+ =begin
335
+ # after test finishing, don't try to parse the data, LoadWise can see $memory database
336
+
337
+ vu_reports.each do |key, value|
338
+ value.each do |entry|
339
+ append_test_result(key.to_s, entry[:file], entry[:message], entry[:start_time].to_i, entry[:duration])
340
+ if entry[:error] then
341
+ puts "{load_test_helper.rb} Execution Error: #{entry[:error]}"
342
+ append_test_result(key.to_s, entry[:file], entry[:error], entry[:start_time].to_i, entry[:duration])
343
+ else
344
+ puts "[#{key}] #{entry[:message]}, #{entry[:duration]}"
345
+ end
346
+ end
347
+ end
348
+ =end
349
+ return vu_reports
350
+
351
+
352
+ end
353
+ end
354
+
355
+ end
356
+
357
+
358
+ end
359
+ end
@@ -0,0 +1,93 @@
1
+ require 'socket'
2
+
3
+ module RWebSpec
4
+ module Mechanize
5
+
6
+ module LoadWisePlugin
7
+
8
+ def debug(message)
9
+ Thread.pass
10
+ connect_to_loadwise(" DEBUG", message.to_s + "\r\n") if $RUN_IN_TESTWISE && message
11
+ end
12
+
13
+ def connect_to_loadwise(message_type, body)
14
+ return if RUBY_PLATFORM !~ /java/i && $LOADWISE_TRACE_PORT.nil?
15
+ # Thread.pass
16
+ loadwise_port = ($LOADWISE_TRACE_PORT || 7125) + rand(5)
17
+ the_message = message_type + "|" + body
18
+
19
+ begin
20
+ # $log.info("[MESSAGE] " + the_message)
21
+ loadwise_socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
22
+ loadwise_socket.connect(Socket.pack_sockaddr_in(loadwise_port, '127.0.0.1'))
23
+ loadwise_socket.puts(the_message)
24
+ @last_message = the_message
25
+ loadwise_socket.close
26
+ rescue => e
27
+ puts("Failed to contact LoadWise '#{message_type}|#{body}' at #{loadwise_port}: #{e}")
28
+ retry
29
+ # $log.warn("Failed to contact TestWise: #{e}")
30
+ end
31
+ end
32
+
33
+ # Support of iTest to ajust the intervals between keystroke/mouse operations
34
+ def operation_delay
35
+ begin
36
+
37
+ if $TESTWISE_OPERATION_DELAY && $TESTWISE_OPERATION_DELAY > 0 &&
38
+ $TESTWISE_OPERATION_DELAY < 30000 then # max 30 seconds
39
+ Thread.pass
40
+ sleep($TESTWISE_OPERATION_DELAY / 1000)
41
+ end
42
+
43
+ while $TESTWISE_PAUSE || $LOADWISE_PAUSE
44
+ Thread.pass
45
+ debug("Paused, waiting ...")
46
+ sleep 1
47
+ end
48
+ rescue => e
49
+ puts "Error on delaying: #{e}"
50
+ # ignore
51
+ end
52
+ end
53
+
54
+ def notify_screenshot_location(image_file_path)
55
+ connect_to_loadwise(" SHOT", image_file_path)
56
+ end
57
+
58
+ # find out the line (and file) the execution is on, and notify iTest via Socket
59
+ def dump_caller_stack
60
+ # puts "CAlling dump_caller_stack"
61
+ return unless ($TESTWISE_TRACE_EXECUTION || $LOADWISE_TRACE_EXECUTION)
62
+ begin
63
+ trace_lines = []
64
+ trace_file = nil
65
+ found_first_spec_reference = false
66
+ caller.each_with_index do |position, idx|
67
+ next unless position =~ /\A(.*?):(\d+)/
68
+ trace_file = $1
69
+ if trace_file =~ /(_spec|_test|_rwebspec)\.rb\s*$/ || trace_file =~ /\.feature$/
70
+ found_first_spec_reference = true
71
+ trace_lines << position
72
+ break
73
+ end
74
+ trace_lines << position
75
+ break if trace_file =~ /example\/example_methods\.rb$/ or trace_file =~ /example\/example_group_methods\.rb$/
76
+ break if trace_lines.size > 10
77
+ # TODO: send multiple trace to be parse with pages.rb
78
+ # break if trace_file =~ /example\/example_methods\.rb$/ or trace_file =~ /example\/example_group_methods\.rb$/ or trace_file =~ /driver\.rb$/ or trace_file =~ /timeout\.rb$/ # don't include rspec or ruby trace
79
+ end
80
+
81
+ # (trace_file.include?("_spec.rb") || trace_file.include?("_rwebspec.rb") || trace_file.include?("_test.rb") || trace_file.include?("_cmd.rb"))
82
+ if !trace_lines.empty?
83
+ connect_to_loadwise(" TRACE", trace_lines.reverse.join("|"))
84
+ end
85
+
86
+ rescue => e
87
+ puts "failed to capture log: #{e}"
88
+ end
89
+ end
90
+
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,96 @@
1
+ require 'uri'
2
+
3
+ # ZZ patches to RSpec 1.1.2 - 1.1.4
4
+ # - add to_s method to example_group
5
+ module Spec
6
+ module Example
7
+ class ExampleGroup
8
+ def to_s
9
+ @_defined_description
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ # example
16
+ # should link_by_text(text, options).size > 0
17
+
18
+ module RWebSpec
19
+ module RSpecHelper
20
+ include RWebSpec::Driver
21
+ include RWebSpec::Utils
22
+ include RWebSpec::Assert
23
+
24
+ # --
25
+ # Content
26
+ # --
27
+
28
+ def table_source(table_id)
29
+ table(:id, table_id).innerHTML
30
+ # elem = @web_browser.document.getElementById(table_id)
31
+ # raise "The element '#{table_id}' is not a table or there are multple elements with same id" unless elem.name.uppercase == "TABLE"
32
+ # elem.innerHTML
33
+ end
34
+ alias table_source_by_id table_source
35
+
36
+ def element_text(elem_id)
37
+ @web_browser.element_value(elem_id)
38
+ end
39
+ alias element_text_by_id element_text
40
+
41
+ #TODO: is it working?
42
+ def element_source(elem_id)
43
+ @web_browser.get_html_in_element(elem_id)
44
+ end
45
+
46
+
47
+ def button_by_id(button_id)
48
+ button(:id, button_id)
49
+ end
50
+
51
+ def buttons_by_caption(text)
52
+ button(:text, text)
53
+ end
54
+ alias buttons_by_text buttons_by_caption
55
+
56
+ def link_by_id(link_id)
57
+ link(:id, link_id)
58
+ end
59
+
60
+ # default options: exact => true
61
+ def links_by_text(link_text, options = {})
62
+ options.merge!({:exact=> true})
63
+ matching_links = []
64
+ links.each { |link|
65
+ matching_links << link if (options[:exact] ? link.text == link_text : link.text.include?(link_text))
66
+ }
67
+ return matching_links
68
+ end
69
+ alias links_with_text links_by_text
70
+
71
+ def save_page(file_name = nil)
72
+ @web_browser.save_page(file_name)
73
+ end
74
+
75
+ def save_content_to_file(content, file_name = nil)
76
+ file_name ||= Time.now.strftime("%Y%m%d%H%M%S") + ".html"
77
+ puts "about to save page: #{File.expand_path(file_name)}"
78
+ File.open(file_name, "w").puts content
79
+ end
80
+
81
+ # When running
82
+ def debugging?
83
+ ($TESTWISE_DEBUGGING && $TESTWISE_RUNNING_AS == "test_case")
84
+ end
85
+
86
+ # RSpec Matchers
87
+ #
88
+ # Example,
89
+ # a_number.should be_odd_number
90
+ def be_odd_number
91
+ simple_matcher("must be odd number") { |actual| actual && actual.to_id % 2 == 1}
92
+ end
93
+
94
+ end
95
+
96
+ end