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