nakajima-screw-driver 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +58 -0
- data/Rakefile +13 -0
- data/bin/screwdriver +8 -0
- data/js/jquery.ajax_queue.js +59 -0
- data/js/screw.driver.js +32 -0
- data/lib/browser.rb +6 -0
- data/lib/browsers/firefox.rb +21 -0
- data/lib/browsers/safari.rb +19 -0
- data/lib/ext/hpricot.rb +7 -0
- data/lib/ext/string.rb +13 -0
- data/lib/helpers.rb +42 -0
- data/lib/rails.rb +25 -0
- data/lib/server.rb +26 -0
- data/lib/suite.rb +182 -0
- metadata +83 -0
data/README.textile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
h1. Screw.Driver
|
2
|
+
|
3
|
+
Another way to run your Screw.Unit tests.
|
4
|
+
|
5
|
+
h3. INSTALL
|
6
|
+
|
7
|
+
To build the gem, run this:
|
8
|
+
|
9
|
+
gemify -I
|
10
|
+
|
11
|
+
"View the CI build":http://ci.patnakajima.com/screw-driver
|
12
|
+
|
13
|
+
h3. ABOUT
|
14
|
+
|
15
|
+
Still really early. To try it, run this:
|
16
|
+
|
17
|
+
./bin/screwdriver spec/fixtures/suite.html
|
18
|
+
|
19
|
+
You can specify a browser like so:
|
20
|
+
|
21
|
+
screwdriver spec/fixtures/suite.html --browser Safari
|
22
|
+
|
23
|
+
By default, Screw.Driver uses Firefox.
|
24
|
+
|
25
|
+
If you don't want the server to automatically quit after
|
26
|
+
your suite runs, you can use the "--server" option:
|
27
|
+
|
28
|
+
screwdriver spec/fixtures/suite.html --server
|
29
|
+
|
30
|
+
Screw.Driver will generate urls for each of the external
|
31
|
+
scripts and CSS links you include in your suite.html file,
|
32
|
+
and will serve them from the directory that contains the
|
33
|
+
suite.html file.
|
34
|
+
|
35
|
+
To specify additional load paths for your JavaScripts, you can
|
36
|
+
use the "--load-paths" option:
|
37
|
+
|
38
|
+
screwdriver spec/fixtures/suite.html --load-paths src/
|
39
|
+
|
40
|
+
h3. Using with Rails
|
41
|
+
|
42
|
+
If you're using Rails, run screwdriver from the root directory
|
43
|
+
of your app, and specify the "--rails" option. This will cause
|
44
|
+
the Screw.Driver server to serve the JavaScript files in your
|
45
|
+
public/javascripts directory. Example:
|
46
|
+
|
47
|
+
screwdriver spec/fixtures/suite.html --rails
|
48
|
+
|
49
|
+
REQUIREMENTS
|
50
|
+
- sinatra
|
51
|
+
- hpricot
|
52
|
+
- gemify (to build)
|
53
|
+
|
54
|
+
TODO
|
55
|
+
- Inject base tag into DOM at Hpricot parsing stage
|
56
|
+
- Convert script src attributes to absolute paths at Hpricot parsing stage
|
57
|
+
- Don't kill all Firefox tabs when complete
|
58
|
+
- Use #load_paths for Rails functionality
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
|
3
|
+
task :default => [:spec]
|
4
|
+
|
5
|
+
desc "Run all specs"
|
6
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
7
|
+
t.spec_files = FileList['spec/**/*.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
task :run do
|
11
|
+
dir = File.dirname(__FILE__)
|
12
|
+
system "#{dir}/bin/screwdriver #{dir}/spec/fixtures/suite.html"
|
13
|
+
end
|
data/bin/screwdriver
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
// AjaxQueue - by Pat Nakajima
|
2
|
+
(function($) {
|
3
|
+
var startNextRequest = function() {
|
4
|
+
if ($.ajaxQueue.currentRequest) { return; }
|
5
|
+
if (request = $.ajaxQueue.queue.shift()) {
|
6
|
+
$.ajaxQueue.currentRequest = request;
|
7
|
+
request.perform();
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
var Request = function(url, type, options) {
|
12
|
+
this.opts = options || { };
|
13
|
+
this.opts.url = url;
|
14
|
+
this.opts.type = type;
|
15
|
+
var oldComplete = this.opts.complete || function() { }
|
16
|
+
this.opts.complete = function(response) {
|
17
|
+
oldComplete(response);
|
18
|
+
$.ajaxQueue.currentRequest = null;
|
19
|
+
startNextRequest();
|
20
|
+
};
|
21
|
+
}
|
22
|
+
|
23
|
+
Request.prototype.perform = function() {
|
24
|
+
$.ajax(this.opts);
|
25
|
+
}
|
26
|
+
|
27
|
+
var addRequest = function(url, type, options) {
|
28
|
+
var request = new Request(url, type, options);
|
29
|
+
$.ajaxQueue.queue.push(request);
|
30
|
+
startNextRequest();
|
31
|
+
}
|
32
|
+
|
33
|
+
$.ajaxQueue = {
|
34
|
+
queue: [],
|
35
|
+
|
36
|
+
currentRequest: null,
|
37
|
+
|
38
|
+
reset: function() {
|
39
|
+
$.ajaxQueue.queue = [];
|
40
|
+
$.ajaxQueue.currentRequest = null;
|
41
|
+
},
|
42
|
+
|
43
|
+
post: function(url, options) {
|
44
|
+
addRequest(url, 'POST', options);
|
45
|
+
},
|
46
|
+
|
47
|
+
get: function(url, options) {
|
48
|
+
addRequest(url, 'GET', options);
|
49
|
+
},
|
50
|
+
|
51
|
+
put: function(url, options) {
|
52
|
+
addRequest(url, 'PUT', options);
|
53
|
+
},
|
54
|
+
|
55
|
+
"delete": function(url, options) {
|
56
|
+
addRequest(url, 'DELETE', options);
|
57
|
+
}
|
58
|
+
}
|
59
|
+
})(jQuery)
|
data/js/screw.driver.js
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
(function($) {
|
2
|
+
$(Screw).bind('before', function() {
|
3
|
+
$.ajaxQueue.post('/before');
|
4
|
+
});
|
5
|
+
|
6
|
+
$(Screw).bind('after', function() {
|
7
|
+
$.ajaxQueue.post('/after');
|
8
|
+
$.ajaxQueue.post('/exit');
|
9
|
+
});
|
10
|
+
|
11
|
+
$(Screw).bind('loaded', function() {
|
12
|
+
$('.it').bind('passed', function(e) {
|
13
|
+
$.ajaxQueue.post('/passed');
|
14
|
+
});
|
15
|
+
|
16
|
+
$('.it').bind('failed', function(e, reason) {
|
17
|
+
var specName = $(this).find('h2').text();
|
18
|
+
|
19
|
+
// ERROR
|
20
|
+
if (reason.fileName || reason.lineNumber || reason.line) {
|
21
|
+
return $.ajaxQueue.post('/errored', {
|
22
|
+
data: { name: specName, reason: reason }
|
23
|
+
});
|
24
|
+
}
|
25
|
+
|
26
|
+
// FAILURE
|
27
|
+
$.ajaxQueue.post('/failed', {
|
28
|
+
data: { name: specName, reason: reason }
|
29
|
+
});
|
30
|
+
});
|
31
|
+
});
|
32
|
+
})(jQuery);
|
data/lib/browser.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Screw
|
2
|
+
module Driver
|
3
|
+
module Browser
|
4
|
+
class Firefox
|
5
|
+
def start
|
6
|
+
Thread.new do
|
7
|
+
sleep 1
|
8
|
+
system "open -a Firefox 'http://localhost:4567/?body%20%3E%20.describe'"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def kill
|
13
|
+
puts " killing Firefox... "
|
14
|
+
browser_pid = `ps aux | grep -v grep | grep Firefox.app`.scan(/\d+/).first
|
15
|
+
system("kill -s SIGTERM #{browser_pid}")
|
16
|
+
puts ' done.'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Screw
|
2
|
+
module Driver
|
3
|
+
module Browser
|
4
|
+
class Safari
|
5
|
+
def start
|
6
|
+
Thread.new { sleep 1; system "open -n -a Safari 'http://localhost:4567/?body%20%3E%20.describe'" }
|
7
|
+
# t.join
|
8
|
+
end
|
9
|
+
|
10
|
+
def kill
|
11
|
+
puts " killing Safari... "
|
12
|
+
browser_pid = `ps aux | grep -v grep | grep Safari.app`.scan(/\d+/).first
|
13
|
+
system("kill -s SIGTERM #{browser_pid}")
|
14
|
+
puts ' done.'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/ext/hpricot.rb
ADDED
data/lib/ext/string.rb
ADDED
data/lib/helpers.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
helpers do
|
2
|
+
def padded(spaces=1)
|
3
|
+
spaces.times { puts "" }
|
4
|
+
yield
|
5
|
+
spaces.times { puts "" }
|
6
|
+
:ok
|
7
|
+
end
|
8
|
+
|
9
|
+
def report(str, params=nil)
|
10
|
+
params ? SUITE.failed!(params) : SUITE.passed!
|
11
|
+
SUITE.write_dot!(str)
|
12
|
+
sleep 0.1
|
13
|
+
:ok
|
14
|
+
end
|
15
|
+
|
16
|
+
def before_suite
|
17
|
+
SUITE.reset!
|
18
|
+
padded do
|
19
|
+
puts "Staring to run test suite..."
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def after_suite
|
24
|
+
padded do
|
25
|
+
SUITE.failures.each do |failure|
|
26
|
+
padded do
|
27
|
+
puts "Failure:".red
|
28
|
+
puts "- #{failure[:name]}: #{failure[:reason]}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
print "Finished. #{SUITE.test_count} tests. "
|
32
|
+
print "#{SUITE.failures.length} failures." unless SUITE.failures.empty?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def exit_suite
|
37
|
+
padded do
|
38
|
+
puts "== Spec Server Exiting..."
|
39
|
+
SUITE.exit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/rails.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Screw
|
2
|
+
module Driver
|
3
|
+
module Rails
|
4
|
+
def rails?
|
5
|
+
@rails
|
6
|
+
end
|
7
|
+
|
8
|
+
def rails_urls
|
9
|
+
@rails_urls ||= Dir[File.join(public_path, "**", "*.js")]
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate_rails_urls
|
13
|
+
rails_urls.each do |url|
|
14
|
+
generate "/javascripts/#{File.basename(url)}", 'text/javascript', public_path
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def public_path
|
21
|
+
@public_path ||= File.join(Dir.pwd, 'public', 'javascripts')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/server.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sinatra'
|
3
|
+
require 'ext/string.rb'
|
4
|
+
require 'ext/hpricot.rb'
|
5
|
+
require 'browser.rb'
|
6
|
+
require 'browsers/firefox.rb'
|
7
|
+
require 'browsers/safari.rb'
|
8
|
+
require 'rails.rb'
|
9
|
+
require 'suite.rb'
|
10
|
+
require 'helpers.rb'
|
11
|
+
|
12
|
+
configure do
|
13
|
+
Rack::CommonLogger.class_eval { def <<(str) end } # Inhibit logging retardase
|
14
|
+
SUITE = Screw::Driver::Suite.new(self, ARGS)
|
15
|
+
SUITE.browser.start
|
16
|
+
end
|
17
|
+
|
18
|
+
SUITE.generate_urls
|
19
|
+
|
20
|
+
get('/') { SUITE.to_s }
|
21
|
+
post('/passed') { report '.'.green }
|
22
|
+
post('/failed') { report 'F'.red, params }
|
23
|
+
post('/errored') { report 'E'.magenta, params }
|
24
|
+
post('/before') { before_suite }
|
25
|
+
post('/after') { after_suite }
|
26
|
+
post('/exit') { exit_suite unless SUITE.server? }
|
data/lib/suite.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
require 'optparse'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
module Screw
|
6
|
+
module Driver
|
7
|
+
class Suite
|
8
|
+
include Rails
|
9
|
+
|
10
|
+
attr_reader :failures, :test_count, :path, :load_paths, :context
|
11
|
+
|
12
|
+
def initialize(context, args)
|
13
|
+
@context = context
|
14
|
+
parse_args(args)
|
15
|
+
reset!
|
16
|
+
end
|
17
|
+
|
18
|
+
def passed!
|
19
|
+
@test_count += 1
|
20
|
+
end
|
21
|
+
|
22
|
+
def failed!(result)
|
23
|
+
@test_count += 1
|
24
|
+
@failures << result
|
25
|
+
end
|
26
|
+
|
27
|
+
def reset!
|
28
|
+
@test_count = 0
|
29
|
+
@failures = []
|
30
|
+
end
|
31
|
+
|
32
|
+
def working_directory
|
33
|
+
File.dirname(@path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def generate_urls
|
37
|
+
generate_js_urls
|
38
|
+
generate_css_urls
|
39
|
+
generate_rails_urls if rails?
|
40
|
+
generate_fixture_urls if fixtures?
|
41
|
+
end
|
42
|
+
|
43
|
+
def script_urls
|
44
|
+
doc.search('script').map { |script| script['src'] }.compact
|
45
|
+
end
|
46
|
+
|
47
|
+
def link_urls
|
48
|
+
doc.search('link').map { |script| script['href'] }.compact
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s
|
52
|
+
doc.to_html
|
53
|
+
end
|
54
|
+
|
55
|
+
def browser
|
56
|
+
@browser
|
57
|
+
end
|
58
|
+
|
59
|
+
def server?
|
60
|
+
@server
|
61
|
+
end
|
62
|
+
|
63
|
+
def fixtures?
|
64
|
+
File.exists?(File.join(working_directory, 'fixtures'))
|
65
|
+
end
|
66
|
+
|
67
|
+
def exit
|
68
|
+
browser.kill
|
69
|
+
exit! failures.empty? ? 0 : 1
|
70
|
+
end
|
71
|
+
|
72
|
+
def write_dot!(str)
|
73
|
+
$stdout.print(str)
|
74
|
+
$stdout.flush
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def generate_fixture_urls
|
80
|
+
Dir[File.join(working_directory, "fixtures", "*.html")].each do |fixture|
|
81
|
+
generate(File.join("fixtures", File.basename(fixture)), "text/html")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def generate_js_urls
|
86
|
+
script_urls.each do |url|
|
87
|
+
generate(url, "text/javascript")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def generate_css_urls
|
92
|
+
link_urls.each do |url|
|
93
|
+
generate(url, "text/css")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# i know. this is hideous.
|
98
|
+
def absolutize_url(url)
|
99
|
+
# file_path = File.expand_path(File.join(working_directory, url))
|
100
|
+
# full_path = File.expand_path(File.join(path, url))
|
101
|
+
# full_path.gsub(File.expand_path(path), '')
|
102
|
+
('/' + url.split('./').last).gsub(%r(/+), '/')
|
103
|
+
end
|
104
|
+
|
105
|
+
def generate(url, content_type, default_path=working_directory)
|
106
|
+
prefix = load_paths.detect { |load_path| File.exists?(File.join(load_path, url)) } || default_path
|
107
|
+
path = File.join(prefix, url)
|
108
|
+
@context.send(:get, absolutize_url(url)) do
|
109
|
+
headers 'Content-Type' => content_type
|
110
|
+
File.read(path)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def parse_args(args)
|
115
|
+
options = OpenStruct.new
|
116
|
+
options.browser = "Firefox"
|
117
|
+
options.rails = false
|
118
|
+
options.server = false
|
119
|
+
options.paths = []
|
120
|
+
|
121
|
+
opts = OptionParser.new do |opts|
|
122
|
+
opts.banner = "Usage: screwdriver [options] suite"
|
123
|
+
|
124
|
+
opts.on("-b", "--browser BROWSER", "Specify the browser to use (default: Firefox)") do |browser|
|
125
|
+
options.browser = browser
|
126
|
+
end
|
127
|
+
|
128
|
+
opts.on("-p", "--load-paths src,dist,etc", Array, "Adds additional load paths from which files can be served.") do |paths|
|
129
|
+
options.paths = paths
|
130
|
+
end
|
131
|
+
|
132
|
+
opts.on("--rails", "Enable Rails integration (serves files from the RAILS_ROOT/public/javascripts directory)") do
|
133
|
+
options.rails = true
|
134
|
+
end
|
135
|
+
|
136
|
+
opts.on("-s", "--server", "Keeps the server running after completion") do
|
137
|
+
options.server = true
|
138
|
+
end
|
139
|
+
|
140
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
141
|
+
puts opts
|
142
|
+
exit!
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
opts.parse!(args)
|
147
|
+
|
148
|
+
if args.empty?
|
149
|
+
puts opts
|
150
|
+
exit!
|
151
|
+
else
|
152
|
+
@browser = eval("Screw::Driver::Browser::#{options.browser}.new")
|
153
|
+
@rails = options.rails
|
154
|
+
@server = options.server
|
155
|
+
@path = File.join(Dir.pwd, args.shift)
|
156
|
+
setup_load_paths options.paths
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def setup_load_paths(paths=[])
|
161
|
+
@load_paths = paths.map { |path| File.join(Dir.pwd, path) }
|
162
|
+
@load_paths << File.join(File.dirname(__FILE__), '..', 'js')
|
163
|
+
@load_paths << File.dirname(@path)
|
164
|
+
end
|
165
|
+
|
166
|
+
def doc
|
167
|
+
@doc ||= extended_doc(File.open(@path))
|
168
|
+
end
|
169
|
+
|
170
|
+
def extended_doc(file)
|
171
|
+
hpricot_doc = Hpricot(file)
|
172
|
+
hpricot_doc.search('script').last.after(%(<base href="http://localhost:4567">))
|
173
|
+
hpricot_doc.search('script').each do |node|
|
174
|
+
case File.basename(node['src'])
|
175
|
+
when "screw.behaviors.js" then node.insert_js(:before, 'jquery.ajax_queue.js')
|
176
|
+
when "screw.events.js" then node.insert_js(:after, 'screw.driver.js')
|
177
|
+
end
|
178
|
+
end and hpricot_doc
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nakajima-screw-driver
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pat Nakajima
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-10-23 00:00:00 -07:00
|
13
|
+
default_executable: screwdriver
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: sinatra
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: hpricot
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: "0"
|
32
|
+
version:
|
33
|
+
description:
|
34
|
+
email: patnakajima@gmail.com
|
35
|
+
executables:
|
36
|
+
- screwdriver
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files: []
|
40
|
+
|
41
|
+
files:
|
42
|
+
- README.textile
|
43
|
+
- Rakefile
|
44
|
+
- bin/screwdriver
|
45
|
+
- lib/browser.rb
|
46
|
+
- lib/browsers/firefox.rb
|
47
|
+
- lib/browsers/safari.rb
|
48
|
+
- lib/ext/string.rb
|
49
|
+
- lib/ext/hpricot.rb
|
50
|
+
- lib/helpers.rb
|
51
|
+
- lib/rails.rb
|
52
|
+
- lib/suite.rb
|
53
|
+
- lib/server.rb
|
54
|
+
- js/jquery.ajax_queue.js
|
55
|
+
- js/screw.driver.js
|
56
|
+
has_rdoc: false
|
57
|
+
homepage: http://github.com/nakajima/screw-driver
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.2.0
|
79
|
+
signing_key:
|
80
|
+
specification_version: 2
|
81
|
+
summary: Run your Screw.Unit specs from the command line.
|
82
|
+
test_files: []
|
83
|
+
|