watirspec 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ === Version 0.1.0 / 2010-04-03
2
+
3
+ First release of WatiRspec, a small library for combining Watir and RSpec for browser-based functional testing in Ruby.
data/License.txt ADDED
@@ -0,0 +1,24 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2010 Jarmo Pertman
4
+
5
+ Permission is hereby granted, free of charge, to any person
6
+ obtaining a copy of this software and associated documentation
7
+ files (the "Software"), to deal in the Software without
8
+ restriction, including without limitation the rights to use,
9
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the
11
+ Software is furnished to do so, subject to the following
12
+ conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ OTHER DEALINGS IN THE SOFTWARE
data/README.rdoc ADDED
@@ -0,0 +1,161 @@
1
+ = WatiRspec
2
+
3
+ * Web: http://github.com/jarmo/WatiRspec
4
+ * Author: Jarmo Pertman (mailto:jarmo.p[at]gmail.com)
5
+
6
+ == DESCRIPTION:
7
+
8
+ WatiRspec is a small library for easier browser-based functional testing in Ruby.
9
+ It combines Watir (http://www.watir.com) for controlling the browser (currently mainly IE) and
10
+ RSpec (http://rspec.info) for testing framework. This powerful combination gives you
11
+ the ability to write easily well-maintained and easy-to-read specs (specifications in RSpec) so
12
+ you don't need to have any extra documentation for your applications.
13
+
14
+ WatiRspec makes it easier to use best features of both of these tools together so
15
+ you won't have to spend time on thinking how to do that yourself - you can start
16
+ testing right away!
17
+
18
+ == FEATURES:
19
+
20
+ * generate command for generating default project structure
21
+ * generate_common command for generating common ui-tests directory
22
+ * Browser will be opened and closed for each example group automatically
23
+ * You can use Watir method names directly without having to specify a browser object:
24
+ text_field(:name => "locator") # instead of @browser.text_field(:name => "locator")
25
+ * All needed libraries will be loaded and helper modules will be included automatically
26
+ * All JavaScript errors will be detected automatically
27
+ * Some additional methods to help using Watir (download_file, wait_until, wait_until! etc.)
28
+
29
+ * Custom html formatter for RSpec:
30
+ * Saves screenshot of the browser window
31
+ * Saves html of the page
32
+ * Saves all the files created/downloaded during the example and shows them on the report
33
+ * Automatically archives test results for later reviewing
34
+
35
+ == SYNOPSIS:
36
+
37
+ describe "Google" do
38
+ before :all do
39
+ goto "http://google.com"
40
+ url.should =~ /google/
41
+ end
42
+
43
+ it "has search field" do
44
+ text_field = text_field(:name => "q")
45
+ text_field.should exist
46
+ text_field.should be_visible
47
+ end
48
+
49
+ it "performs search" do
50
+ text_field(:name => "q").set "Bing"
51
+ button(:name => "btnG").click
52
+ text.should include("Bing")
53
+ end
54
+ end
55
+
56
+ C:\project\ui-test>watirspec spec\google_spec.rb
57
+ Results will be saved into the directory C:/project/ui-test/results
58
+ Google
59
+ has search field
60
+ performs search
61
+
62
+ Finished in 6.782388 seconds
63
+
64
+ 2 examples, 0 failures
65
+
66
+ == INSTALL:
67
+
68
+ * install Ruby 1.8.6:
69
+ http://rubyinstaller.org/
70
+
71
+ * install ImageMagick with rmagick-win32 for making the screenshots:
72
+ http://rubyforge.org/frs/?group_id=12&release_id=42049
73
+
74
+ * update RubyGems:
75
+ gem update --system
76
+
77
+ * install WatiRspec:
78
+ gem install watirspec
79
+
80
+ == USAGE:
81
+
82
+ If you have a web-application project (it may have been written in any programming language) without any browser-based tests,
83
+ then it has probably a directory structure similar to this example:
84
+ C:\example_project
85
+ ├───doc
86
+ ├───lib
87
+ └───test
88
+
89
+ Now from the command line go to this directory and execute generate command:
90
+ C:\>cd example_project
91
+
92
+ C:\example_project>watirspec generate
93
+ Creating WatiRspec project directory structure to C:/example_project/ui-test...
94
+ Done
95
+
96
+ After that the directory structure will be something like this:
97
+ C:\example_project
98
+ ├───doc
99
+ ├───lib
100
+ ├───test
101
+ └───ui-test
102
+ └───spec
103
+
104
+ The contents of that ui-test directory will be:
105
+ ui-test\application_helper.rb
106
+ ui-test\config.rb
107
+ ui-test\environment.rb
108
+ ui-test\ide_runner.rb
109
+
110
+ ui-test\spec
111
+ ui-test\spec\dummy_spec.rb
112
+
113
+ Just check out all the files to see some example code and comments in it and execute dummy_spec.rb
114
+ to verify that everything works correctly:
115
+ watirspec spec\dummy_spec.rb
116
+
117
+ You can have whatever directory structure for your tests just make sure that all test files have _spec.rb in
118
+ the end of their filename (if using default RSpec options).
119
+
120
+ == USAGE FOR MULTIPLE PROJECTS:
121
+
122
+ Usually you're going to write tests for multiple different projects. It would be shame if you'd
123
+ going to create all those common helper methods again for different projects or just copy-paste
124
+ the code from previous project's helpers. This is the place where ui-test-common comes into play.
125
+
126
+ ui-test-common would be a place where you can hold all your common functionality in helper
127
+ modules/methods/classes and then use those things in your tests so you won't have multiple
128
+ copies of similar or even same code in different places. So it helps you
129
+ to keep DRY (http://en.wikipedia.org/wiki/Don't_repeat_yourself).
130
+
131
+ After finding yourself in a situation where a new project comes into play, then execute
132
+ generate_common command once somewhere in a higher level of a directory tree than your project's ui-test directory
133
+ to generate ui-test-common directory:
134
+ C:\example_project>cd ..
135
+
136
+ C:\>watirspec generate_common
137
+ Creating WatiRspec common project directory structure to C:/ui-test-common...
138
+ Done
139
+
140
+ It has a structure of:
141
+ C:\UI-TEST-COMMON
142
+ └───lib
143
+
144
+ ui-test-common\config.rb
145
+ ui-test-common\environment.rb
146
+ ui-test-common\lib
147
+ ui-test-common\lib\common_application_helper.rb
148
+
149
+ In environment.rb under project/ui-test you shall add common functionality to your project.
150
+ Add the following line before any other require statements to do that:
151
+ WatiRspec::Util.load_common
152
+
153
+ This gives you by default the access to every method in ui-test-common/lib/common_application_helper.rb
154
+
155
+ Now, in ui-test-common/config.rb change the URL to your hostname and in config.rb under project/ui-test:
156
+ URL = Config.full_url("/relative_path")
157
+
158
+ This gives you the ability to have host and port written only in one place.
159
+
160
+ Now move all the common functionality away from your project's files into ui-test-common files and start testing.
161
+ From now on, add all common functionality into ui-test-common/lib
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ Spec::Rake::SpecTask.new(:rcov) do |t|
5
+ t.spec_files = FileList['spec/**/*_spec.rb']
6
+ t.rcov = true
7
+ t.rcov_dir = 'coverage'
8
+ t.rcov_opts << '--sort coverage --text-summary --aggregate coverage.data'
9
+ end
10
+
11
+ Spec::Rake::SpecTask.new(:spec) do |t|
12
+ t.spec_files = FileList['spec/**/*_spec.rb']
13
+ t.spec_opts << "--options" << "lib/spec.opts" <<
14
+ "--require" << "lib/watirspec/html_formatter" <<
15
+ "--format" << "WatiRspec::HtmlFormatter:results/index.html"
16
+ end
data/bin/watirspec ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ watirspec_dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $LOAD_PATH.unshift(watirspec_dir) unless $LOAD_PATH.include?(watirspec_dir)
4
+ require 'watirspec/runner'
5
+
6
+ if ARGV.size == 1 && ::WatiRspec::Runner.respond_to?(ARGV[0])
7
+ exit ::WatiRspec::Runner.send(ARGV[0])
8
+ else
9
+ exit ::WatiRspec::Runner.run
10
+ end
data/lib/spec.opts ADDED
@@ -0,0 +1,5 @@
1
+ --color
2
+ --format
3
+ nested
4
+ --diff
5
+ u
@@ -0,0 +1,54 @@
1
+ # Helper class for AutoIt
2
+ class AutoItHelper
3
+ extend WatiRspec::Waiter
4
+
5
+ @@autoit = Watir.autoit
6
+
7
+ class << self
8
+ # clicks save button on window with specified title,
9
+ # activates window automatically and makes sure that the click
10
+ # was successful
11
+ def click_save(window_title="File Download")
12
+ click_button(window_title, "&Save")
13
+ end
14
+
15
+ # sets edit field value to field_value on window with specified title,
16
+ # activates window automatically and makes sure that the field's
17
+ # value got changed
18
+ def set_edit(field_value, window_title="Save As")
19
+ set_field(window_title, "Edit1", field_value)
20
+ end
21
+
22
+ # sets specified field's value on window with specified title,
23
+ # activates window automatically and makes sure that the field's
24
+ # value got changed
25
+ def set_field(window_title, field_name, field_value)
26
+ wait_until! do
27
+ activate_window(window_title) &&
28
+ @@autoit.ControlFocus(window_title, "", field_name) == 1 &&
29
+ @@autoit.ControlSetText(window_title, "", field_name, field_value) == 1 &&
30
+ @@autoit.ControlGetText(window_title, "", field_name) == field_value
31
+ end
32
+ end
33
+
34
+ # clicks specified button on window with specified title,
35
+ # activates window automatically and makes sure that the click
36
+ # was successful
37
+ def click_button(window_title, button_name)
38
+ wait_until! do
39
+ activate_window(window_title) &&
40
+ @@autoit.ControlFocus(window_title, "", button_name) == 1 &&
41
+ @@autoit.ControlClick(window_title, "", button_name) == 1 &&
42
+ wait_until(3) {@@autoit.WinExists(window_title) == 0}
43
+ end
44
+ end
45
+
46
+ # makes window active with specified title
47
+ # * returns true if activation was successful and false otherwise
48
+ def activate_window(window_title)
49
+ @@autoit.WinWait(window_title, "", 1) == 1 &&
50
+ @@autoit.WinActivate(window_title) != 0 &&
51
+ @@autoit.WinActive(window_title) != 0
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec/runner/formatter/html_formatter'
2
+ require 'win32screenshot'
3
+ require 'rmagick'
4
+ require 'pathname'
5
+ require 'fileutils'
6
+
7
+ module WatiRspec
8
+ # Custom RSpec formatter for WatiRspec
9
+ # * saves screenshot of the browser upon test failure
10
+ # * saves html of the browser upon test failure
11
+ # * saves javascript error dialog upon test failure
12
+ # * saves all files generated/downloaded during the test and shows them in the report
13
+ class HtmlFormatter < ::Spec::Runner::Formatter::HtmlFormatter
14
+
15
+ # currently used browser object
16
+ # needed for saving of screenshots and html
17
+ attr_writer :browser
18
+
19
+ def initialize(options, output) # :nodoc:
20
+ raise "output has to be a file path!" unless output.is_a?(String)
21
+ @output_dir = File.expand_path(File.dirname(output))
22
+ puts "Results will be saved into the directory #{@output_dir}"
23
+ @files_dir = File.join(@output_dir, "files")
24
+ if File.exists?(@output_dir)
25
+ archive_dir = File.join(@output_dir, "../archive")
26
+ FileUtils.mkdir_p(archive_dir) unless File.exists?(archive_dir)
27
+ FileUtils.mv @output_dir, File.join(archive_dir, "#{File.basename(@output_dir)}_#{File.mtime(@output_dir).strftime("%y%m%d_%H%M%S")}")
28
+ end
29
+ FileUtils.mkdir_p(@files_dir)
30
+ super
31
+ end
32
+
33
+ def example_started(example) # :nodoc:
34
+ @files_saved_during_example = []
35
+ super
36
+ end
37
+
38
+ def extra_failure_content(failure) # :nodoc:
39
+ save_javascript_error
40
+ save_html
41
+ save_screenshot
42
+
43
+ content = []
44
+ content << "<span>"
45
+ @files_saved_during_example.each {|f| content << link_for(f)}
46
+ content << "</span>"
47
+ super + content.join($/)
48
+ end
49
+
50
+ def link_for(file) # :nodoc:
51
+ return unless File.exists?(file[:path])
52
+
53
+ description = file[:desc] ? file[:desc] : File.extname(file[:path]).upcase[1..-1]
54
+ path = Pathname.new(file[:path])
55
+ "<a href='#{path.relative_path_from(Pathname.new(@output_dir))}'>#{description}</a>&nbsp;"
56
+ end
57
+
58
+ def save_html # :nodoc:
59
+ begin
60
+ html = @browser.html
61
+ file_name = file_path("browser.html")
62
+ File.open(file_name, 'w') {|f| f.puts html}
63
+ rescue => e
64
+ $stderr.puts "saving of html failed: #{e.message}"
65
+ $stderr.puts e.backtrace
66
+ end
67
+ file_name
68
+ end
69
+
70
+ def save_screenshot(description="Screenshot", hwnd=@browser.hwnd) # :nodoc:
71
+ begin
72
+ @browser.bring_to_front
73
+ width, height, blob = Win32::Screenshot.capture_hwnd(hwnd)
74
+ file_name = file_path("screenshot.png", description)
75
+ img = Magick::ImageList.new
76
+ img.from_blob(blob)
77
+ img.write(file_name)
78
+ rescue => e
79
+ $stderr.puts "saving of screenshot failed: #{e.message}"
80
+ $stderr.puts e.backtrace
81
+ end
82
+ file_name
83
+ end
84
+
85
+ def save_javascript_error # :nodoc:
86
+ file_name = nil
87
+ if @browser.is_a?(Watir::IE) && @browser.status =~ /Error on page/
88
+ autoit = Watir::autoit
89
+ autoit.AutoItSetOption("MouseCoordMode", 0)
90
+ autoit.ControlClick("[TITLE:#{@browser.title}]", "", "[CLASS:msctls_statusbar32]", "left", 2)
91
+ popup_title = "[REGEXPTITLE:^(Windows )?Internet Explorer$]"
92
+ autoit.WinWait(popup_title, "", 10)
93
+ file_name = save_screenshot("JS_Error", autoit.WinGetHandle(popup_title).hex)
94
+ autoit.WinClose(popup_title)
95
+ end
96
+ file_name
97
+ end
98
+
99
+ # Generates unique file name and path for each example.
100
+ #
101
+ # All file names generated with this method will be shown
102
+ # on the report.
103
+ def file_path(file_name, description=nil)
104
+ extension = File.extname(file_name)
105
+ basename = File.basename(file_name, extension)
106
+ file_path = File.join(@files_dir, "#{basename}_#{Time.now.strftime("%H%M%S")}_#{example_group_number}_#{example_number}#{extension}")
107
+ @files_saved_during_example.unshift(:desc => description, :path => file_path)
108
+ file_path
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,17 @@
1
+ Spec::Runner.configure do |config| #:nodoc:
2
+ config.include(WatiRspec::SpecHelper)
3
+
4
+ config.before(:all) do
5
+ open_browser_at "about:blank"
6
+ end
7
+
8
+ config.after(:all) do
9
+ close
10
+ end
11
+ end
12
+
13
+ module Spec #:nodoc:all
14
+ class ExampleGroup
15
+ subject {self}
16
+ end
17
+ end
@@ -0,0 +1,97 @@
1
+ module WatiRspec
2
+
3
+ # WatiRspec runner class is responsible for:
4
+ # * generating directory structures for projects
5
+ # * starting RSpec with specified settings
6
+ class Runner
7
+ @@template_directory = File.join(File.dirname(__FILE__), "../../templates/")
8
+
9
+ class << self
10
+
11
+ # Run RSpec with custom settings
12
+ # * loads spec.opts from project's directory if exists
13
+ # * loads environment.rb from project's directory if exists
14
+ # * loads custom Formatter
15
+ def run
16
+ unless ARGV.empty?
17
+ require "watirspec"
18
+ load_formatter
19
+ load_options
20
+ load_project_env
21
+ else
22
+ return help
23
+ end
24
+
25
+ ::Spec::Runner::CommandLine.run
26
+ end
27
+
28
+ # Generates ui-test project structure for project
29
+ def generate
30
+ ui_test_dir = File.join(Dir.pwd, "ui-test")
31
+ puts "Creating WatiRspec project directory structure to #{ui_test_dir}..."
32
+ require "fileutils"
33
+ FileUtils.cp_r File.join(@@template_directory, "project/."), ui_test_dir
34
+ puts "Done"
35
+ return 0
36
+ rescue => e
37
+ puts "Failed:"
38
+ puts e.message
39
+ return -1
40
+ end
41
+
42
+ # Generates ui-test-common directory structure
43
+ def generate_common
44
+ common_dir = File.join(Dir.pwd, "ui-test-common")
45
+ puts "Creating WatiRspec common project directory structure to #{common_dir}..."
46
+ require "fileutils"
47
+ FileUtils.cp_r File.join(@@template_directory, "common/."), common_dir
48
+ puts "Done"
49
+ return 0
50
+ rescue => e
51
+ puts "Failed:"
52
+ puts e.message
53
+ return -1
54
+ end
55
+
56
+ # Shows help
57
+ def help
58
+ puts %Q{WatiRspec:
59
+ Usage: watirspec (COMMAND|FILE(:LINE)?|DIRECTORY|GLOB)+ [options]
60
+ Commands:
61
+ * generate - generate default directory structure for new project
62
+ * generate_common - generate common project directory structure
63
+ * help - show this help
64
+ * --help - show RSpec's help
65
+
66
+ All other commands/options will be passed to RSpec directly.}
67
+
68
+ return 1
69
+ end
70
+
71
+ private
72
+
73
+ def load_formatter
74
+ ARGV << "--require" << "watirspec/html_formatter.rb"
75
+ ARGV << "--format" << "WatiRspec::HtmlFormatter:#{File.join(Dir.pwd, "results/index.html")}"
76
+ end
77
+
78
+ def load_options
79
+ ARGV << "--options"
80
+ project_spec_opts = File.join(Dir.pwd, "spec.opts")
81
+ if File.exists?(project_spec_opts)
82
+ ARGV << project_spec_opts
83
+ else
84
+ ARGV << "#{File.join(File.dirname(__FILE__), "../spec.opts")}"
85
+ end
86
+ end
87
+
88
+ def load_project_env
89
+ project_env_file = File.join(Dir.pwd, "environment.rb")
90
+ if File.exists?(project_env_file)
91
+ ARGV << "--require" << project_env_file
92
+ end
93
+ end
94
+
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,68 @@
1
+ module WatiRspec
2
+ # main helper module
3
+ #
4
+ # these methods can be used in specs directly
5
+ module SpecHelper
6
+ include Waiter
7
+
8
+ # opens the browser at specified url
9
+ def open_browser_at url
10
+ @browser = Watir::Browser.new
11
+ @browser.speed = :fast
12
+ add_checker Watir::PageCheckers::JAVASCRIPT_ERRORS_CHECKER
13
+ begin
14
+ formatter.browser = @browser
15
+ rescue
16
+ end
17
+ goto url
18
+ maximize
19
+ end
20
+
21
+ # downloads file with browser
22
+ #
23
+ # you need to use click_no_wait to use this method:
24
+ # button(:id => "something").click_no_wait # opens a browser save as dialog
25
+ # download_file("document.pdf")
26
+ #
27
+ # * raises an exception if saving the file is unsuccessful
28
+ # * returns absolute file_path of the saved file
29
+ def download_file file_name
30
+ AutoItHelper.click_save
31
+ file_path = native_file_path(file_path(file_name))
32
+ AutoItHelper.set_edit(file_path)
33
+ AutoItHelper.click_save("Save As")
34
+ wait_until! {File.exists?(file_path)}
35
+ file_path
36
+ end
37
+
38
+ # returns WatiRspec::HtmlFormatter object, nil if not in use
39
+ def formatter
40
+ @formatter ||= Spec::Runner.options.formatters.find {|f| f.kind_of?(WatiRspec::HtmlFormatter) rescue false}
41
+ end
42
+
43
+ # returns unique file path for use in examples.
44
+ #
45
+ # all file names generated with this method will
46
+ # be shown on the report upon test failure.
47
+ def file_path(file_name, description=nil)
48
+ formatter.file_path(file_name, description)
49
+ rescue
50
+ extension = File.extname(file_name)
51
+ basename = File.basename(file_name, extension)
52
+ file_path = File.join(Dir.pwd, "#{basename}_#{Time.now.strftime("%H%M%S")}#{extension}")
53
+ file_path
54
+ end
55
+
56
+ # returns native file path
57
+ # e.g. on Windows:
58
+ # native_file_path("c:/blah/blah2/file.txt") => c:\\blah\\blah2\\file.txt
59
+ def native_file_path(file_path)
60
+ File::ALT_SEPARATOR ? file_path.gsub(File::SEPARATOR, File::ALT_SEPARATOR) : file_path
61
+ end
62
+
63
+ def method_missing name, *arg #:nodoc:
64
+ @browser.respond_to?(name) ? @browser.send(name, *arg) : super
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,35 @@
1
+ module WatiRspec
2
+ # class for common functionality
3
+ class Util
4
+ @@ui_test_common_dir = "ui-test-common"
5
+
6
+ class << self
7
+
8
+ # loads ui-test-common/environment.rb
9
+ #
10
+ # ui-test-common has to be located at some higher level within directory
11
+ # structure compared to project/ui-test directory
12
+ def load_common
13
+ dir = common_dir
14
+ puts "Loading ui-test-common from #{dir}..."
15
+ require File.join(dir, "environment.rb")
16
+ end
17
+
18
+ private
19
+
20
+ def common_dir
21
+ Dir.chdir("..") do
22
+ dirs = Dir.entries(Dir.pwd).find_all {|entry| File.directory?(entry)}
23
+ if dirs.include?(@@ui_test_common_dir) && File.exists?(@@ui_test_common_dir + "/environment.rb")
24
+ File.join(Dir.pwd, @@ui_test_common_dir)
25
+ elsif dirs.include?("..")
26
+ common_dir
27
+ else
28
+ raise "#{@@ui_test_common_dir} directory was not found! It has to exist somewhere higher in directory tree than your project's directory and it has to have environment.rb file in it!"
29
+ end
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module WatiRspec
2
+ VERSION = "0.1.0" #:nodoc:
3
+ end
@@ -0,0 +1,29 @@
1
+ module WatiRspec
2
+ module Waiter
3
+ # waits until some condition is true and
4
+ # throws Watir::Exception::TimeOutException upon timeout
5
+ #
6
+ # examples:
7
+ # wait_until! {text_field(:name => 'x').exists?} # waits until text field exists
8
+ # wait_until!(5) {...} # waits maximum of 5 seconds condition to be true
9
+ def wait_until! *arg
10
+ Watir::Waiter.wait_until(*arg) {yield}
11
+ end
12
+
13
+ # waits until some condition is true and
14
+ # returns false if timeout occurred, true otherwise
15
+ #
16
+ # examples:
17
+ # wait_until {text_field(:name => 'x').exists?} # waits until text field exists
18
+ # wait_until(5) {...} # waits maximum of 5 seconds condition to be true
19
+ def wait_until *arg
20
+ begin
21
+ wait_until!(*arg) {yield}
22
+ rescue Watir::Exception::TimeOutException
23
+ return false
24
+ end
25
+
26
+ return true
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,80 @@
1
+ module Watir
2
+ module PageCheckers
3
+ # raises an error if javascript error was found
4
+ JAVASCRIPT_ERRORS_CHECKER = lambda {|ie| raise "Got JavaScript error!" if ie.status =~ /Error on page/}
5
+ end
6
+ end
7
+
8
+ # patches for Watir
9
+ module Watir #:nodoc:all
10
+ class IE
11
+ # This is Watir's overriden wait method, which is used in many places for deciding
12
+ # if browser is ready or not. We have to patch one line in it to work properly
13
+ # when file save as dialog has been displayed. For some reason READYSTATE (4)
14
+ # property value will be READYSTATE_INTERACTIVE (3) after file has been downloaded
15
+ # and not 4, thus wait will stay blocking.
16
+ # read more about IE READYSTATE property:
17
+ # http://msdn.microsoft.com/en-us/library/aa768362(VS.85).aspx
18
+ def wait(no_sleep=false)
19
+ @xml_parser_doc = nil
20
+ @down_load_time = 0.0
21
+ a_moment = 0.2 # seconds
22
+ start_load_time = Time.now
23
+
24
+ begin
25
+ while @ie.busy # XXX need to add time out
26
+ sleep a_moment
27
+ end
28
+ # this is the line which has been changed to accept also state 3
29
+ until @ie.readyState <= READYSTATE_COMPLETE do
30
+ sleep a_moment
31
+ end
32
+ sleep a_moment
33
+ until @ie.document do
34
+ sleep a_moment
35
+ end
36
+
37
+ documents_to_wait_for = [@ie.document]
38
+
39
+ rescue WIN32OLERuntimeError # IE window must have been closed
40
+ @down_load_time = Time.now - start_load_time
41
+ sleep @pause_after_wait unless no_sleep
42
+ return @down_load_time
43
+ end
44
+
45
+ while doc = documents_to_wait_for.shift
46
+ begin
47
+ until doc.readyState == "complete" do
48
+ sleep a_moment
49
+ end
50
+ @url_list << doc.location.href unless @url_list.include?(doc.location.href)
51
+ doc.frames.length.times do |n|
52
+ begin
53
+ documents_to_wait_for << doc.frames[n.to_s].document
54
+ rescue WIN32OLERuntimeError, NoMethodError
55
+ end
56
+ end
57
+ rescue WIN32OLERuntimeError
58
+ end
59
+ end
60
+
61
+ @down_load_time = Time.now - start_load_time
62
+ run_error_checks
63
+ sleep @pause_after_wait unless no_sleep
64
+ @down_load_time
65
+ end
66
+ end
67
+
68
+ module PageContainer
69
+ # patch for .click_no_wait
70
+ def eval_in_spawned_process(command)
71
+ command.strip!
72
+ ruby_code = "require 'rubygems';"
73
+ ruby_code << "require 'watir/ie';"
74
+ ruby_code << "pc = #{attach_command};"
75
+ ruby_code << "pc.instance_eval(#{command.inspect});"
76
+ exec_string = "start rubyw -e #{ruby_code.inspect}".gsub("\\\"", "'")
77
+ system(exec_string)
78
+ end
79
+ end
80
+ end
data/lib/watirspec.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "rubygems"
2
+ require "require_all"
3
+ require "spec"
4
+ require "watir"
5
+ require "watirspec/util"
6
+ require "watirspec/waiter"
7
+ require "watirspec/spec_helper"
8
+ require "watirspec/rspec"
9
+ require "watirspec/watir"
10
+ require "watirspec/autoit"
11
+
@@ -0,0 +1,56 @@
1
+ require "watirspec"
2
+ require "spec/autorun"
3
+
4
+ describe WatiRspec::SpecHelper do
5
+
6
+ it "opens browser automatically" do
7
+ @browser.should exist
8
+ @browser.url.should == "about:blank"
9
+ @browser.title.should == ""
10
+ end
11
+
12
+ it "redirects method calls to Watir::Browser" do
13
+ goto "http://google.com"
14
+ url.should =~ /google/
15
+ title.should =~ /google/i
16
+ text_field = text_field(:name => "q")
17
+ text_field.should exist
18
+ text_field.should be_visible
19
+ end
20
+
21
+ it "has wait_until" do
22
+ result = wait_until {sleep 0.1; true}
23
+ result.should be_true
24
+
25
+ result = wait_until(0.5) {sleep 0.1; false}
26
+ result.should be_false
27
+ end
28
+
29
+ it "has wait_until!" do
30
+ lambda {wait_until!(0.5) {sleep 0.1; true}}.should_not raise_exception
31
+ lambda {wait_until!(0.5) {sleep 0.1; false}}.should raise_exception(Watir::Exception::TimeOutException)
32
+ end
33
+
34
+ it "has file_path methods without using formatter" do
35
+ file_name = "blah.temp"
36
+ ext = File.extname(file_name)
37
+ base = File.basename(file_name, ext)
38
+ expected_path = File.join(Dir.pwd, "#{base}_\\d{6}#{ext}")
39
+
40
+ file_path(file_name).should =~ Regexp.new(expected_path)
41
+ expected_path = expected_path.gsub("/", "\\")
42
+ native_file_path(file_path(file_name)).should =~ Regexp.new(Regexp.escape(expected_path).gsub("\\\\d\\{6\\}", "\\d{6}"))
43
+ end
44
+
45
+ it "has download_file method" do
46
+ begin
47
+ goto "http://dl.dropbox.com/u/2731643/misc/download.html"
48
+ link(:text => "Download").click_no_wait
49
+ file = download_file("download.zip")
50
+ File.read(file).should == "this is a 'zip' file!"
51
+ ensure
52
+ FileUtils.rm file
53
+ end
54
+ end
55
+
56
+ end
data/spec/util_spec.rb ADDED
@@ -0,0 +1,38 @@
1
+ require "watirspec"
2
+ require "spec/autorun"
3
+
4
+ describe WatiRspec::Util do
5
+
6
+ it "loads ui-test-common" do
7
+ class WatiRspec::Util
8
+ @@ui_test_common_dir = "ui-test-common-for-test"
9
+ end
10
+
11
+ begin
12
+ ui_test_common_dir = "../ui-test-common-for-test"
13
+ FileUtils.mkdir(ui_test_common_dir)
14
+ File.open(File.join(ui_test_common_dir, "environment.rb"), "w") do |f|
15
+ f.puts "
16
+ module GlobalApplication
17
+ LOADED = true
18
+ end"
19
+ end
20
+
21
+ lambda {WatiRspec::Util.load_common}.should_not raise_exception
22
+ GlobalApplication::LOADED.should be_true
23
+ ensure
24
+ FileUtils.rm_rf(ui_test_common_dir)
25
+ end
26
+ end
27
+
28
+ it "raises exception if ui-test-common is not found" do
29
+ class WatiRspec::Util
30
+ @@ui_test_common_dir = "nonexisting_ui_test_common_dir"
31
+ end
32
+
33
+ lambda {WatiRspec::Util.load_common}.
34
+ should raise_exception(RuntimeError,
35
+ "nonexisting_ui_test_common_dir directory was not found! It has to exist somewhere higher in directory tree than your project's directory and it has to have environment.rb file in it!")
36
+ end
37
+
38
+ end
@@ -0,0 +1,28 @@
1
+ =begin
2
+ Global configuration constants
3
+ For example, you can use the URL constant below from your projects like this:
4
+
5
+ In your projects' config.rb:
6
+
7
+ module Config
8
+ module Application
9
+ URL = Config.full_url("my_subpage/index.html") # => equals "http://localhost/my_subpage/index.html
10
+ end
11
+ end
12
+ =end
13
+
14
+ require "uri"
15
+
16
+ module Config
17
+ module GlobalApplication
18
+ URL = "http://localhost"
19
+ end
20
+
21
+ def Config.full_url relative_url
22
+ URI.join(GlobalApplication::URL, relative_url).to_s
23
+ end
24
+ end
25
+
26
+ Spec::Runner.configure do |config|
27
+ config.include(CommonApplicationHelper)
28
+ end
@@ -0,0 +1,17 @@
1
+ =begin
2
+ you have to require this file from your projects' environment.rb, which
3
+ will use common functionality:
4
+ require_rel "../../../ui-test-common/environment.rb
5
+
6
+ add all your require statements into this file to avoid unnecessary
7
+ code in your other projects' files
8
+
9
+ by default everything, which is not a spec file, will be loaded
10
+ =end
11
+
12
+ local_dir = File.join(File.dirname(__FILE__), "**/*.rb")
13
+ filtered_ruby_files = Dir.glob(local_dir).delete_if do |file|
14
+ File.directory?(file) || File.basename(file) =~ /(config|_spec)\.rb$/
15
+ end
16
+ require_all filtered_ruby_files
17
+ require_rel "config.rb"
@@ -0,0 +1,10 @@
1
+ # you can create under this directory common libraries/helpers/methods which you could
2
+ # use within your specs in different projects
3
+
4
+ module CommonApplicationHelper
5
+
6
+ def new_global_method
7
+ "it just works"
8
+ end
9
+
10
+ end
@@ -0,0 +1,18 @@
1
+ # ApplicationHelper module has helper methods, for easy use in specs
2
+ # see usages in spec/dummy_spec.rb
3
+
4
+ module ApplicationHelper
5
+
6
+ def helper_method_one
7
+ "one"
8
+ end
9
+
10
+ def has_second_method?
11
+ true
12
+ end
13
+
14
+ def correct?
15
+ false
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ # Config for your application
2
+ module Config
3
+ module Application
4
+ # URL, which will be opened by every test
5
+ # replace it with the URL of your application under test
6
+ URL = "about:blank"
7
+ end
8
+ end
9
+
10
+ # A global configuration for specs, which will include by default
11
+ # a ApplicationHelper module and open Config::Application::URL with
12
+ # the browser.
13
+ # You can read more about RSpec-s before and after syntax from:
14
+ # http://rspec.info/documentation/before_and_after.html
15
+ Spec::Runner.configure do |config|
16
+ config.include(ApplicationHelper)
17
+ config.before(:all) {goto Config::Application::URL}
18
+ end
@@ -0,0 +1,10 @@
1
+ # add all your require statements into this file to avoid unnecessary
2
+ # code in your spec files
3
+
4
+ # by default everything, which is not a spec file, will be loaded
5
+ local_dir = File.join(File.dirname(__FILE__), "**/*.rb")
6
+ filtered_ruby_files = Dir.glob(local_dir).delete_if do |file|
7
+ File.directory?(file) || File.basename(file) =~ /(ide_runner|config|_spec)\.rb$/
8
+ end
9
+ require_all filtered_ruby_files
10
+ require_rel "config.rb"
@@ -0,0 +1,3 @@
1
+ # this file is needed for launching specs from IDE
2
+ require "watirspec/runner"
3
+ exit ::WatiRspec::Runner.run
@@ -0,0 +1,44 @@
1
+ # this is just a fully working dummy spec file which you can run to see if
2
+ # your configuration is correct and everything is working
3
+
4
+ describe "WatiRspec" do
5
+
6
+ it "has the browser window opened" do
7
+ url.should == "about:blank"
8
+ end
9
+
10
+ it "has easy access to ApplicationHelper methods" do
11
+ # ApplicationHelper#helper_method_one will be executed
12
+ helper_method_one.should == "one"
13
+ # ApplicationHelper#has_second_method? will be execute
14
+ should have_second_method
15
+ # ApplicationHelper#correct? method will be execute
16
+ should_not be_correct
17
+ end
18
+
19
+ it "fails the example and makes a screenshot of the browser" do
20
+ false.should be_true
21
+ end
22
+
23
+ it "is in pending status" do
24
+ goto "http://google.com"
25
+ title.should == "Google"
26
+ text_field(:name => "q").set "Bing"
27
+ search_button = button(:name => "btnG")
28
+ search_button.should exist
29
+ search_button.should be_visible
30
+ search_button.click
31
+ text.should include("Bing")
32
+ pending "this is a known 'bug'" do
33
+ title.should == "Bing"
34
+ end
35
+ end
36
+
37
+ it "has also access to global methods" do
38
+ pending "it fails as long as there's not executed 'watirspec generate_common' command
39
+ and ui-test-common is not loaded by this project" do
40
+ new_global_method.should == "it just works"
41
+ end
42
+ end
43
+
44
+ end
metadata ADDED
@@ -0,0 +1,192 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: watirspec
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Jarmo Pertman
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-03 00:00:00 +03:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: watir
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 6
30
+ - 5
31
+ version: 1.6.5
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 1
43
+ - 3
44
+ - 0
45
+ version: 1.3.0
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: diff-lcs
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ type: :runtime
59
+ version_requirements: *id003
60
+ - !ruby/object:Gem::Dependency
61
+ name: require_all
62
+ prerelease: false
63
+ requirement: &id004 !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ type: :runtime
71
+ version_requirements: *id004
72
+ - !ruby/object:Gem::Dependency
73
+ name: rmagick
74
+ prerelease: false
75
+ requirement: &id005 !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 2
81
+ - 12
82
+ - 0
83
+ version: 2.12.0
84
+ type: :runtime
85
+ version_requirements: *id005
86
+ - !ruby/object:Gem::Dependency
87
+ name: syntax
88
+ prerelease: false
89
+ requirement: &id006 !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ segments:
94
+ - 0
95
+ version: "0"
96
+ type: :runtime
97
+ version_requirements: *id006
98
+ - !ruby/object:Gem::Dependency
99
+ name: win32console
100
+ prerelease: false
101
+ requirement: &id007 !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ type: :runtime
109
+ version_requirements: *id007
110
+ description: Combines best features of Watir, RSpec and Ruby for browser-based functional testing.
111
+ email:
112
+ - jarmo.p@gmail.com
113
+ executables:
114
+ - watirspec
115
+ extensions: []
116
+
117
+ extra_rdoc_files:
118
+ - README.rdoc
119
+ - History.rdoc
120
+ - License.txt
121
+ files:
122
+ - bin/watirspec
123
+ - History.rdoc
124
+ - lib/spec.opts
125
+ - lib/watirspec/autoit.rb
126
+ - lib/watirspec/html_formatter.rb
127
+ - lib/watirspec/rspec.rb
128
+ - lib/watirspec/runner.rb
129
+ - lib/watirspec/spec_helper.rb
130
+ - lib/watirspec/util.rb
131
+ - lib/watirspec/version.rb
132
+ - lib/watirspec/waiter.rb
133
+ - lib/watirspec/watir.rb
134
+ - lib/watirspec.rb
135
+ - License.txt
136
+ - Rakefile
137
+ - README.rdoc
138
+ - spec/spec_helper_spec.rb
139
+ - spec/util_spec.rb
140
+ - templates/common/config.rb
141
+ - templates/common/environment.rb
142
+ - templates/common/lib/common_application_helper.rb
143
+ - templates/project/application_helper.rb
144
+ - templates/project/config.rb
145
+ - templates/project/environment.rb
146
+ - templates/project/ide_runner.rb
147
+ - templates/project/spec/dummy_spec.rb
148
+ - watirspec-0.1.0.gem
149
+ has_rdoc: true
150
+ homepage: http://github.com/jarmo/WatiRspec
151
+ licenses: []
152
+
153
+ post_install_message: |-
154
+ *************************
155
+
156
+ Thank you for installing WatiRspec! Don't forget to take a look at README file!
157
+
158
+ Execute "watirspec generate" under your project's directory to generate default project structure.
159
+
160
+ *************************
161
+ rdoc_options:
162
+ - --main
163
+ - README.rdoc
164
+ - --template
165
+ - hanna
166
+ - --inline-source
167
+ - --format=html
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ segments:
175
+ - 0
176
+ version: "0"
177
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ segments:
182
+ - 0
183
+ version: "0"
184
+ requirements: []
185
+
186
+ rubyforge_project:
187
+ rubygems_version: 1.3.6
188
+ signing_key:
189
+ specification_version: 3
190
+ summary: watirspec 0.1.0
191
+ test_files: []
192
+