rwebspec-mechanize 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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