guard-jasmine 1.19.2 → 2.0.0beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +31 -234
- data/lib/generators/guard_jasmine/install_generator.rb +17 -0
- data/lib/generators/guard_jasmine/templates/Guardfile +9 -0
- data/lib/guard/jasmine.rb +18 -15
- data/lib/guard/jasmine/cli.rb +5 -5
- data/lib/guard/jasmine/formatter.rb +10 -0
- data/lib/guard/jasmine/inspector.rb +1 -2
- data/lib/guard/jasmine/phantomjs/guard-jasmine.js +54 -180
- data/lib/guard/jasmine/phantomjs/guard-reporter.js +187 -0
- data/lib/guard/jasmine/phantomjs/src/guard-jasmine.coffee +101 -0
- data/lib/guard/jasmine/phantomjs/src/guard-reporter.coffee +109 -0
- data/lib/guard/jasmine/phantomjs/test/guard-reporter_spec.coffee +41 -0
- data/lib/guard/jasmine/runner.rb +178 -268
- data/lib/guard/jasmine/server.rb +17 -3
- data/lib/guard/jasmine/util.rb +1 -7
- data/lib/guard/jasmine/version.rb +1 -1
- metadata +135 -26
- data/lib/guard/jasmine/phantomjs/guard-jasmine.coffee +0 -193
- data/lib/guard/jasmine/phantomjs/lib/console.js +0 -188
- data/lib/guard/jasmine/phantomjs/lib/junit_reporter.js +0 -224
- data/lib/guard/jasmine/phantomjs/lib/reporter.js +0 -144
- data/lib/guard/jasmine/phantomjs/lib/result.js +0 -155
- data/lib/guard/jasmine/phantomjs/src/console.coffee +0 -149
- data/lib/guard/jasmine/phantomjs/src/reporter.coffee +0 -139
- data/lib/guard/jasmine/phantomjs/src/result.coffee +0 -95
- data/lib/guard/jasmine/phantomjs/test/console_spec.coffee +0 -125
- data/lib/guard/jasmine/phantomjs/test/reporter_spec.coffee +0 -0
- data/lib/guard/jasmine/phantomjs/test/result_spec.coffee +0 -311
@@ -0,0 +1,101 @@
|
|
1
|
+
|
2
|
+
# Set default values
|
3
|
+
options =
|
4
|
+
url: phantom.args[0] || 'http://127.0.0.1:3000/jasmine'
|
5
|
+
timeout: parseInt(phantom.args[1] || 10000)
|
6
|
+
|
7
|
+
# Create the web page.
|
8
|
+
page = require('webpage').create()
|
9
|
+
|
10
|
+
# Catch JavaScript errors
|
11
|
+
# abort the request and return the error
|
12
|
+
page.onError = (message, trace) ->
|
13
|
+
reportError "Javascript error encountered on Jasmine test page: #{ message }", trace
|
14
|
+
|
15
|
+
# Once the page is initialized, setup the script for
|
16
|
+
# the GuardReporter class
|
17
|
+
page.onInitialized = ->
|
18
|
+
page.injectJs 'guard-reporter.js'
|
19
|
+
page.evaluate ->
|
20
|
+
window.onload = ->
|
21
|
+
window.reporter = new GuardReporter()
|
22
|
+
window.jasmine.getEnv().addReporter(window.reporter) if window.jasmine
|
23
|
+
|
24
|
+
# Once the page is finished loading
|
25
|
+
page.onLoadFinished = (status)->
|
26
|
+
if status isnt 'success'
|
27
|
+
reportError "Unable to access Jasmine specs at #{ options.url }, page returned status: #{status}"
|
28
|
+
else
|
29
|
+
waitFor reporterReady, jasmineAvailable, options.timeout, reporterMissing
|
30
|
+
|
31
|
+
# Open web page, which will kick off the Jasmine test runner
|
32
|
+
page.open options.url
|
33
|
+
|
34
|
+
# Test if Jasmine and guard has been loaded
|
35
|
+
reporterReady = ->
|
36
|
+
page.evaluate ->
|
37
|
+
window.jasmine && window.reporter
|
38
|
+
|
39
|
+
# Start specs after they are have been loaded
|
40
|
+
jasmineAvailable = ->
|
41
|
+
waitFor specsDone, exitSuccessfully, options.timeout, specsTimedout
|
42
|
+
|
43
|
+
# Error message for when jasmine never loaded asynchronously
|
44
|
+
reporterMissing = ->
|
45
|
+
text = page.evaluate -> document.getElementsByTagName('body')[0]?.innerText
|
46
|
+
reportError """
|
47
|
+
The reporter is not available!
|
48
|
+
Perhaps the url ( #{ options.url } ) is incorrect?
|
49
|
+
|
50
|
+
#{ text }
|
51
|
+
"""
|
52
|
+
|
53
|
+
# tests if the resultComplete flag is set on the reporter
|
54
|
+
specsDone = ->
|
55
|
+
result = page.evaluate ->
|
56
|
+
window.reporter.resultComplete
|
57
|
+
|
58
|
+
# We should end up here. Logs the results as JSON and exits
|
59
|
+
exitSuccessfully = ->
|
60
|
+
results = page.evaluate -> window.reporter.results()
|
61
|
+
console.log JSON.stringify( results )
|
62
|
+
phantom.exit()
|
63
|
+
|
64
|
+
|
65
|
+
# Error message for when specs time out
|
66
|
+
specsTimedout = ->
|
67
|
+
text = page.evaluate -> document.getElementsByTagName('body')[0]?.innerText
|
68
|
+
reportError """
|
69
|
+
Timeout waiting for the Jasmine test results!
|
70
|
+
|
71
|
+
#{ text }
|
72
|
+
"""
|
73
|
+
|
74
|
+
# Wait until the test condition is true or a timeout occurs.
|
75
|
+
#
|
76
|
+
# @param [Function] test the test that returns true if condition is met
|
77
|
+
# @param [Function] ready the action when the condition is fulfilled
|
78
|
+
# @param [Number] timeout the max amount of time to wait in milliseconds
|
79
|
+
#
|
80
|
+
waitFor = (test, ready, timeout = 10000, timeoutFunction)->
|
81
|
+
condition = false
|
82
|
+
interval = undefined
|
83
|
+
start = Date.now(0)
|
84
|
+
wait = ->
|
85
|
+
if !condition && (Date.now() - start < timeout)
|
86
|
+
condition = test()
|
87
|
+
else
|
88
|
+
clearInterval interval
|
89
|
+
if condition
|
90
|
+
ready()
|
91
|
+
else
|
92
|
+
timeoutFunction()
|
93
|
+
interval = setInterval( wait, 250 )
|
94
|
+
|
95
|
+
# Logs the error to the console as JSON and exits with status '1'
|
96
|
+
reportError = (msg, trace=[])->
|
97
|
+
if 0 == trace.length
|
98
|
+
err = new Error();
|
99
|
+
trace = err.stack
|
100
|
+
console.log JSON.stringify({ error: msg, trace: trace })
|
101
|
+
phantom.exit(1)
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# Capture statements that were logged to the console
|
2
|
+
# during spec execution.
|
3
|
+
#
|
4
|
+
# To do so it substitues it's own functions for the console.<levels>
|
5
|
+
#
|
6
|
+
extendObject = (a, b)->
|
7
|
+
for key,value of b
|
8
|
+
a[key] = value if b.hasOwnProperty(key)
|
9
|
+
return a
|
10
|
+
|
11
|
+
|
12
|
+
class ConsoleCapture
|
13
|
+
# Instead of attempting to de-activate the console dot reporter in hacky ways,
|
14
|
+
# just ignore it's output
|
15
|
+
@DOT_REPORTER_MATCH = /\[\d+m[F.]..0m/
|
16
|
+
@levels: ['log','info','warn','error','debug' ]
|
17
|
+
@original = console
|
18
|
+
|
19
|
+
@original_levels = {}
|
20
|
+
@original_levels[level] = console[level] for level in ConsoleCapture.levels
|
21
|
+
|
22
|
+
constructor:->
|
23
|
+
@original = {}
|
24
|
+
@captured = []
|
25
|
+
this._reassign_level( level ) for level in ConsoleCapture.levels
|
26
|
+
|
27
|
+
revert: ->
|
28
|
+
for level in ConsoleCapture.levels
|
29
|
+
ConsoleCapture.original[level] = ConsoleCapture.original_levels[level]
|
30
|
+
|
31
|
+
_reassign_level: ( level )->
|
32
|
+
my = this
|
33
|
+
console[level] = ->
|
34
|
+
args = Array.prototype.slice.call(arguments, 0)
|
35
|
+
return if args[0] && args[0].toString && args[0].toString().match( ConsoleCapture.DOT_REPORTER_MATCH )
|
36
|
+
my.captured.push( [ level ].concat( args ) )
|
37
|
+
ConsoleCapture.original_levels[ level ].apply( ConsoleCapture.original, arguments )
|
38
|
+
|
39
|
+
|
40
|
+
# Implements a Jasmine reporter
|
41
|
+
class GuardReporter
|
42
|
+
@STACK_MATCHER=new RegExp("__spec__\/(.*):([0-9]+)","g")
|
43
|
+
|
44
|
+
jasmineStarted: ->
|
45
|
+
@console = new ConsoleCapture();
|
46
|
+
@startedAt = Date.now()
|
47
|
+
@currentSuite = { suites: [] }
|
48
|
+
@stack = [ @currentSuite ]
|
49
|
+
|
50
|
+
suiteStarted: (suite)->
|
51
|
+
suite = extendObject({ specs: [], suites: [] }, suite )
|
52
|
+
@currentSuite.suites.push( suite )
|
53
|
+
@currentSuite = suite
|
54
|
+
@stack.push(suite)
|
55
|
+
|
56
|
+
suiteDone: (Suite)->
|
57
|
+
@stack.pop()
|
58
|
+
@currentSuite = @stack[@stack.length-1]
|
59
|
+
|
60
|
+
jasmineDone: ->
|
61
|
+
@resultComplete = true
|
62
|
+
|
63
|
+
specDone: (spec)->
|
64
|
+
@resultReceived = true
|
65
|
+
spec = extendObject({ logs: @console.captured, errors: [] }, spec )
|
66
|
+
for failure in spec.failedExpectations
|
67
|
+
error = extendObject({trace:[]}, failure )
|
68
|
+
while match = GuardReporter.STACK_MATCHER.exec( failure.stack )
|
69
|
+
error.trace.push({ file: match[1], line: parseInt(match[2]) })
|
70
|
+
delete error.stack
|
71
|
+
spec.errors.push( error )
|
72
|
+
delete spec.failedExpectations
|
73
|
+
@currentSuite.specs.push( spec )
|
74
|
+
|
75
|
+
this.resetConsoleLog()
|
76
|
+
spec
|
77
|
+
|
78
|
+
resetConsoleLog: ->
|
79
|
+
@console.revert()
|
80
|
+
@console = new ConsoleCapture
|
81
|
+
|
82
|
+
eachSuite: (suite)->
|
83
|
+
suites = [].concat( suite.suites )
|
84
|
+
for suite in suite.suites
|
85
|
+
suites = suites.concat( this.eachSuite(suite) )
|
86
|
+
suites
|
87
|
+
|
88
|
+
results: ->
|
89
|
+
stats = {
|
90
|
+
time : ( Date.now() - @startedAt ) / 1000
|
91
|
+
specs : 0
|
92
|
+
failed : 0
|
93
|
+
pending : 0
|
94
|
+
disabled : 0
|
95
|
+
}
|
96
|
+
for suite in this.eachSuite(@stack[0])
|
97
|
+
stats.specs += suite.specs.length
|
98
|
+
for spec in suite.specs
|
99
|
+
stats[spec.status] += 1 unless undefined == stats[spec.status]
|
100
|
+
{
|
101
|
+
jasmine_version: jasmine?.version
|
102
|
+
stats: stats
|
103
|
+
suites: @stack[0].suites
|
104
|
+
}
|
105
|
+
|
106
|
+
if typeof module isnt 'undefined' and module.exports
|
107
|
+
module.exports = GuardReporter
|
108
|
+
else
|
109
|
+
window.GuardReporter = GuardReporter
|
@@ -0,0 +1,41 @@
|
|
1
|
+
sinon = require 'sinon'
|
2
|
+
{expect} = require 'chai'
|
3
|
+
|
4
|
+
GuardReporter = require '../src/guard-reporter'
|
5
|
+
|
6
|
+
describe 'Reporter', ->
|
7
|
+
beforeEach ->
|
8
|
+
@reporter = new GuardReporter
|
9
|
+
@reporter.jasmineStarted()
|
10
|
+
|
11
|
+
it 'captures the console', ->
|
12
|
+
@reporter.suiteStarted({name:'Blank'})
|
13
|
+
console.log("A %s Logging", "Test")
|
14
|
+
console.warn("This is your last warning")
|
15
|
+
@reporter.specDone( { failedExpectations: [] } )
|
16
|
+
expect( @reporter.results() )
|
17
|
+
.to.have.deep.property('.suites[0].specs[0].logs')
|
18
|
+
.and.equal([
|
19
|
+
[ 'log', 'A %s Logging', "Test" ],
|
20
|
+
[ 'warn', 'This is your last warning' ]
|
21
|
+
])
|
22
|
+
|
23
|
+
it "reports counts", ->
|
24
|
+
@reporter.suiteStarted({name:'Blank'})
|
25
|
+
console.log("A %s Logging", "Test")
|
26
|
+
console.warn("This is your last warning")
|
27
|
+
@reporter.specDone( { status: 'passed', failedExpectations: [] } )
|
28
|
+
@reporter.specDone( { status: 'failed', failedExpectations: [{
|
29
|
+
matcherName:"toEqual",
|
30
|
+
message: "Expected 2 to equal 5"
|
31
|
+
}] })
|
32
|
+
@reporter.specDone( { status: 'passed', failedExpectations: [] } )
|
33
|
+
@reporter.specDone( { status: 'pending', failedExpectations: [] } )
|
34
|
+
results = @reporter.results()
|
35
|
+
expect( results ).to.have.property('stats')
|
36
|
+
expect( results.stats )
|
37
|
+
.to.have.property('specs').and.equal(4)
|
38
|
+
expect( results.stats )
|
39
|
+
.to.have.property('failed').and.equal(1)
|
40
|
+
expect( results.stats )
|
41
|
+
.to.have.property('pending').and.equal(1)
|
data/lib/guard/jasmine/runner.rb
CHANGED
@@ -11,53 +11,68 @@ module Guard
|
|
11
11
|
# evaluates the JSON response from the PhantomJS Script `guard_jasmine.coffee`,
|
12
12
|
# writes the result to the console and triggers optional system notifications.
|
13
13
|
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
14
|
+
class Runner
|
15
|
+
include ::Guard::Jasmine::Util
|
16
|
+
|
17
|
+
attr_reader :options
|
18
|
+
|
19
|
+
# Name of the coverage threshold options
|
20
|
+
THRESHOLDS = [:statements_threshold, :functions_threshold, :branches_threshold, :lines_threshold]
|
21
|
+
|
22
|
+
# Run the supplied specs.
|
23
|
+
#
|
24
|
+
# @param [Hash] options the options for the execution
|
25
|
+
# @option options [String] :jasmine_url the url of the Jasmine test runner
|
26
|
+
# @option options [String] :phantomjs_bin the location of the PhantomJS binary
|
27
|
+
# @option options [Integer] :timeout the maximum time in seconds to wait for the spec runner to finish
|
28
|
+
# @option options [String] :rackup_config custom rackup config to use
|
29
|
+
# @option options [Boolean] :notification show notifications
|
30
|
+
# @option options [Boolean] :hide_success hide success message notification
|
31
|
+
# @option options [Integer] :max_error_notify maximum error notifications to show
|
32
|
+
# @option options [Symbol] :specdoc options for the specdoc output, either :always, :never
|
33
|
+
# @option options [Symbol] :console options for the console.log output, either :always, :never or :failure
|
34
|
+
# @option options [String] :spec_dir the directory with the Jasmine specs
|
35
|
+
# @option options [Hash] :query_params Parameters to pass along with the request
|
36
|
+
# @option options [Boolean] :debug display raw JSON output from the runner
|
37
|
+
#
|
38
|
+
def initialize(options)
|
39
|
+
@options = options
|
40
|
+
end
|
40
41
|
|
41
|
-
|
42
|
+
# @param [Array<String>] paths the spec files or directories
|
43
|
+
# @return [Hash<String,Array>] keys for the spec_file, value is array of failure messages.
|
44
|
+
# Only specs with failures will be returned. Therefore an empty? return hash indicates success.
|
45
|
+
def run(paths, per_run_options = {})
|
46
|
+
previous_options = @options
|
47
|
+
@options.merge!( per_run_options )
|
42
48
|
|
43
|
-
|
44
|
-
results << evaluate_response(run_jasmine_spec(file, options), file, options) if File.exist?(file_and_line_number_parts(file)[0])
|
49
|
+
return {} if paths.empty?
|
45
50
|
|
46
|
-
|
47
|
-
end.compact
|
51
|
+
notify_start_message(paths)
|
48
52
|
|
49
|
-
|
53
|
+
run_results = paths.each_with_object({}) do |file, results|
|
54
|
+
if File.exist?(file_and_line_number_parts(file)[0])
|
55
|
+
results[file] = evaluate_response(run_jasmine_spec(file), file)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
# return the errors
|
59
|
+
return run_results.each_with_object({}) do | spec_run, hash |
|
60
|
+
file, r = spec_run
|
61
|
+
errors = collect_spec_errors(r['suites']||[])
|
62
|
+
errors.push( r['error'] ) if r.has_key? 'error'
|
63
|
+
hash[file] = errors unless errors.empty?
|
50
64
|
end
|
65
|
+
ensure
|
66
|
+
@options=previous_options
|
67
|
+
end
|
51
68
|
|
52
|
-
|
69
|
+
private
|
53
70
|
|
54
|
-
|
55
|
-
|
71
|
+
# Shows a notification in the console that the runner starts.
|
72
|
+
#
|
56
73
|
# @param [Array<String>] paths the spec files or directories
|
57
|
-
# @param [Hash] options the options for the execution
|
58
|
-
# @option options [String] :spec_dir the directory with the Jasmine specs
|
59
74
|
#
|
60
|
-
def notify_start_message(paths
|
75
|
+
def notify_start_message(paths)
|
61
76
|
message = if paths == [options[:spec_dir]]
|
62
77
|
'Run all Jasmine suites'
|
63
78
|
else
|
@@ -67,33 +82,12 @@ module Guard
|
|
67
82
|
Formatter.info(message, reset: true)
|
68
83
|
end
|
69
84
|
|
70
|
-
# Returns the failed spec file names.
|
71
|
-
#
|
72
|
-
# @param [Array<Object>] results the spec runner results
|
73
|
-
# @return [Array<String>] the list of failed spec files
|
74
|
-
#
|
75
|
-
def failed_paths_from(results)
|
76
|
-
results.map { |r| !r['passed'] ? r['file'] : nil }.compact
|
77
|
-
end
|
78
|
-
|
79
|
-
# Returns the response status for the given result set.
|
80
|
-
#
|
81
|
-
# @param [Array<Object>] results the spec runner results
|
82
|
-
# @return [Boolean] whether it has passed or not
|
83
|
-
#
|
84
|
-
def response_status_for(results)
|
85
|
-
results.none? { |r| r.has_key?('error') || !r['passed'] }
|
86
|
-
end
|
87
|
-
|
88
85
|
# Run the Jasmine spec by executing the PhantomJS script.
|
89
86
|
#
|
90
87
|
# @param [String] file the path of the spec
|
91
|
-
# @param [Hash] options the options for the execution
|
92
|
-
# @option options [Integer] :timeout the maximum time in seconds to wait for the spec runner to finish
|
93
88
|
#
|
94
|
-
def run_jasmine_spec(file
|
95
|
-
suite = jasmine_suite(file
|
96
|
-
Formatter.info("Run Jasmine suite at #{ suite }")
|
89
|
+
def run_jasmine_spec(file)
|
90
|
+
suite = jasmine_suite(file)
|
97
91
|
|
98
92
|
arguments = [
|
99
93
|
options[:timeout] * 1000,
|
@@ -105,30 +99,27 @@ module Guard
|
|
105
99
|
options[:junit_consolidate],
|
106
100
|
"'#{ options[:junit_save_path] }'"
|
107
101
|
]
|
108
|
-
|
109
|
-
IO.popen(
|
102
|
+
cmd = "#{ phantomjs_command } \"#{ suite }\" #{ arguments.collect { |i| i.to_s }.join(' ')}"
|
103
|
+
IO.popen(cmd, 'r:UTF-8')
|
110
104
|
end
|
111
105
|
|
112
106
|
# Get the PhantomJS binary and script to execute.
|
113
107
|
#
|
114
|
-
# @param [Hash] options the options for the execution
|
115
|
-
# @option options [String] :phantomjs_bin the location of the PhantomJS binary
|
116
108
|
# @return [String] the command
|
117
109
|
#
|
118
|
-
def phantomjs_command
|
110
|
+
def phantomjs_command
|
119
111
|
options[:phantomjs_bin] + ' ' + phantomjs_script
|
112
|
+
#options[:phantomjs_bin] + ' --remote-debugger-port=9000 ' + phantomjs_script
|
120
113
|
end
|
121
114
|
|
122
115
|
# Get the Jasmine test runner URL with the appended suite name
|
123
116
|
# that acts as the spec filter.
|
124
117
|
#
|
125
118
|
# @param [String] file the spec file
|
126
|
-
# @param [Hash] options the options for the execution
|
127
|
-
# @option options [String] :jasmine_url the url of the Jasmine test runner
|
128
119
|
# @return [String] the Jasmine url
|
129
120
|
#
|
130
|
-
def jasmine_suite(file
|
131
|
-
options[:jasmine_url] + query_string_for_suite(file
|
121
|
+
def jasmine_suite(file)
|
122
|
+
options[:jasmine_url] + query_string_for_suite(file)
|
132
123
|
end
|
133
124
|
|
134
125
|
# Get the PhantomJS script that executes the spec and extracts
|
@@ -144,22 +135,17 @@ module Guard
|
|
144
135
|
# will be run.
|
145
136
|
#
|
146
137
|
# @param [String] file the spec file
|
147
|
-
# @param [Hash] options the options for the execution
|
148
|
-
# @option options [String] :spec_dir the directory with the Jasmine specs
|
149
138
|
# @return [String] the suite name
|
150
139
|
#
|
151
|
-
def query_string_for_suite(file
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
unless query_string
|
157
|
-
query_string = query_string_for_suite_from_first_describe(file, options)
|
140
|
+
def query_string_for_suite(file)
|
141
|
+
params = {}
|
142
|
+
if options[:query_params]
|
143
|
+
params.merge!(options[:query_params])
|
158
144
|
end
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
URI.
|
145
|
+
if file != options[:spec_dir]
|
146
|
+
params[:spec] = suite_from_line_number(file) || suite_from_first_describe(file)
|
147
|
+
end
|
148
|
+
params.empty? ? "" : "?"+URI.encode_www_form(params)
|
163
149
|
end
|
164
150
|
|
165
151
|
# When providing a line number by either the option or by
|
@@ -167,11 +153,9 @@ module Guard
|
|
167
153
|
# fromt the corresponding line number in the file.
|
168
154
|
#
|
169
155
|
# @param [String] file the spec file
|
170
|
-
# @param [Hash] options the options for the execution
|
171
|
-
# @option options [Fixnum] :line_number the line number to run
|
172
156
|
# @return [String] the suite name
|
173
157
|
#
|
174
|
-
def
|
158
|
+
def suite_from_line_number(file)
|
175
159
|
file_name, line_number = file_and_line_number_parts(file)
|
176
160
|
line_number ||= options[:line_number]
|
177
161
|
|
@@ -196,12 +180,11 @@ module Guard
|
|
196
180
|
# found.
|
197
181
|
#
|
198
182
|
# @param [String] file the spec file
|
199
|
-
# @param [Hash] options the options for the execution
|
200
183
|
# @return [String] the suite name
|
201
184
|
#
|
202
|
-
def
|
185
|
+
def suite_from_first_describe(file)
|
203
186
|
File.foreach(file) do |line|
|
204
|
-
if line =~ /describe\s*[("']+(.*?)["')]+/
|
187
|
+
if line =~ /describe\s*[("']+(.*?)["')]+/ #'
|
205
188
|
return $1
|
206
189
|
end
|
207
190
|
end
|
@@ -241,7 +224,7 @@ module Guard
|
|
241
224
|
# @return [String] the extracted title
|
242
225
|
#
|
243
226
|
def spec_title(line)
|
244
|
-
line[/['"](.+?)['
|
227
|
+
line[/['"](.+?)["']/, 1]
|
245
228
|
end
|
246
229
|
|
247
230
|
# Evaluates the JSON response that the PhantomJS script
|
@@ -250,33 +233,31 @@ module Guard
|
|
250
233
|
#
|
251
234
|
# @param [String] output the JSON output the spec run
|
252
235
|
# @param [String] file the file name of the spec
|
253
|
-
# @
|
254
|
-
# @return [Hash] the suite result
|
236
|
+
# @return [Hash] results of the suite's specs
|
255
237
|
#
|
256
|
-
def evaluate_response(output, file
|
238
|
+
def evaluate_response(output, file)
|
257
239
|
json = output.read
|
258
240
|
json = json.encode('UTF-8') if json.respond_to?(:encode)
|
259
|
-
|
260
241
|
begin
|
261
242
|
result = MultiJson.decode(json, { max_nesting: false })
|
262
243
|
raise 'No response from Jasmine runner' if !result && options[:is_cli]
|
263
|
-
|
244
|
+
pp result if options[:debug]
|
264
245
|
if result['error']
|
265
246
|
if options[:is_cli]
|
266
247
|
raise 'An error occurred in the Jasmine runner'
|
267
248
|
else
|
268
|
-
notify_runtime_error(result
|
249
|
+
notify_runtime_error(result)
|
269
250
|
end
|
270
251
|
elsif result
|
271
252
|
result['file'] = file
|
272
|
-
notify_spec_result(result
|
253
|
+
notify_spec_result(result)
|
273
254
|
end
|
274
255
|
|
275
256
|
if result && result['coverage'] && options[:coverage]
|
276
|
-
notify_coverage_result(result['coverage'], file
|
257
|
+
notify_coverage_result(result['coverage'], file)
|
277
258
|
end
|
278
259
|
|
279
|
-
result
|
260
|
+
return result
|
280
261
|
|
281
262
|
rescue MultiJson::DecodeError => e
|
282
263
|
if e.data == ''
|
@@ -287,10 +268,11 @@ module Guard
|
|
287
268
|
end
|
288
269
|
else
|
289
270
|
if options[:is_cli]
|
290
|
-
raise
|
271
|
+
raise "Cannot decode JSON from PhantomJS runner, message received was:\n#{json}"
|
291
272
|
else
|
292
273
|
Formatter.error("Cannot decode JSON from PhantomJS runner: #{ e.message }")
|
293
274
|
Formatter.error("JSON response: #{ e.data }")
|
275
|
+
Formatter.error("message received was:\n#{json}")
|
294
276
|
end
|
295
277
|
end
|
296
278
|
ensure
|
@@ -302,12 +284,11 @@ module Guard
|
|
302
284
|
# prohibits the execution of the Jasmine spec.
|
303
285
|
#
|
304
286
|
# @param [Hash] result the suite result
|
305
|
-
# @param [Hash] options the options for the execution
|
306
|
-
# @option options [Boolean] :notification show notifications
|
307
287
|
#
|
308
|
-
def notify_runtime_error(result
|
288
|
+
def notify_runtime_error(result)
|
309
289
|
message = "An error occurred: #{ result['error'] }"
|
310
|
-
Formatter.error(message)
|
290
|
+
Formatter.error(message )
|
291
|
+
Formatter.error( result['trace'] ) if result['trace']
|
311
292
|
Formatter.notify(message, title: 'Jasmine error', image: :failed, priority: 2) if options[:notification]
|
312
293
|
end
|
313
294
|
|
@@ -315,62 +296,59 @@ module Guard
|
|
315
296
|
# and some stats.
|
316
297
|
#
|
317
298
|
# @param [Hash] result the suite result
|
318
|
-
# @param [Hash] options the options for the execution
|
319
|
-
# @option options [Boolean] :notification show notifications
|
320
|
-
# @option options [Boolean] :hide_success hide success message notification
|
321
299
|
#
|
322
|
-
def notify_spec_result(result
|
323
|
-
specs
|
324
|
-
|
325
|
-
time
|
326
|
-
specs_plural
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
message = "#{ specs } spec#{ specs_plural }, #{ failures } failure#{ failures_plural }"
|
300
|
+
def notify_spec_result(result)
|
301
|
+
specs = result['stats']['specs'] - result['stats']['disabled']
|
302
|
+
failed = result['stats']['failed']
|
303
|
+
time = sprintf( '%0.2f', result['stats']['time'] )
|
304
|
+
specs_plural = specs == 1 ? '' : 's'
|
305
|
+
failed_plural = failed == 1 ? '' : 's'
|
306
|
+
Formatter.info("Finished in #{ time } seconds")
|
307
|
+
pending = result['stats']['pending'].to_i > 0 ? " #{result['stats']['pending']} pending," : ""
|
308
|
+
message = "#{ specs } spec#{ specs_plural },#{pending} #{ failed } failure#{ failed_plural }"
|
332
309
|
full_message = "#{ message }\nin #{ time } seconds"
|
333
|
-
passed =
|
310
|
+
passed = failed == 0
|
311
|
+
|
312
|
+
report_specdoc(result, passed) if specdoc_shown?(passed)
|
334
313
|
|
335
314
|
if passed
|
336
|
-
report_specdoc(result, passed, options)
|
337
315
|
Formatter.success(message)
|
338
316
|
Formatter.notify(full_message, title: 'Jasmine suite passed') if options[:notification] && !options[:hide_success]
|
339
317
|
else
|
340
|
-
|
318
|
+
errors = collect_spec_errors(result['suites'])
|
319
|
+
error_message = errors[0..options[:max_error_notify]].join("\n")
|
320
|
+
|
341
321
|
Formatter.error(message)
|
342
|
-
|
343
|
-
|
322
|
+
if options[:notification]
|
323
|
+
Formatter.notify( "#{error_message}\n#{full_message}",
|
324
|
+
title: 'Jasmine suite failed', image: :failed, priority: 2)
|
325
|
+
end
|
344
326
|
end
|
345
327
|
|
346
|
-
Formatter.info("Done.\n")
|
347
328
|
end
|
348
329
|
|
349
|
-
# Notification about the coverage of a spec run, success or
|
330
|
+
# Notification about the coverage of a spec run, success or failed,
|
350
331
|
# and some stats.
|
351
332
|
#
|
352
333
|
# @param [Hash] coverage the coverage hash from the JSON
|
353
334
|
# @param [String] file the file name of the spec
|
354
|
-
# @param [Hash] options the options for the execution
|
355
|
-
# @option options [Boolean] :notification show notifications
|
356
|
-
# @option options [Boolean] :hide_success hide success message notification
|
357
335
|
#
|
358
|
-
def notify_coverage_result(coverage, file
|
336
|
+
def notify_coverage_result(coverage, file)
|
359
337
|
if coverage_bin
|
360
338
|
FileUtils.mkdir_p(coverage_root) unless File.exist?(coverage_root)
|
361
339
|
|
362
|
-
update_coverage(coverage, file
|
340
|
+
update_coverage(coverage, file)
|
363
341
|
|
364
342
|
if options[:coverage_summary]
|
365
343
|
generate_summary_report
|
366
344
|
else
|
367
|
-
generate_text_report(file
|
345
|
+
generate_text_report(file)
|
368
346
|
end
|
369
347
|
|
370
|
-
check_coverage
|
348
|
+
check_coverage
|
371
349
|
|
372
350
|
if options[:coverage_html]
|
373
|
-
generate_html_report
|
351
|
+
generate_html_report
|
374
352
|
end
|
375
353
|
else
|
376
354
|
Formatter.error('Skipping coverage report: unable to locate istanbul in your PATH')
|
@@ -381,9 +359,8 @@ module Guard
|
|
381
359
|
# last coverage run.
|
382
360
|
#
|
383
361
|
# @param [String] file the file name of the spec
|
384
|
-
# @param [Hash] options the options for the execution
|
385
362
|
#
|
386
|
-
def generate_text_report(file
|
363
|
+
def generate_text_report(file)
|
387
364
|
Formatter.info 'Spec coverage details:'
|
388
365
|
|
389
366
|
if file == options[:spec_dir]
|
@@ -405,11 +382,9 @@ module Guard
|
|
405
382
|
# Uses the Istanbul text reported to output the result of the
|
406
383
|
# last coverage run.
|
407
384
|
#
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
if any_coverage_threshold?(options)
|
412
|
-
coverage = `#{coverage_bin} check-coverage #{ istanbul_coverage_options(options) } #{ coverage_file } 2>&1`
|
385
|
+
def check_coverage
|
386
|
+
if any_coverage_threshold?
|
387
|
+
coverage = `#{coverage_bin} check-coverage #{ istanbul_coverage_options } #{ coverage_file } 2>&1`
|
413
388
|
coverage = coverage.split("\n").grep(/ERROR/).join.sub('ERROR:', '')
|
414
389
|
failed = $? && $?.exitstatus != 0
|
415
390
|
|
@@ -426,10 +401,8 @@ module Guard
|
|
426
401
|
# Uses the Istanbul text reported to output the result of the
|
427
402
|
# last coverage run.
|
428
403
|
#
|
429
|
-
|
430
|
-
|
431
|
-
def generate_html_report(options)
|
432
|
-
report_directory = coverage_report_directory(options)
|
404
|
+
def generate_html_report
|
405
|
+
report_directory = coverage_report_directory
|
433
406
|
`#{coverage_bin} report --dir #{ report_directory } --root #{ coverage_root } html #{ coverage_file }`
|
434
407
|
Formatter.info "Updated HTML report available at: #{ report_directory }/index.html"
|
435
408
|
end
|
@@ -453,12 +426,10 @@ module Guard
|
|
453
426
|
#
|
454
427
|
# @param [Hash] result the suite result
|
455
428
|
# @param [Boolean] passed status
|
456
|
-
# @param [Hash] options the options
|
457
|
-
# @option options [Symbol] :console options for the console.log output, either :always, :never or :failure
|
458
429
|
#
|
459
|
-
def report_specdoc(result, passed
|
430
|
+
def report_specdoc(result, passed)
|
460
431
|
result['suites'].each do |suite|
|
461
|
-
report_specdoc_suite(suite, passed
|
432
|
+
report_specdoc_suite(suite, passed)
|
462
433
|
end
|
463
434
|
end
|
464
435
|
|
@@ -466,128 +437,78 @@ module Guard
|
|
466
437
|
#
|
467
438
|
# @param [Hash] suite the suite
|
468
439
|
# @param [Boolean] passed status
|
469
|
-
# @param [Hash] options the options
|
470
|
-
# @option options [Symbol] :console options for the console.log output, either :always, :never or :failure
|
471
|
-
# @option options [Symbol] :focus options for focus on failures in the specdoc
|
472
440
|
# @param [Number] level the indention level
|
473
441
|
#
|
474
|
-
def report_specdoc_suite(suite,
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
end
|
442
|
+
def report_specdoc_suite(suite, run_passed, level = 0)
|
443
|
+
|
444
|
+
# Print the suite description when the specdoc is shown or there are logs to display
|
445
|
+
Formatter.suite_name((' ' * level) + suite['description'])
|
479
446
|
|
480
447
|
suite['specs'].each do |spec|
|
481
|
-
if
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
448
|
+
# Specs are shown if they failed, or if they passed and the "focus" option is falsey
|
449
|
+
# If specs are going to be shown, then pending are also shown
|
450
|
+
# If the focus option is set, then only failing tests are shown
|
451
|
+
next unless :always==options[:specdoc] || spec['status'] == 'failed' || ( !run_passed && !options[:focus] )
|
452
|
+
if spec['status'] == 'passed'
|
453
|
+
Formatter.success(indent(" ✔ #{ spec['description'] }", level))
|
454
|
+
elsif spec['status'] == 'failed'
|
455
|
+
Formatter.spec_failed(indent(" ✘ #{ spec['description'] }", level))
|
487
456
|
else
|
488
|
-
Formatter.
|
489
|
-
spec['messages'].each do |message|
|
490
|
-
Formatter.spec_failed(indent(" ➤ #{ format_message(message, false) }", level)) if specdoc_shown?(passed, options)
|
491
|
-
end
|
492
|
-
report_specdoc_errors(spec, options, level)
|
493
|
-
report_specdoc_logs(spec, options, level)
|
457
|
+
Formatter.spec_pending(indent(" ○ #{ spec['description'] }", level))
|
494
458
|
end
|
459
|
+
report_specdoc_errors(spec, level)
|
460
|
+
report_specdoc_logs(spec, level)
|
495
461
|
end
|
496
462
|
|
497
|
-
suite['suites'].each { |s| report_specdoc_suite(s,
|
463
|
+
suite['suites'].each { |s| report_specdoc_suite(s, run_passed, level + 2) } if suite['suites']
|
498
464
|
end
|
499
465
|
|
500
466
|
# Is the specdoc shown for this suite?
|
501
467
|
#
|
502
468
|
# @param [Boolean] passed the spec status
|
503
|
-
# @param [Hash] options the options
|
504
469
|
#
|
505
|
-
def specdoc_shown?(passed
|
470
|
+
def specdoc_shown?(passed)
|
506
471
|
options[:specdoc] == :always || (options[:specdoc] == :failure && !passed)
|
507
472
|
end
|
508
473
|
|
509
|
-
# Are console logs shown for this suite?
|
510
|
-
#
|
511
|
-
# @param [Hash] suite the suite
|
512
|
-
# @param [Boolean] passed the spec status
|
513
|
-
# @param [Hash] options the options
|
514
|
-
#
|
515
|
-
def console_logs_shown?(suite, passed, options = { })
|
516
|
-
# Are console messages displayed?
|
517
|
-
console_enabled = options[:console] == :always || (options[:console] == :failure && !passed)
|
518
|
-
|
519
|
-
# Are there any logs to display at all for this suite?
|
520
|
-
logs_for_current_options = suite['specs'].select do |spec|
|
521
|
-
spec['logs'] && (options[:console] == :always || (options[:console] == :failure && !spec['passed']))
|
522
|
-
end
|
523
|
-
|
524
|
-
any_logs_present = !logs_for_current_options.empty?
|
525
|
-
|
526
|
-
console_enabled && any_logs_present
|
527
|
-
end
|
528
474
|
|
529
475
|
# Are console logs shown for this spec?
|
530
476
|
#
|
531
477
|
# @param [Hash] spec the spec
|
532
|
-
# @param [Hash] options the options
|
533
478
|
#
|
534
|
-
def console_for_spec?(spec
|
535
|
-
spec['logs'] && ((spec['
|
536
|
-
(
|
479
|
+
def console_for_spec?(spec)
|
480
|
+
spec['logs'] && (( spec['status'] == 'passed' && options[:console] == :always) ||
|
481
|
+
(spec['status'] == 'failed' && options[:console] != :never) )
|
537
482
|
end
|
538
483
|
|
539
|
-
# Are error logs shown for this suite?
|
540
|
-
#
|
541
|
-
# @param [Hash] suite the suite
|
542
|
-
# @param [Boolean] passed the spec status
|
543
|
-
# @param [Hash] options the options
|
544
|
-
#
|
545
|
-
def error_logs_shown?(suite, passed, options = { })
|
546
|
-
# Are error messages displayed?
|
547
|
-
errors_enabled = options[:errors] == :always || (options[:errors] == :failure && !passed)
|
548
|
-
|
549
|
-
# Are there any errors to display at all for this suite?
|
550
|
-
errors_for_current_options = suite['specs'].select do |spec|
|
551
|
-
spec['errors'] && (options[:errors] == :always || (options[:errors] == :failure && !spec['passed']))
|
552
|
-
end
|
553
|
-
|
554
|
-
any_errors_present= !errors_for_current_options.empty?
|
555
|
-
|
556
|
-
errors_enabled && any_errors_present
|
557
|
-
end
|
558
484
|
|
559
485
|
# Are errors shown for this spec?
|
560
486
|
#
|
561
487
|
# @param [Hash] spec the spec
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
(!spec['passed'] && options[:errors] != :never))
|
488
|
+
def errors_for_spec?(spec)
|
489
|
+
spec['errors'] && ((spec['status']=='passed' && options[:errors] == :always) ||
|
490
|
+
(spec['status']=='failed' && options[:errors] != :never))
|
566
491
|
end
|
567
492
|
|
568
493
|
# Is the description shown for this spec?
|
569
494
|
#
|
570
495
|
# @param [Boolean] passed the spec status
|
571
496
|
# @param [Hash] spec the spec
|
572
|
-
# @param [Hash] options the options
|
573
497
|
#
|
574
|
-
def description_shown?(passed, spec
|
575
|
-
specdoc_shown?(passed
|
498
|
+
def description_shown?(passed, spec)
|
499
|
+
specdoc_shown?(passed) || console_for_spec?(spec) || errors_for_spec?(spec)
|
576
500
|
end
|
577
501
|
|
578
502
|
# Shows the logs for a given spec.
|
579
503
|
#
|
580
504
|
# @param [Hash] spec the spec result
|
581
|
-
# @param [Hash] options the options
|
582
|
-
# @option options [Symbol] :console options for the console.log output, either :always, :never or :failure
|
583
505
|
# @param [Number] level the indention level
|
584
506
|
#
|
585
|
-
def report_specdoc_logs(spec,
|
586
|
-
if
|
587
|
-
spec['logs'].each do |
|
588
|
-
log
|
589
|
-
|
590
|
-
end
|
507
|
+
def report_specdoc_logs(spec, level)
|
508
|
+
if console_for_spec?(spec)
|
509
|
+
spec['logs'].each do |log_level, message|
|
510
|
+
log_level = log_level == 'log' ? '' : "#{log_level.upcase}: "
|
511
|
+
Formatter.info(indent(" • #{log_level}#{ message }", level))
|
591
512
|
end
|
592
513
|
end
|
593
514
|
end
|
@@ -595,19 +516,16 @@ module Guard
|
|
595
516
|
# Shows the errors for a given spec.
|
596
517
|
#
|
597
518
|
# @param [Hash] spec the spec result
|
598
|
-
# @param [Hash] options the options
|
599
|
-
# @option options [Symbol] :errors options for the errors output, either :always, :never or :failure
|
600
519
|
# @param [Number] level the indention level
|
601
520
|
#
|
602
|
-
def report_specdoc_errors(spec,
|
603
|
-
if spec['errors'] && (options[:errors] == :always || (options[:errors] == :failure &&
|
521
|
+
def report_specdoc_errors(spec, level)
|
522
|
+
if spec['errors'] && (options[:errors] == :always || (options[:errors] == :failure && spec['status']=='failed'))
|
604
523
|
spec['errors'].each do |error|
|
524
|
+
Formatter.spec_failed(indent(" ➤ #{ format_error(error,true) }", level))
|
605
525
|
if error['trace']
|
606
526
|
error['trace'].each do |trace|
|
607
|
-
Formatter.spec_failed(indent(" ➜
|
527
|
+
Formatter.spec_failed(indent(" ➜ #{ trace['file'] } on line #{ trace['line'] }", level+2))
|
608
528
|
end
|
609
|
-
else
|
610
|
-
Formatter.spec_failed(indent(" ➜ Exception: #{ error['msg'] }", level))
|
611
529
|
end
|
612
530
|
end
|
613
531
|
end
|
@@ -622,24 +540,6 @@ module Guard
|
|
622
540
|
(' ' * level) + message
|
623
541
|
end
|
624
542
|
|
625
|
-
# Show system notifications about the occurred errors.
|
626
|
-
#
|
627
|
-
# @param [Hash] result the suite result
|
628
|
-
# @param [Hash] options the options
|
629
|
-
# @option options [Integer] :max_error_notify maximum error notifications to show
|
630
|
-
# @option options [Boolean] :notification show notifications
|
631
|
-
#
|
632
|
-
def notify_errors(result, options)
|
633
|
-
collect_specs(result['suites']).each_with_index do |spec, index|
|
634
|
-
if !spec['passed'] && options[:max_error_notify] > index
|
635
|
-
msg = spec['messages'].map { |message| format_message(message, true) }.join(', ')
|
636
|
-
Formatter.notify("#{ spec['description'] }: #{ msg }",
|
637
|
-
title: 'Jasmine spec failed',
|
638
|
-
image: :failed,
|
639
|
-
priority: 2) if options[:notification]
|
640
|
-
end
|
641
|
-
end
|
642
|
-
end
|
643
543
|
|
644
544
|
# Tests if the given suite has a failing spec underneath.
|
645
545
|
#
|
@@ -647,7 +547,19 @@ module Guard
|
|
647
547
|
# @return [Boolean] the search result
|
648
548
|
#
|
649
549
|
def contains_failed_spec?(suite)
|
650
|
-
collect_specs([suite]).any? { |spec|
|
550
|
+
collect_specs([suite]).any? { |spec| spec['status'] == 'failed' }
|
551
|
+
end
|
552
|
+
|
553
|
+
|
554
|
+
# Get all failed specs from the suites and its nested suites.
|
555
|
+
#
|
556
|
+
# @param suites [Array<Hash>] the suites results
|
557
|
+
# @return [Array<Hash>] all failed
|
558
|
+
#
|
559
|
+
def collect_spec_errors(suites)
|
560
|
+
collect_specs(suites).map { |spec|
|
561
|
+
(spec['errors']||[]).map { |error| format_error(error,false) }
|
562
|
+
}.flatten
|
651
563
|
end
|
652
564
|
|
653
565
|
# Get all specs from the suites and its nested suites.
|
@@ -656,10 +568,9 @@ module Guard
|
|
656
568
|
# @return [Array<Hash>] all specs
|
657
569
|
#
|
658
570
|
def collect_specs(suites)
|
659
|
-
suites.
|
660
|
-
specs
|
661
|
-
specs
|
662
|
-
specs
|
571
|
+
suites.each_with_object([]) do |suite, specs|
|
572
|
+
specs.concat( suite['specs'] )
|
573
|
+
specs.concat( collect_specs(suite['suites']) ) if suite['suites']
|
663
574
|
end
|
664
575
|
end
|
665
576
|
|
@@ -669,9 +580,11 @@ module Guard
|
|
669
580
|
# @param [Boolean] short show a short version of the message
|
670
581
|
# @return [String] the cleaned error message
|
671
582
|
#
|
672
|
-
def
|
673
|
-
|
674
|
-
|
583
|
+
def format_error(error, short)
|
584
|
+
message = error['message'].gsub(%r{ in http.*\(line \d+\)$},'')
|
585
|
+
if !short && error['trace'] && error['trace'].length > 0
|
586
|
+
location = error['trace'][0]
|
587
|
+
"#{message} in #{location['file']}:#{location['line']}"
|
675
588
|
else
|
676
589
|
message
|
677
590
|
end
|
@@ -682,9 +595,8 @@ module Guard
|
|
682
595
|
#
|
683
596
|
# @param [Hash] coverage the last run coverage data
|
684
597
|
# @param [String] file the file name of the spec
|
685
|
-
# @param [Hash] options the options for the execution
|
686
598
|
#
|
687
|
-
def update_coverage(coverage, file
|
599
|
+
def update_coverage(coverage, file)
|
688
600
|
if file == options[:spec_dir]
|
689
601
|
File.write(coverage_file, MultiJson.encode(coverage, { max_nesting: false }))
|
690
602
|
else
|
@@ -707,16 +619,15 @@ module Guard
|
|
707
619
|
#
|
708
620
|
# @return [Boolean] true if any coverage threshold is set
|
709
621
|
#
|
710
|
-
def any_coverage_threshold?
|
622
|
+
def any_coverage_threshold?
|
711
623
|
THRESHOLDS.any? { |threshold| options[threshold] != 0 }
|
712
624
|
end
|
713
625
|
|
714
626
|
# Converts the options to Istanbul recognized options
|
715
627
|
#
|
716
|
-
# @param [Hash] options the options for the coverage
|
717
628
|
# @return [String] the command line options
|
718
629
|
#
|
719
|
-
def istanbul_coverage_options
|
630
|
+
def istanbul_coverage_options
|
720
631
|
THRESHOLDS.inject([]) do |coverage, name|
|
721
632
|
threshold = options[name]
|
722
633
|
coverage << (threshold != 0 ? "--#{ name.to_s.sub('_threshold', '') } #{ threshold }" : '')
|
@@ -750,13 +661,12 @@ module Guard
|
|
750
661
|
|
751
662
|
# Creates and returns the coverage report directory.
|
752
663
|
#
|
753
|
-
# @param [Hash] options for the coverage report directory
|
754
664
|
# @return [String] the coverage report directory
|
755
665
|
#
|
756
|
-
def coverage_report_directory
|
666
|
+
def coverage_report_directory
|
757
667
|
File.expand_path(options[:coverage_html_dir])
|
758
668
|
end
|
759
|
-
end
|
760
669
|
end
|
761
670
|
end
|
762
671
|
end
|
672
|
+
|