hanoi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Luca Guidi
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,114 @@
1
+ h1. Automated jQuery tests with QUnit
2
+
3
+ h2. Installation
4
+
5
+ (sudo) gem install jodosha-hanoi -s http://gems.github.com
6
+
7
+ h2. Crash Course
8
+
9
+ hanoi # prepare the target directory
10
+ rake test:js # run the tests
11
+
12
+ h2. How To Use
13
+
14
+ Prepare the target directory, you can use one of the following options:
15
+
16
+ hanoi
17
+ hanoi .
18
+ hanoi /path/to/project
19
+
20
+ You need a valid "Rake":http://rake.rubyforge.org/ installation to run your tests:
21
+
22
+ rake test:js
23
+
24
+ You can specify to run the test against one or more browsers:
25
+
26
+ rake test:js BROWSERS=firefox,ie,safari
27
+
28
+ You can specify to run only certain tests:
29
+
30
+ rake test:js TESTS=path/to/first_test.js,path/to/second_test.js
31
+
32
+ You can combine both @BROWSERS@ and @TESTS@ configurations.
33
+
34
+ h2. Structure
35
+
36
+ The @hanoi@ executable will create the following structure:
37
+
38
+ Rakefile
39
+ test/
40
+ javascript/
41
+ assets/
42
+ jquery.js
43
+ testrunner.js
44
+ testsuite.css
45
+ example_test.js
46
+ fixtures/
47
+ example_fixtures.html
48
+ templates/
49
+ test_case.erb
50
+
51
+ * @Rakefile@ creates a fresh rakefile, *you have to edit* it according to your setup
52
+ * @test@ Hanoi creates it if missing, otherwise will choose between existing @test@ or @spec@ paths
53
+ * @javascript@ is the root directory of your tests
54
+ * @assets@ contains the jQuery and QUnit source file and a css.
55
+ Place your assets in this directory, it's mapped as root path @/@.
56
+ * @example_test.js@ is a sample of a real test case.
57
+ * @templates@ contains the template file for your tests. You shouldn't edit it, if you don't know the risk.
58
+ * @fixtures@ contains all the HTML fixtures, each file will be injected into the proper case.
59
+
60
+ By convention, your @test/javascript@ folder *should reflect* the structure of your source directory,
61
+ appending the @_test.js@ suffix to each test case and the @_fixtures.html@ to each fixture file.
62
+
63
+ Example:
64
+
65
+ src/
66
+ directory_a/
67
+ directory_b/
68
+ file_3.js
69
+ file_2.js
70
+ file_1.js
71
+ test/
72
+ javascript/
73
+ directory_a/
74
+ directory_b/
75
+ file_3_test.js
76
+ file_2_test.js
77
+ file_1_test.js
78
+ fixtures/
79
+ directory_a/
80
+ directory_b/
81
+ file_3_fixtures.html
82
+ file_1_fixtures.html
83
+
84
+ You have probably noticed that @file_2_fixtures.html@ is missing, this because *fixtures are optional*.
85
+
86
+ h2. Browsers & Platforms
87
+
88
+ h3. Platforms:
89
+
90
+ * MacOS
91
+ * Windows
92
+ * Linux
93
+
94
+ h3. Browsers:
95
+
96
+ * Firefox
97
+ * Safari
98
+ * Internet Explorer
99
+ * Opera
100
+ * Konqueror
101
+
102
+ h2. Acknowledgements
103
+
104
+ The Ruby libraries of Hanoi are a customization of the jsblib.rb, courtesy of the "Prototype":http://prototypejs.org team.
105
+
106
+ Hanoi runs a customized version of QUnit, courtesy of the "jQuery":http://jquery.com team.
107
+
108
+ h2. Repository
109
+
110
+ git clone git://github.com/jodosha/hanoi.git
111
+
112
+ h2. Copyright
113
+
114
+ (c) 2009 Luca Guidi - "http://lucaguidi.com":http://lucaguidi.com, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ $:.unshift 'lib'
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rake/rdoctask'
6
+ require 'spec/rake/spectask'
7
+
8
+ HANOI_VERSION = "0.0.1"
9
+
10
+ task :default => :spec
11
+
12
+ desc 'Build and install the gem (useful for development purposes).'
13
+ task :install do
14
+ system "gem build hanoi.gemspec"
15
+ system "sudo gem uninstall hanoi"
16
+ system "sudo gem install --local --no-rdoc --no-ri hanoi-#{HANOI_VERSION}.gem"
17
+ system "rm hanoi-*.gem"
18
+ end
19
+
20
+ Spec::Rake::SpecTask.new do |t|
21
+ t.spec_files = FileList['spec/**/*_spec.rb']
22
+ t.spec_opts = %w(-fs --color)
23
+ end
24
+
25
+ desc 'Show the file list for the gemspec file'
26
+ task :files do
27
+ puts "Files:\n #{Dir['**/*'].reject {|f| File.directory?(f)}.sort.inspect}"
28
+ puts "Test files:\n #{Dir['spec/**/*_spec.rb'].reject {|f| File.directory?(f)}.sort.inspect}"
29
+ end
data/bin/hanoi ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ require "fileutils"
3
+
4
+ path = ARGV[0] || Dir.pwd
5
+ raise "Unknown path '#{path}'" unless File.directory?(path)
6
+ templates_path = File.expand_path(File.dirname(__FILE__) + "/../templates")
7
+
8
+ # Rakefile
9
+ File.open(File.join(path, "Rakefile"), "a") { |f| f.write(File.readlines(File.join(templates_path, "fresh_rakefile")))}
10
+
11
+ # JavaScript test directory
12
+ name = File.directory?(File.join(path, "spec")) ? "spec" : "test"
13
+ path = File.join(path, name, "javascript")
14
+ FileUtils.mkdir_p path
15
+
16
+ # Assets
17
+ FileUtils.cp_r File.join(templates_path, "assets"), File.join(path)
18
+
19
+ # Template
20
+ FileUtils.mkdir_p File.join(path, "templates")
21
+ FileUtils.cp File.join(templates_path, "test_case.erb"), File.join(path, "templates")
22
+
23
+ # Samples
24
+ FileUtils.mkdir_p File.join(path, "fixtures")
25
+ FileUtils.cp File.join(templates_path, "example_fixtures.html"), File.join(path, "fixtures")
26
+ FileUtils.cp File.join(templates_path, "example_test.js"), path
data/hanoi.gemspec ADDED
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "hanoi"
3
+ s.version = "0.0.1"
4
+ s.date = "2009-06-09"
5
+ s.summary = "Automated jQuery tests with QUnit"
6
+ s.author = "Luca Guidi"
7
+ s.email = "guidi.luca@gmail.com"
8
+ s.description = "Automated jQuery tests with QUnit"
9
+ s.has_rdoc = true
10
+ s.executables = [ 'hanoi' ]
11
+ s.files = ["MIT-LICENSE", "README.textile", "Rakefile", "bin/hanoi", "hanoi.gemspec", "lib/hanoi.rb", "lib/hanoi/browser.rb", "lib/hanoi/browsers/firefox.rb", "lib/hanoi/browsers/internet_explorer.rb", "lib/hanoi/browsers/konqueror.rb", "lib/hanoi/browsers/opera.rb", "lib/hanoi/browsers/safari.rb", "lib/hanoi/javascript_test_task.rb", "lib/hanoi/test_case.rb", "lib/hanoi/test_results.rb", "lib/hanoi/test_suite_results.rb", "lib/hanoi/webrick.rb", "spec/browser_spec.rb", "spec/browsers/firefox_spec.rb", "spec/browsers/internet_explorer_spec.rb", "spec/browsers/konqueror_spec.rb", "spec/browsers/opera_spec.rb", "spec/browsers/safari_spec.rb", "spec/spec_helper.rb", "templates/assets/jquery.js", "templates/assets/testrunner.js", "templates/assets/testsuite.css", "templates/example_fixtures.html", "templates/example_test.js", "templates/fresh_rakefile", "templates/test_case.erb"]
12
+ s.test_files = ["spec/browser_spec.rb", "spec/browsers/firefox_spec.rb", "spec/browsers/internet_explorer_spec.rb", "spec/browsers/konqueror_spec.rb", "spec/browsers/opera_spec.rb", "spec/browsers/safari_spec.rb"]
13
+ s.extra_rdoc_files = ['README.textile']
14
+ end
data/lib/hanoi.rb ADDED
@@ -0,0 +1,14 @@
1
+ require "erb"
2
+ require "webrick"
3
+ require "rake/tasklib"
4
+ require "hanoi/webrick"
5
+ require "hanoi/browser"
6
+ require "hanoi/browsers/firefox"
7
+ require "hanoi/browsers/internet_explorer"
8
+ require "hanoi/browsers/konqueror"
9
+ require "hanoi/browsers/safari"
10
+ require "hanoi/browsers/opera"
11
+ require "hanoi/test_case"
12
+ require "hanoi/test_results"
13
+ require "hanoi/test_suite_results"
14
+ require "hanoi/javascript_test_task"
@@ -0,0 +1,28 @@
1
+ class Browser
2
+ def supported?; true; end
3
+ def setup ; end
4
+ def open(url) ; end
5
+ def teardown ; end
6
+
7
+ def host
8
+ require 'rbconfig'
9
+ Config::CONFIG['host']
10
+ end
11
+
12
+ def macos?
13
+ host.include?('darwin')
14
+ end
15
+
16
+ def windows?
17
+ host.include?('mswin')
18
+ end
19
+
20
+ def linux?
21
+ host.include?('linux')
22
+ end
23
+
24
+ def applescript(script)
25
+ raise "Can't run AppleScript on #{host}" unless macos?
26
+ system "osascript -e '#{script}' 2>&1 >/dev/null"
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ class Firefox < Browser
2
+ def initialize(path = File.join(ENV['ProgramFiles'] || 'c:\Program Files', '\Mozilla Firefox\firefox.exe'))
3
+ @path = path
4
+ end
5
+
6
+ def visit(url)
7
+ system("open -a Firefox '#{url}'") if macos?
8
+ system("#{@path} #{url}") if windows?
9
+ system("firefox #{url}") if linux?
10
+ end
11
+
12
+ def to_s
13
+ "Firefox"
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ class InternetExplorer < Browser
2
+ def setup
3
+ require 'win32ole' if windows?
4
+ end
5
+
6
+ def supported?
7
+ windows?
8
+ end
9
+
10
+ def visit(url)
11
+ if windows?
12
+ ie = WIN32OLE.new('InternetExplorer.Application')
13
+ ie.visible = true
14
+ ie.Navigate(url)
15
+ sleep 0.01 while ie.Busy || ie.ReadyState != 4
16
+ end
17
+ end
18
+
19
+ def to_s
20
+ "Internet Explorer"
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ class Konqueror < Browser
2
+ @@config_dir = File.join((ENV['HOME'] || ''), '.kde', 'share', 'config')
3
+ @@global_config = File.join(@@config_dir, 'kdeglobals')
4
+ @@konqueror_config = File.join(@@config_dir, 'konquerorrc')
5
+
6
+ def supported?
7
+ linux?
8
+ end
9
+
10
+ # Forces KDE's default browser to be Konqueror during the tests, and forces
11
+ # Konqueror to open external URL requests in new tabs instead of a new
12
+ # window.
13
+ def setup
14
+ cd @@config_dir, :verbose => false do
15
+ copy @@global_config, "#{@@global_config}.bak", :preserve => true, :verbose => false
16
+ copy @@konqueror_config, "#{@@konqueror_config}.bak", :preserve => true, :verbose => false
17
+ # Too lazy to write it in Ruby... Is sed dependency so bad?
18
+ system "sed -ri /^BrowserApplication=/d '#{@@global_config}'"
19
+ system "sed -ri /^KonquerorTabforExternalURL=/s:false:true: '#{@@konqueror_config}'"
20
+ end
21
+ end
22
+
23
+ def teardown
24
+ cd @@config_dir, :verbose => false do
25
+ copy "#{@@global_config}.bak", @@global_config, :preserve => true, :verbose => false
26
+ copy "#{@@konqueror_config}.bak", @@konqueror_config, :preserve => true, :verbose => false
27
+ end
28
+ end
29
+
30
+ def visit(url)
31
+ system("kfmclient openURL #{url}")
32
+ end
33
+
34
+ def to_s
35
+ "Konqueror"
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ class Opera < Browser
2
+ def initialize(path = 'c:\Program Files\Opera\Opera.exe')
3
+ @path = path
4
+ end
5
+
6
+ def setup
7
+ if windows?
8
+ puts %{
9
+ MAJOR ANNOYANCE on Windows.
10
+ You have to shut down Opera manually after each test
11
+ for the script to proceed.
12
+ Any suggestions on fixing this is GREATLY appreciated!
13
+ Thank you for your understanding.
14
+ }
15
+ end
16
+ end
17
+
18
+ def visit(url)
19
+ applescript('tell application "Opera" to GetURL "' + url + '"') if macos?
20
+ system("#{@path} #{url}") if windows?
21
+ system("opera #{url}") if linux?
22
+ end
23
+
24
+ def to_s
25
+ "Opera"
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ class Safari < Browser
2
+ def supported?
3
+ macos? || windows?
4
+ end
5
+
6
+ def setup
7
+ applescript('tell application "Safari" to make new document')
8
+ end
9
+
10
+ def visit(url)
11
+ applescript('tell application "Safari" to set URL of front document to "' + url + '"')
12
+ end
13
+
14
+ def teardown
15
+ #applescript('tell application "Safari" to close front document')
16
+ end
17
+
18
+ def to_s
19
+ "Safari"
20
+ end
21
+ end
@@ -0,0 +1,190 @@
1
+ class JavaScriptTestTask < ::Rake::TaskLib
2
+ BROWSERS = %w( safari firefox ie konqueror opera ).freeze
3
+ attr_reader :sources_directory
4
+
5
+ def initialize(name = :test)
6
+ @name = name
7
+ @tests = []
8
+ @browsers = []
9
+
10
+ @queue = Queue.new
11
+
12
+ @server = WEBrick::HTTPServer.new(:Port => 4711) # TODO: make port configurable
13
+ @server.mount_proc("/results") do |req, res|
14
+ @queue.push(req)
15
+ res.body = "OK"
16
+ end
17
+ @server.mount("/response", BasicServlet)
18
+ @server.mount("/slow", SlowServlet)
19
+ @server.mount("/down", DownServlet)
20
+ @server.mount("/inspect", InspectionServlet)
21
+ yield self if block_given?
22
+ define
23
+ end
24
+
25
+ def define
26
+ task @name do
27
+ trap("INT") { @server.shutdown; exit }
28
+ t = Thread.new { @server.start }
29
+
30
+ # run all combinations of browsers and tests
31
+ @browsers.each do |browser|
32
+ if browser.supported?
33
+ t0 = Time.now
34
+ test_suite_results = TestSuiteResults.new
35
+
36
+ browser.setup
37
+ puts "\nStarted tests in #{browser}."
38
+
39
+ @tests.each do |test|
40
+ browser.visit(get_url(test))
41
+ results = TestResults.new(@queue.pop.query, test[:url])
42
+ print results
43
+ test_suite_results << results
44
+ end
45
+
46
+ print "\nFinished in #{Time.now - t0} seconds."
47
+ print test_suite_results
48
+ browser.teardown
49
+ else
50
+ puts "\nSkipping #{browser}, not supported on this OS."
51
+ end
52
+ end
53
+
54
+ destroy_temp_directory
55
+ @server.shutdown
56
+ t.join
57
+ end
58
+ end
59
+
60
+ def get_url(test)
61
+ params = "resultsURL=http://localhost:4711/results&t=" + ("%.6f" % Time.now.to_f)
62
+ params << "&tests=#{test[:testcases]}" unless test[:testcases] == :all
63
+ "http://localhost:4711#{test[:url]}?#{params}"
64
+ end
65
+
66
+ def setup(sources_directory, test_cases, browsers)
67
+ @sources_directory = sources_directory
68
+ test_cases = setup_tests(test_cases)
69
+ run_test_cases(test_cases)
70
+ setup_mount_paths
71
+ setup_browsers(browsers)
72
+ end
73
+
74
+ def mount(path, dir = nil)
75
+ dir = current_directory + path unless dir
76
+
77
+ # don't cache anything in our tests
78
+ @server.mount(path, NonCachingFileHandler, dir)
79
+ end
80
+
81
+ # test should be specified as a hash of the form
82
+ # {:url => "url", :testcases => "testFoo,testBar"}.
83
+ # specifying :testcases is optional
84
+ def run(url, testcases = :all)
85
+ @tests << { :url => url, :testcases => testcases }
86
+ end
87
+
88
+ def browser(browser)
89
+ browser =
90
+ case(browser)
91
+ when :firefox
92
+ Firefox.new
93
+ when :safari
94
+ Safari.new
95
+ when :ie
96
+ InternetExplorer.new
97
+ when :konqueror
98
+ Konqueror.new
99
+ when :opera
100
+ Opera.new
101
+ else
102
+ browser
103
+ end
104
+
105
+ @browsers << browser
106
+ end
107
+
108
+ protected
109
+ def setup_tests(test_cases)
110
+ create_temp_directory
111
+ test_cases ||= Dir["#{test_directory}/**/*_test.js"] + Dir["#{test_directory}/**/*_spec.js"]
112
+ test_cases.map do |test_case|
113
+ test_case = TestCase.new(test_case, test_directory)
114
+ unless test_case.exist?
115
+ destroy_temp_directory
116
+ raise "Test case not found: '#{test_case.path}'"
117
+ end
118
+ test_case.create_temp_directory
119
+ write_template test_case
120
+ test_case
121
+ end
122
+ end
123
+
124
+ def setup_mount_paths
125
+ mount "/", assets_directory
126
+ mount "/test", temp_directory
127
+ mount "/javascripts", sources_directory
128
+ end
129
+
130
+ def setup_browsers(browsers)
131
+ BROWSERS.each do |browser|
132
+ browser(browser.to_sym) unless browsers && !browsers.include?(browser)
133
+ end
134
+ end
135
+
136
+ def run_test_cases(test_cases)
137
+ test_cases.each { |test_case| run test_case.url }
138
+ end
139
+
140
+ def test_directory
141
+ @test_directory ||= begin
142
+ directory = File.directory?(File.expand_path(current_directory + "/test")) ? "test" : "spec"
143
+ directory << "/javascript"
144
+ unless File.directory?(directory)
145
+ raise <<-END
146
+ Can't find JavaScript test directory in '#{current_directory}'.
147
+ Please make sure at least one of them exist:
148
+ \t'#{current_directory}/test/javascript'
149
+ \t'#{current_directory}/spec/javascript'\n
150
+ END
151
+ end
152
+ directory
153
+ end
154
+ end
155
+
156
+ def template
157
+ @template ||= begin
158
+ path = File.expand_path(test_directory + "/templates/test_case.erb")
159
+ raise "Can't find the Javascript test template: '#{path}'" unless File.exist?(path)
160
+ ERB.new(File.new(path).read)
161
+ end
162
+ end
163
+
164
+ def write_template(test_case)
165
+ # instance var is needed by ERb binding.
166
+ @test_case = test_case
167
+ template_path = "#{test_case.temp_directory}/#{test_case.name}.html"
168
+ File.open(template_path, 'w') { |f| f.write(template.result(binding)) }
169
+ end
170
+
171
+ def current_directory
172
+ @current_directory ||= Dir.pwd
173
+ end
174
+
175
+ def create_temp_directory
176
+ FileUtils.mkdir_p temp_directory
177
+ end
178
+
179
+ def destroy_temp_directory
180
+ FileUtils.rm_rf(temp_directory) rescue nil
181
+ end
182
+
183
+ def temp_directory
184
+ @temp_directory ||= test_directory + "/tmp"
185
+ end
186
+
187
+ def assets_directory
188
+ @assets_directory ||= File.expand_path(current_directory + "/test/javascript/assets").freeze
189
+ end
190
+ end