ghostbuster 0.0.1
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/.gitignore +5 -0
- data/Gemfile +4 -0
- data/README.md +53 -0
- data/Rakefile +3 -0
- data/bin/ghostbuster +4 -0
- data/bin/setup-ghostbuster +32 -0
- data/ghost/config.ru +14 -0
- data/ghost/start.sh +3 -0
- data/ghost/stop.sh +3 -0
- data/ghost/test_ghost.coffee +37 -0
- data/ghost/test_ghostmore.coffee +8 -0
- data/ghost/views/form.erb +12 -0
- data/ghost/views/index.erb +15 -0
- data/ghostbuster.gemspec +25 -0
- data/lib/ghostbuster.coffee +241 -0
- data/lib/ghostbuster.rb +34 -0
- data/lib/ghostbuster/install_rake.rb +2 -0
- data/lib/ghostbuster/rake.rb +18 -0
- data/lib/ghostbuster/runner.rb +17 -0
- data/lib/ghostbuster/shell.rb +14 -0
- data/lib/ghostbuster/version.rb +3 -0
- data/log/thin.log +24 -0
- metadata +151 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Ghostbuster
|
2
|
+
|
3
|
+
Automated browser testing via phantom.js, with all of the pain taken out!
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
To install first `gem install ghostbuster`. Once you've done that, you can run `setup-ghostbuster`. Right now this only works on Mac, so, otherwise, ghostbuster will look for a copy of the `phantomjs` binary in `~/.ghostbuster`.
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
Once installed, you can simply use `ghostbuster path/to/tests` to run your tests. You should get some output that looks something liek this.
|
12
|
+
|
13
|
+
~~~~
|
14
|
+
|
15
|
+
GhostBuster
|
16
|
+
For /Users/joshbuddy/Development/ghostbuster/ghost/test_ghost.coffee
|
17
|
+
✓ Simple index
|
18
|
+
✓ Form input
|
19
|
+
✓ Link traversal
|
20
|
+
✗ Bad link traversal
|
21
|
+
Assert location failed: Excepted http://127.0.0.1:4567/not-correct, got http://127.0.0.1:4567/
|
22
|
+
✗ Form input not equal
|
23
|
+
Assert first for selector #out did not meet expectations
|
24
|
+
|
25
|
+
For /Users/joshbuddy/Development/ghostbuster/ghost/test_ghostmore.coffee
|
26
|
+
✓ Simple form
|
27
|
+
• Form should do more things
|
28
|
+
|
29
|
+
~~~~
|
30
|
+
|
31
|
+
Your test directory should look something like this:
|
32
|
+
|
33
|
+
ghost_tests/start.sh # Used to start your web application
|
34
|
+
ghost_tests/stop.sh # Used to stop your web application
|
35
|
+
ghost_tests/test_*.coffee # Your tests
|
36
|
+
|
37
|
+
Look inside `ghost` to see some examples of what actual tests would look like. Let's dive into a couple of simple examples.
|
38
|
+
|
39
|
+
~~~~
|
40
|
+
|
41
|
+
phantom.test.root = "http://127.0.0.1:4567" # you must specify your root.
|
42
|
+
|
43
|
+
phantom.test.add "Simple index", -> # this adds a test
|
44
|
+
@get '/', -> # this will get your a path relative to your root
|
45
|
+
@body.assertFirst 'p', (p) -> # this asserts the first paragraph's inner text
|
46
|
+
p.innerHTML == 'This is my paragraph' # is 'This is my paragraph'
|
47
|
+
@body.assertAll 'ul li', (li, idx) ->
|
48
|
+
li.innerHTML == "List item #{idx + 1}"
|
49
|
+
@succeed() # all tests must succeed
|
50
|
+
|
51
|
+
~~~~
|
52
|
+
|
53
|
+
To use this within rake, just put `require 'ghostbuster/install_rake'` in your Rakefile.
|
data/Rakefile
ADDED
data/bin/ghostbuster
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
case RUBY_PLATFORM
|
4
|
+
when /darwin/
|
5
|
+
require 'fileutils'
|
6
|
+
puts "Creating ghostbuster directory in your home"
|
7
|
+
root = File.expand_path(File.join(ENV['HOME'], '.ghostbuster'))
|
8
|
+
cache = File.expand_path(File.join(root, 'cache'))
|
9
|
+
unless File.exist?(File.join(root, 'version')) && File.read(File.join(root, 'version')) == '1'
|
10
|
+
FileUtils.rm_rf(root)
|
11
|
+
FileUtils.mkdir_p(root)
|
12
|
+
FileUtils.mkdir_p(cache)
|
13
|
+
phantom_url = "http://phantomjs.googlecode.com/files/phantomjs-1.2.0-macosx-universal.dmg"
|
14
|
+
unless File.exist?(File.join(cache, File.basename(phantom_url)))
|
15
|
+
Dir.chdir(cache) do
|
16
|
+
puts "Downloading and install phantom.js"
|
17
|
+
system("curl #{phantom_url} -O") or raise("Unable to download from #{phantom_url}")
|
18
|
+
system("open #{File.basename(phantom_url)}") or raise("Unable to open disk image")
|
19
|
+
end
|
20
|
+
system("cp -r /Volumes/phantomjs/phantomjs.app #{root}")
|
21
|
+
File.exist?("#{root}/phantomjs.app") or raise("Unable to copy phantomjs from your disk image")
|
22
|
+
system("ln -s #{root}/phantomjs.app/Contents/MacOS/phantomjs #{root}/phantomjs") or raise("Unable to copy phantomjs from your disk image")
|
23
|
+
`hdiutil eject /Volumes/phantomjs`
|
24
|
+
File.open("#{root}/version", 'w') {|f| f << '1' }
|
25
|
+
else
|
26
|
+
puts "Already installed and at the right version"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
else
|
30
|
+
puts "I don't know how to install for RUBY_PLATFORM #{RUBY_PLATFORM}"
|
31
|
+
exit(1)
|
32
|
+
end
|
data/ghost/config.ru
ADDED
data/ghost/start.sh
ADDED
data/ghost/stop.sh
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
phantom.test.root = "http://127.0.0.1:4567"
|
2
|
+
|
3
|
+
phantom.test.add "Simple index", ->
|
4
|
+
@get '/', ->
|
5
|
+
@body.assertFirst 'p', (p) ->
|
6
|
+
p.innerHTML == 'This is my paragraph'
|
7
|
+
@body.assertAll 'ul li', (li, idx) ->
|
8
|
+
li.innerHTML == "List item #{idx + 1}"
|
9
|
+
@succeed()
|
10
|
+
|
11
|
+
phantom.test.add "Form input", ->
|
12
|
+
@get '/form', ->
|
13
|
+
@body.input "#in", "this is my input"
|
14
|
+
@body.click "#btn"
|
15
|
+
@body.assertFirst '#out', (out) ->
|
16
|
+
out.innerHTML == 'this is my input'
|
17
|
+
@succeed()
|
18
|
+
|
19
|
+
phantom.test.add "Link traversal", ->
|
20
|
+
@get '/', ->
|
21
|
+
@body.click 'a'
|
22
|
+
@body.assertLocation('/form')
|
23
|
+
@succeed()
|
24
|
+
|
25
|
+
phantom.test.add "Bad link traversal", ->
|
26
|
+
@get '/', ->
|
27
|
+
@body.click 'a'
|
28
|
+
@body.assertLocation('/not-correct')
|
29
|
+
@succeed()
|
30
|
+
|
31
|
+
phantom.test.add "Form input not equal", ->
|
32
|
+
@get '/form', ->
|
33
|
+
@body.input "#in", "this is my input"
|
34
|
+
@body.click "#btn"
|
35
|
+
@body.assertFirst '#out', (out) ->
|
36
|
+
out.innerHTML == 'this is NOT my input'
|
37
|
+
@succeed()
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>This is my index</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<form>
|
7
|
+
<input type="text" id="in">
|
8
|
+
<input type="button" id="btn" onclick="document.getElementById('out').innerHTML = document.getElementById('in').value;">
|
9
|
+
</form>
|
10
|
+
<p id="out"></p>
|
11
|
+
</body>
|
12
|
+
</html>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>This is my index</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<h1>First header</h1>
|
7
|
+
<p>This is my paragraph</p>
|
8
|
+
<ul>
|
9
|
+
<li>List item 1</li>
|
10
|
+
<li>List item 2</li>
|
11
|
+
<li>List item 3</li>
|
12
|
+
</ul>
|
13
|
+
<a href="/form">form</a>
|
14
|
+
</body>
|
15
|
+
</html>
|
data/ghostbuster.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "ghostbuster/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ghostbuster"
|
7
|
+
s.version = Ghostbuster::VERSION
|
8
|
+
s.authors = ["Josh Hull"]
|
9
|
+
s.email = ["joshbuddy@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/joshbuddy/ghostbuster"
|
11
|
+
s.summary = %q{Integration testing ftw}
|
12
|
+
s.description = %q{Integration testing ftw.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "ghostbuster"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency 'thin', '~> 1.2.11'
|
22
|
+
s.add_development_dependency 'rake', '~> 0.8.7'
|
23
|
+
s.add_development_dependency 'bundler', '~> 1.0.14'
|
24
|
+
s.add_development_dependency 'sinatra'
|
25
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
class Test
|
2
|
+
constructor: (@runner, @name, @testBody) ->
|
3
|
+
@page = new WebPage()
|
4
|
+
@page.onConsoleMessage = (msg) ->
|
5
|
+
console.log "PAGE CONSOLE: #{msg}"
|
6
|
+
testName = @name
|
7
|
+
@page.onAlert = (msg) =>
|
8
|
+
@runner.lastErrors[testName] = msg
|
9
|
+
@lastError = null
|
10
|
+
@assertions = []
|
11
|
+
@seenCallbacks = []
|
12
|
+
waitForAssertions: (whenDone) ->
|
13
|
+
if @assertions.length == 0
|
14
|
+
whenDone.call(this)
|
15
|
+
else
|
16
|
+
test = this
|
17
|
+
waiting = ->
|
18
|
+
test.waitForAssertions(whenDone)
|
19
|
+
setTimeout waiting, 10
|
20
|
+
run: (@callback) ->
|
21
|
+
@testBody.call(this)
|
22
|
+
get: (path, getCallback) ->
|
23
|
+
@waitForAssertions ->
|
24
|
+
test = this
|
25
|
+
loadedCallback = (status) ->
|
26
|
+
return if test.seenCallbacks.indexOf(getCallback) != -1
|
27
|
+
test.seenCallbacks.push getCallback #traversing links causes this to get re-fired.
|
28
|
+
switch status
|
29
|
+
when 'success'
|
30
|
+
test.body = new Body(test)
|
31
|
+
getCallback.call(test) if getCallback
|
32
|
+
when 'fail'
|
33
|
+
test.fail()
|
34
|
+
@page.open @runner.normalizePath(path), loadedCallback
|
35
|
+
succeed: ->
|
36
|
+
@waitForAssertions ->
|
37
|
+
@callback(true)
|
38
|
+
fail: (msg) ->
|
39
|
+
@callback(false, msg)
|
40
|
+
assert: (valueFetcher) ->
|
41
|
+
@assertions.push(new Assertion(this, valueFetcher))
|
42
|
+
@assertions[0].start() if @assertions.length == 1
|
43
|
+
|
44
|
+
class Assertion
|
45
|
+
constructor: (@test, @fetcher) ->
|
46
|
+
@count = 0
|
47
|
+
start: ->
|
48
|
+
test = @test
|
49
|
+
assertion = this
|
50
|
+
failedCallback = ->
|
51
|
+
assertion.start()
|
52
|
+
if @count == 0
|
53
|
+
fatalCallback = ->
|
54
|
+
test.fail(test.lastError || "This assertion failed to complete.")
|
55
|
+
@fatal = setTimeout(fatalCallback, 1000)
|
56
|
+
@fetcher.call test, (val) ->
|
57
|
+
assertion.count++
|
58
|
+
if val == true
|
59
|
+
delete test.runner.lastErrors[test.name];
|
60
|
+
test.assertions.splice(test.assertions.indexOf(assertion), 1)
|
61
|
+
clearTimeout assertion.fatal
|
62
|
+
if test.assertions.length > 0
|
63
|
+
test.assertions[0].start()
|
64
|
+
else if assertion.count > 10
|
65
|
+
clearTimeout assertion.fatal
|
66
|
+
test.fail(test.lastError)
|
67
|
+
else
|
68
|
+
setTimeout(failedCallback, 75)
|
69
|
+
|
70
|
+
class Body
|
71
|
+
constructor: (@test) ->
|
72
|
+
input: (selector, value) ->
|
73
|
+
eval "
|
74
|
+
var input = function() {
|
75
|
+
var value = '#{value}';
|
76
|
+
var list = document.querySelectorAll('#{selector}');
|
77
|
+
for(var i = 0; i != list.length; i++) {
|
78
|
+
list[i].value = value;
|
79
|
+
}
|
80
|
+
}
|
81
|
+
"
|
82
|
+
@test.page.evaluate(input)
|
83
|
+
|
84
|
+
click: (selector) ->
|
85
|
+
eval "
|
86
|
+
var fn = function() {
|
87
|
+
var targets = document.querySelectorAll('#{selector}'),
|
88
|
+
evt = document.createEvent('MouseEvents'),
|
89
|
+
i, len;
|
90
|
+
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
91
|
+
|
92
|
+
for ( i = 0, len = targets.length; i < len; ++i ) {
|
93
|
+
targets[i].dispatchEvent(evt);
|
94
|
+
}
|
95
|
+
};
|
96
|
+
"
|
97
|
+
@test.page.evaluate(fn)
|
98
|
+
|
99
|
+
assertLocation: (path) ->
|
100
|
+
test = @test
|
101
|
+
location = @test.runner.normalizePath(path)
|
102
|
+
@test.assert (withValue) ->
|
103
|
+
alerter = if test.runner.lastErrors[test.name]? then "" else "alert('Assert location failed: Excepted #{location}, got '+currentLocation);"
|
104
|
+
eval "
|
105
|
+
var fn = function() {
|
106
|
+
var currentLocation = window.location.href;
|
107
|
+
if (window.location.href === '#{location}') {
|
108
|
+
return true;
|
109
|
+
} else {
|
110
|
+
#{alerter}
|
111
|
+
return false;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
"
|
115
|
+
withValue @page.evaluate(fn)
|
116
|
+
|
117
|
+
assertFirst: (selector, assertionCallback) ->
|
118
|
+
test = @test
|
119
|
+
@test.assert (withValue) ->
|
120
|
+
alerter = if test.runner.lastErrors[test.name]? then "" else "alert('Assert first for selector #{selector} did not meet expectations');"
|
121
|
+
eval "
|
122
|
+
var evaluator = function() {
|
123
|
+
try {
|
124
|
+
var assertionCallback = #{assertionCallback.toString()};
|
125
|
+
var ret = assertionCallback(document.querySelector('#{selector}'));
|
126
|
+
if (ret) {
|
127
|
+
return true;
|
128
|
+
} else {
|
129
|
+
#{alerter}
|
130
|
+
return false;
|
131
|
+
}
|
132
|
+
} catch(e) {
|
133
|
+
var err = 'Assert first for selector #{selector} encountered an unexpected error:'+e;
|
134
|
+
console.log(err);
|
135
|
+
alert(err);
|
136
|
+
return false;
|
137
|
+
}
|
138
|
+
};
|
139
|
+
"
|
140
|
+
withValue @page.evaluate(evaluator)
|
141
|
+
|
142
|
+
assertAll: (selector, assertionCallback) ->
|
143
|
+
@test.assert (withValue) ->
|
144
|
+
eval "
|
145
|
+
var evaluator = function() {
|
146
|
+
try {
|
147
|
+
var assertionCallback = #{assertionCallback.toString()};
|
148
|
+
var list = document.querySelectorAll('#{selector}');
|
149
|
+
if (list.length == 0) throw('list is empty');
|
150
|
+
for (var i=0; i != list.length; i++) {
|
151
|
+
if (!assertionCallback(list[i], i)) {
|
152
|
+
alert('Assert all for selector #{selector} on item '+i+' didn\\'t meet expectations');
|
153
|
+
return false;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
return true;
|
157
|
+
} catch(e) {
|
158
|
+
alert('Assert all for selector #{selector} encountered an unexpected error:'+e);
|
159
|
+
return false;
|
160
|
+
}
|
161
|
+
};
|
162
|
+
"
|
163
|
+
withValue @page.evaluate(evaluator)
|
164
|
+
|
165
|
+
class PendingTest
|
166
|
+
constructor: (@runner, @name) ->
|
167
|
+
run: (callback) -> callback('pending')
|
168
|
+
|
169
|
+
class TestFile
|
170
|
+
constructor: (@suite, @name) ->
|
171
|
+
@tests = []
|
172
|
+
@lastErrors = {}
|
173
|
+
normalizePath: (path) -> if path.match(/^http/) then path else "#{@root}#{path}"
|
174
|
+
addPending: (name, body) -> @tests.push new PendingTest(this, name)
|
175
|
+
add: (name, body) -> @tests.push new Test(this, name, body)
|
176
|
+
run: (callback) ->
|
177
|
+
throw "No root is defined" unless @root?
|
178
|
+
count = 0
|
179
|
+
testFile = this
|
180
|
+
testStates = {}
|
181
|
+
nextTest = ->
|
182
|
+
testFile.tests[count].run (state) ->
|
183
|
+
testStates[testFile.tests[count].name] = state
|
184
|
+
count++
|
185
|
+
if count < testFile.tests.length
|
186
|
+
nextTest()
|
187
|
+
else
|
188
|
+
testFile.report(testStates)
|
189
|
+
callback()
|
190
|
+
nextTest()
|
191
|
+
report: (testStates) ->
|
192
|
+
success = 0
|
193
|
+
failure = 0
|
194
|
+
pending = 0
|
195
|
+
console.log "For \033[1m#{@name}\033[0m"
|
196
|
+
for name, state of testStates
|
197
|
+
if state == true
|
198
|
+
success++
|
199
|
+
console.log " \033[32m\u2713\033[0m #{name}"
|
200
|
+
else if state == 'pending'
|
201
|
+
pending++
|
202
|
+
console.log " \033[33m\u2022\033[0m #{name}"
|
203
|
+
else
|
204
|
+
failure++
|
205
|
+
console.log " \033[31m\u2717\033[0m #{name}\n #{@lastErrors[name] || "There was a problem"}"
|
206
|
+
console.log ""
|
207
|
+
@suite.report(success, failure, pending)
|
208
|
+
console.log "GhostBuster"
|
209
|
+
|
210
|
+
class TestSuite
|
211
|
+
constructor: (@args) ->
|
212
|
+
@success = 0
|
213
|
+
@failure = 0
|
214
|
+
@pending = 0
|
215
|
+
report: (success, failure, pending) ->
|
216
|
+
@success += success
|
217
|
+
@failure += failure
|
218
|
+
@pending += pending
|
219
|
+
run: ->
|
220
|
+
count = 0
|
221
|
+
suite = this
|
222
|
+
runNextTest = ->
|
223
|
+
if suite.args.length == count
|
224
|
+
console.log "#{suite.success} success, #{suite.failure} failure, #{suite.pending} pending"
|
225
|
+
phantom.exit (if suite.failure == 0 then 0 else 1)
|
226
|
+
else
|
227
|
+
testFile = suite.args[count]
|
228
|
+
phantom.test = new TestFile(suite, testFile)
|
229
|
+
if phantom.injectJs(testFile)
|
230
|
+
phantom.test.run ->
|
231
|
+
count++
|
232
|
+
runNextTest()
|
233
|
+
else
|
234
|
+
console.log "Unable to load #{testFile}"
|
235
|
+
runNextTest()
|
236
|
+
|
237
|
+
if phantom.args.length == 0
|
238
|
+
console.log("You need to specify a test file")
|
239
|
+
else
|
240
|
+
suite = new TestSuite(phantom.args)
|
241
|
+
suite.run()
|
data/lib/ghostbuster.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'ghostbuster/version'
|
2
|
+
require 'ghostbuster/shell'
|
3
|
+
|
4
|
+
class Ghostbuster
|
5
|
+
include Shell
|
6
|
+
autoload :Rake, 'ghostbuster/rake'
|
7
|
+
autoload :Runner, 'ghostbuster/runner'
|
8
|
+
|
9
|
+
def initialize(path)
|
10
|
+
@path = path
|
11
|
+
@dir = File.directory?(path) ? path : File.dirname(path)
|
12
|
+
@ghost_lib = File.expand_path(File.join(File.dirname(__FILE__), "ghostbuster.coffee"))
|
13
|
+
@phantom_bin = File.join(ENV['HOME'], '.ghostbuster', 'phantomjs')
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
files = Dir[@path].to_a.map{|f| File.expand_path(f)}
|
18
|
+
status = 1
|
19
|
+
Dir.chdir(@dir) do
|
20
|
+
sh "./start.sh"
|
21
|
+
sleep 2
|
22
|
+
begin
|
23
|
+
_, status = Process.waitpid2 fork { exec("#{@phantom_bin} #{@ghost_lib} #{files.join(' ')}") }
|
24
|
+
ensure
|
25
|
+
sh "./stop.sh"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
exit(status)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.run(path)
|
32
|
+
new(path).run
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Ghostbuster
|
2
|
+
module Rake
|
3
|
+
def self.included(o)
|
4
|
+
o.class_eval do
|
5
|
+
include Rake::DSL if defined? Rake::DSL
|
6
|
+
Ghostbuster::Rake.include_rake_tasks
|
7
|
+
end
|
8
|
+
end
|
9
|
+
def self.include_rake_tasks(opts = {})
|
10
|
+
opts[:task_name] ||= :"test:ghostbuster"
|
11
|
+
opts[:file_pattern] ||= "ghost/test_*.{coffee,js}"
|
12
|
+
desc "Run ghostbuster tasks"
|
13
|
+
task opts[:task_name] do
|
14
|
+
Ghostbuster.new(opts[:file_pattern]).run
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Ghostbuster
|
2
|
+
class Runner
|
3
|
+
def initialize(args)
|
4
|
+
@args = args
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
if @args.size != 1
|
9
|
+
puts "ghostbuster <path/to/tests>"
|
10
|
+
puts " Version #{VERSION}"
|
11
|
+
exit(1)
|
12
|
+
else
|
13
|
+
Ghostbuster.new(@args.first).run
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Ghostbuster
|
2
|
+
module Shell
|
3
|
+
def sh(cmd, &block)
|
4
|
+
out, code = sh_with_code(cmd, &block)
|
5
|
+
code == 0 ? out : raise(out.empty? ? "Running `#{cmd}' failed. Run this command directly for more detailed output." : out)
|
6
|
+
end
|
7
|
+
|
8
|
+
def sh_with_code(cmd, &block)
|
9
|
+
outbuf = `#{cmd}`
|
10
|
+
block.call(outbuf) if block && $? == 0
|
11
|
+
[outbuf, $?]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/log/thin.log
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
>> Writing PID to thin.pid
|
2
|
+
>> Exiting!
|
3
|
+
/Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/rack/adapter/loader.rb:35:in `read': No such file or directory - config.ru (Errno::ENOENT)
|
4
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/rack/adapter/loader.rb:35:in `load'
|
5
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/controllers/controller.rb:181:in `load_rackup_config'
|
6
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/controllers/controller.rb:71:in `start'
|
7
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:185:in `send'
|
8
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:185:in `run_command'
|
9
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:151:in `run!'
|
10
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/bin/thin:6
|
11
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/bin/thin:19:in `load'
|
12
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/bin/thin:19
|
13
|
+
>> Writing PID to thin.pid
|
14
|
+
>> Exiting!
|
15
|
+
/Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/rack/adapter/loader.rb:35:in `read': No such file or directory - config.ru (Errno::ENOENT)
|
16
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/rack/adapter/loader.rb:35:in `load'
|
17
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/controllers/controller.rb:181:in `load_rackup_config'
|
18
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/controllers/controller.rb:71:in `start'
|
19
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:185:in `send'
|
20
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:185:in `run_command'
|
21
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/lib/thin/runner.rb:151:in `run!'
|
22
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/gems/thin-1.2.11/bin/thin:6
|
23
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/bin/thin:19:in `load'
|
24
|
+
from /Users/joshbuddy/.rvm/gems/ree-1.8.7-2011.03/bin/thin:19
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ghostbuster
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Josh Hull
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-08-26 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 9
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 2
|
32
|
+
- 11
|
33
|
+
version: 1.2.11
|
34
|
+
requirement: *id001
|
35
|
+
name: thin
|
36
|
+
type: :development
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 49
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
- 8
|
48
|
+
- 7
|
49
|
+
version: 0.8.7
|
50
|
+
requirement: *id002
|
51
|
+
name: rake
|
52
|
+
type: :development
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
prerelease: false
|
55
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 11
|
61
|
+
segments:
|
62
|
+
- 1
|
63
|
+
- 0
|
64
|
+
- 14
|
65
|
+
version: 1.0.14
|
66
|
+
requirement: *id003
|
67
|
+
name: bundler
|
68
|
+
type: :development
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
requirement: *id004
|
81
|
+
name: sinatra
|
82
|
+
type: :development
|
83
|
+
description: Integration testing ftw.
|
84
|
+
email:
|
85
|
+
- joshbuddy@gmail.com
|
86
|
+
executables:
|
87
|
+
- ghostbuster
|
88
|
+
- setup-ghostbuster
|
89
|
+
extensions: []
|
90
|
+
|
91
|
+
extra_rdoc_files: []
|
92
|
+
|
93
|
+
files:
|
94
|
+
- .gitignore
|
95
|
+
- Gemfile
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- bin/ghostbuster
|
99
|
+
- bin/setup-ghostbuster
|
100
|
+
- ghost/config.ru
|
101
|
+
- ghost/start.sh
|
102
|
+
- ghost/stop.sh
|
103
|
+
- ghost/test_ghost.coffee
|
104
|
+
- ghost/test_ghostmore.coffee
|
105
|
+
- ghost/views/form.erb
|
106
|
+
- ghost/views/index.erb
|
107
|
+
- ghostbuster.gemspec
|
108
|
+
- lib/ghostbuster.coffee
|
109
|
+
- lib/ghostbuster.rb
|
110
|
+
- lib/ghostbuster/install_rake.rb
|
111
|
+
- lib/ghostbuster/rake.rb
|
112
|
+
- lib/ghostbuster/runner.rb
|
113
|
+
- lib/ghostbuster/shell.rb
|
114
|
+
- lib/ghostbuster/version.rb
|
115
|
+
- log/thin.log
|
116
|
+
has_rdoc: true
|
117
|
+
homepage: https://github.com/joshbuddy/ghostbuster
|
118
|
+
licenses: []
|
119
|
+
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
|
123
|
+
require_paths:
|
124
|
+
- lib
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
hash: 3
|
131
|
+
segments:
|
132
|
+
- 0
|
133
|
+
version: "0"
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
none: false
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
hash: 3
|
140
|
+
segments:
|
141
|
+
- 0
|
142
|
+
version: "0"
|
143
|
+
requirements: []
|
144
|
+
|
145
|
+
rubyforge_project: ghostbuster
|
146
|
+
rubygems_version: 1.6.2
|
147
|
+
signing_key:
|
148
|
+
specification_version: 3
|
149
|
+
summary: Integration testing ftw
|
150
|
+
test_files: []
|
151
|
+
|