jasmine-coverage 0.2.2 → 0.2.4
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/README.md +4 -0
- data/lib/jasmine/coverage/version.rb +1 -1
- data/lib/tasks/base64.min.js +2 -0
- data/lib/tasks/coverage_output_generator.js +17 -10
- data/lib/tasks/jasmine_coverage.rake +60 -27
- data/lib/tasks/jasmine_headless_coverage_patches.rb +29 -9
- metadata +12 -11
data/README.md
CHANGED
@@ -59,6 +59,10 @@ it generates regardless, you can specify that in an environment variable.
|
|
59
59
|
|
60
60
|
bundle exec rake jasmine:coverage JASMINE_COVERAGE_KEEP_TEST_RIG=true
|
61
61
|
|
62
|
+
You can also specify if you want missing coverage warnings
|
63
|
+
|
64
|
+
bundle exec rake jasmine:coverage JASMINE_COVERAGE_WARNINGS=true
|
65
|
+
|
62
66
|
# How it works
|
63
67
|
|
64
68
|
First Sprockets is interrogated to get a list of JS files concerned. This way, the right JS files
|
@@ -0,0 +1,2 @@
|
|
1
|
+
// credit to https://github.com/dankogai/js-base64
|
2
|
+
(function(global){"use strict";if(global.Base64)return;var version="2.1.1";var buffer;if(typeof module!=="undefined"&&module.exports){buffer=require("buffer").Buffer}var b64chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var b64tab=function(bin){var t={};for(var i=0,l=bin.length;i<l;i++)t[bin.charAt(i)]=i;return t}(b64chars);var fromCharCode=String.fromCharCode;var cb_utob=function(c){if(c.length<2){var cc=c.charCodeAt(0);return cc<128?c:cc<2048?fromCharCode(192|cc>>>6)+fromCharCode(128|cc&63):fromCharCode(224|cc>>>12&15)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}else{var cc=65536+(c.charCodeAt(0)-55296)*1024+(c.charCodeAt(1)-56320);return fromCharCode(240|cc>>>18&7)+fromCharCode(128|cc>>>12&63)+fromCharCode(128|cc>>>6&63)+fromCharCode(128|cc&63)}};var re_utob=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;var utob=function(u){return u.replace(re_utob,cb_utob)};var cb_encode=function(ccc){var padlen=[0,2,1][ccc.length%3],ord=ccc.charCodeAt(0)<<16|(ccc.length>1?ccc.charCodeAt(1):0)<<8|(ccc.length>2?ccc.charCodeAt(2):0),chars=[b64chars.charAt(ord>>>18),b64chars.charAt(ord>>>12&63),padlen>=2?"=":b64chars.charAt(ord>>>6&63),padlen>=1?"=":b64chars.charAt(ord&63)];return chars.join("")};var btoa=global.btoa||function(b){return b.replace(/[\s\S]{1,3}/g,cb_encode)};var _encode=buffer?function(u){return new buffer(u).toString("base64")}:function(u){return btoa(utob(u))};var encode=function(u,urisafe){return!urisafe?_encode(u):_encode(u).replace(/[+\/]/g,function(m0){return m0=="+"?"-":"_"}).replace(/=/g,"")};var encodeURI=function(u){return encode(u,true)};var re_btou=new RegExp(["[À-ß][-¿]","[à-ï][-¿]{2}","[ð-÷][-¿]{3}"].join("|"),"g");var cb_btou=function(cccc){switch(cccc.length){case 4:var cp=(7&cccc.charCodeAt(0))<<18|(63&cccc.charCodeAt(1))<<12|(63&cccc.charCodeAt(2))<<6|63&cccc.charCodeAt(3),offset=cp-65536;return fromCharCode((offset>>>10)+55296)+fromCharCode((offset&1023)+56320);case 3:return fromCharCode((15&cccc.charCodeAt(0))<<12|(63&cccc.charCodeAt(1))<<6|63&cccc.charCodeAt(2));default:return fromCharCode((31&cccc.charCodeAt(0))<<6|63&cccc.charCodeAt(1))}};var btou=function(b){return b.replace(re_btou,cb_btou)};var cb_decode=function(cccc){var len=cccc.length,padlen=len%4,n=(len>0?b64tab[cccc.charAt(0)]<<18:0)|(len>1?b64tab[cccc.charAt(1)]<<12:0)|(len>2?b64tab[cccc.charAt(2)]<<6:0)|(len>3?b64tab[cccc.charAt(3)]:0),chars=[fromCharCode(n>>>16),fromCharCode(n>>>8&255),fromCharCode(n&255)];chars.length-=[0,0,2,1][padlen];return chars.join("")};var atob=global.atob||function(a){return a.replace(/[\s\S]{1,4}/g,cb_decode)};var _decode=buffer?function(a){return new buffer(a,"base64").toString()}:function(a){return btou(atob(a))};var decode=function(a){return _decode(a.replace(/[-_]/g,function(m0){return m0=="-"?"+":"/"}).replace(/[^A-Za-z0-9\+\/]/g,""))};global.Base64={VERSION:version,atob:atob,btoa:btoa,fromBase64:decode,toBase64:encode,utob:utob,encode:encode,encodeURI:encodeURI,btou:btou,decode:decode};if(typeof Object.defineProperty==="function"){var noEnum=function(v){return{value:v,enumerable:false,writable:true,configurable:true}};global.Base64.extendString=function(){Object.defineProperty(String.prototype,"fromBase64",noEnum(function(){return decode(this)}));Object.defineProperty(String.prototype,"toBase64",noEnum(function(urisafe){return encode(this,urisafe)}));Object.defineProperty(String.prototype,"toBase64URI",noEnum(function(){return encode(this,true)}))}}})(this);
|
@@ -31,8 +31,8 @@ function generateEncodedCoverage() {
|
|
31
31
|
for (var file_name in window._$jscoverage) {
|
32
32
|
var jscov = window._$jscoverage[ file_name ];
|
33
33
|
var file_report = rv[ file_name ] = {
|
34
|
-
coverage:new Array(jscov.length),
|
35
|
-
source:new Array(jscov.length)
|
34
|
+
coverage: new Array(jscov.length),
|
35
|
+
source: new Array(jscov.length)
|
36
36
|
};
|
37
37
|
for (var i = 0; i < jscov.length; ++i) {
|
38
38
|
var hit_count = jscov[ i ] !== undefined ? jscov[ i ] : null;
|
@@ -47,7 +47,7 @@ function generateEncodedCoverage() {
|
|
47
47
|
|
48
48
|
function coverageForAllFiles() {
|
49
49
|
|
50
|
-
var totals = { files:0, statements:0, executed:0 };
|
50
|
+
var totals = { files: 0, statements: 0, executed: 0 };
|
51
51
|
|
52
52
|
var output = "Coverage was:\n";
|
53
53
|
|
@@ -58,8 +58,14 @@ function coverageForAllFiles() {
|
|
58
58
|
totals['files']++;
|
59
59
|
totals['statements'] += simple_file_coverage['statements'];
|
60
60
|
totals['executed'] += simple_file_coverage['executed'];
|
61
|
+
if (simple_file_coverage['missing'].length > 0 && JasmineCoverage.warnings) {
|
62
|
+
var i, ar = simple_file_coverage['missing'], l = ar.length;
|
63
|
+
for (i = 0; i < l; i++) {
|
64
|
+
console.log("\033[31m" + "WARNING -- File '" + file_name + "' has no coverage on line: " + ar[i] + "\033[0m");
|
65
|
+
}
|
66
|
+
}
|
61
67
|
|
62
|
-
var fraction = (simple_file_coverage['executed']+"/"+simple_file_coverage['statements']).lpad(' ', 10);
|
68
|
+
var fraction = (simple_file_coverage['executed'] + "/" + simple_file_coverage['statements']).lpad(' ', 10);
|
63
69
|
output += fraction + (" = " + simple_file_coverage['percentage'] + "").lpad(' ', 3) + "% for " + file_name + "\n";
|
64
70
|
}
|
65
71
|
|
@@ -69,10 +75,10 @@ function coverageForAllFiles() {
|
|
69
75
|
}
|
70
76
|
|
71
77
|
if (totals['statements'] === 0) {
|
72
|
-
log("No Javascript was found to test coverage for.");
|
78
|
+
console.log("No Javascript was found to test coverage for.");
|
73
79
|
} else {
|
74
|
-
output += ( totals['executed'] +"/"+totals['statements']+ " = "+ coverage + "").lpad(' ', 15) + "% Total\n";
|
75
|
-
log(output);
|
80
|
+
output += ( totals['executed'] + "/" + totals['statements'] + " = " + coverage + "").lpad(' ', 15) + "% Total\n";
|
81
|
+
console.log(output);
|
76
82
|
}
|
77
83
|
|
78
84
|
return coverage;
|
@@ -120,8 +126,9 @@ function coverageForFile(fileCC) {
|
|
120
126
|
var percentage = ( num_statements === 0 ? 0 : parseInt(100 * num_executed / num_statements) );
|
121
127
|
|
122
128
|
return {
|
123
|
-
statements:num_statements,
|
124
|
-
executed:num_executed,
|
125
|
-
percentage:percentage
|
129
|
+
statements: num_statements,
|
130
|
+
executed: num_executed,
|
131
|
+
percentage: percentage,
|
132
|
+
missing: missing
|
126
133
|
};
|
127
134
|
}
|
@@ -24,11 +24,11 @@ if env =~ /^(development|test)$/
|
|
24
24
|
FileUtils.rm_rf output_dir
|
25
25
|
FileUtils.mkdir_p instrumented_dir
|
26
26
|
|
27
|
-
# The reprocessing folder map
|
28
|
-
files_map = {
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
# The reprocessing folder map. Use an environment variable if available.
|
28
|
+
files_map = ENV['JS_SRC_PATH'] ? {ENV['JS_SRC_PATH'] => instrumented_dir+'public'} : {
|
29
|
+
File.expand_path('app/assets/javascripts') => instrumented_dir+'app',
|
30
|
+
File.expand_path('lib/assets/javascripts') => instrumented_dir+'lib',
|
31
|
+
File.expand_path('public/javascripts') => instrumented_dir+'public',
|
32
32
|
}
|
33
33
|
|
34
34
|
# Instrument the source files into the instrumented folders
|
@@ -38,6 +38,7 @@ if env =~ /^(development|test)$/
|
|
38
38
|
FileUtils.mv(Dir.glob(files_map[folder]+'/jscoverage*'), output_dir)
|
39
39
|
end
|
40
40
|
|
41
|
+
Jasmine::Coverage.warnings = ENV['JASMINE_COVERAGE_WARNINGS'] || 'false'
|
41
42
|
Jasmine::Coverage.resources = files_map
|
42
43
|
Jasmine::Coverage.output_dir = output_dir
|
43
44
|
test_rig_folder = "#{Jasmine::Coverage.output_dir}/test-rig"
|
@@ -46,26 +47,40 @@ if env =~ /^(development|test)$/
|
|
46
47
|
|
47
48
|
# Run Jasmine using the original config.
|
48
49
|
status_code = Jasmine::Headless::Runner.run(
|
49
|
-
|
50
|
+
# Any options from the options.rb file in jasmine-headless-webkit can be used here.
|
50
51
|
|
51
|
-
|
52
|
+
:reporters => [['File', "#{output_dir}/rawreport.txt"]]
|
52
53
|
)
|
53
|
-
errStr =
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
54
|
+
errStr = <<-EOS
|
55
|
+
JSCoverage exited with error code: #{status_code}
|
56
|
+
|
57
|
+
This implies one of five things:
|
58
|
+
0) Your JS files had exactly zero instructions. Are they all blank or just comments?
|
59
|
+
1) A test failed (run bundle exec rake jasmine:headless to see a better error)
|
60
|
+
2) The sourcecode has a syntax error (which JSLint should find)
|
61
|
+
3) An error occurred in a deferred block, e.g. a setTimeout or underscore _.defer. This caused a window error which Jasmine will never see.
|
62
|
+
4) The source files are being loaded out of sequence (so global variables are not being declared in order)
|
63
|
+
To check this, run bundle exec jasmine-headless-webkit -l to see the ordering
|
64
|
+
|
65
|
+
In any case, try running the standard jasmine command to get better errors:
|
66
|
+
|
67
|
+
bundle exec rake jasmine:headless
|
68
|
+
|
69
|
+
Finally, try opening the test-rig in firefox to see the tests run in a browser and get a stacktrace. Chrome has strict security settings
|
70
|
+
that make this difficult since it accesses the local filesystem from Javascript (but you can switch the settings off at the command line).
|
71
|
+
|
72
|
+
|
73
|
+
**********************************************************************************************
|
74
|
+
|
75
|
+
The test rig file needs to load JS directly off disk, which Chrome prevents by default. Your best bet is to open the rig in Firefox.
|
76
|
+
|
77
|
+
The file can be found here: #{test_rig_folder}/jscoverage-test-rig.html
|
78
|
+
|
79
|
+
**********************************************************************************************
|
68
80
|
|
81
|
+
EOS
|
82
|
+
|
83
|
+
fail errStr if status_code == 1
|
69
84
|
# Delete the test_rig folder if not required
|
70
85
|
if ENV['JASMINE_COVERAGE_KEEP_TEST_RIG']
|
71
86
|
p "A copy of the page and files that were used as the jasmine test environment can be found here: #{test_rig_folder}"
|
@@ -77,8 +92,18 @@ if env =~ /^(development|test)$/
|
|
77
92
|
contents = File.open("#{output_dir}/rawreport.txt") { |f| f.read }
|
78
93
|
# Get our Base64.
|
79
94
|
json_report_enc = contents.split(/ENCODED-COVERAGE-EXPORT-STARTS:/m)[1]
|
80
|
-
#
|
81
|
-
|
95
|
+
# Provide warnings to use
|
96
|
+
warning_regex = /^CONSOLE\|\|.{1,6}WARNING.{4}(.*).{5}$/
|
97
|
+
warnings = contents.scan warning_regex
|
98
|
+
if (warnings.length != 0)
|
99
|
+
puts "Detected #{warnings.length} warnings:"
|
100
|
+
puts warnings
|
101
|
+
fail "Aborting. All lines must be covered by a test." if ENV['MUST_COVER_ALL']
|
102
|
+
else
|
103
|
+
puts "No warnings detected."
|
104
|
+
end if
|
105
|
+
# Remove the junk at the end
|
106
|
+
json_report_enc_stripped = json_report_enc[0, json_report_enc.index("\"")]
|
82
107
|
|
83
108
|
# Unpack it from Base64
|
84
109
|
json_report = Base64.decode64(json_report_enc_stripped)
|
@@ -119,6 +144,8 @@ if env =~ /^(development|test)$/
|
|
119
144
|
module Jasmine
|
120
145
|
module Coverage
|
121
146
|
@resources
|
147
|
+
@output_dir
|
148
|
+
@warnings
|
122
149
|
|
123
150
|
def self.resources= resources
|
124
151
|
@resources = resources
|
@@ -128,8 +155,6 @@ if env =~ /^(development|test)$/
|
|
128
155
|
@resources
|
129
156
|
end
|
130
157
|
|
131
|
-
@output_dir
|
132
|
-
|
133
158
|
def self.output_dir= output_dir
|
134
159
|
@output_dir = output_dir
|
135
160
|
end
|
@@ -137,7 +162,15 @@ if env =~ /^(development|test)$/
|
|
137
162
|
def self.output_dir
|
138
163
|
@output_dir
|
139
164
|
end
|
165
|
+
|
166
|
+
def self.warnings= warnings
|
167
|
+
@warnings = warnings
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.warnings
|
171
|
+
@warnings
|
172
|
+
end
|
140
173
|
end
|
141
174
|
end
|
142
175
|
|
143
|
-
end
|
176
|
+
end
|
@@ -16,14 +16,11 @@ module Jasmine::Headless
|
|
16
16
|
FileUtils.mkdir_p test_rigfolder
|
17
17
|
|
18
18
|
p "Copying all view files and potential javascript fixture folders so the jasmine-coverage run has access to the html fixtures."
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# Here we must also copy the spec and app folders to that we have access to all the files if we need them for the test rig
|
25
|
-
FileUtils.copy_entry("#{Jasmine::Coverage.output_dir}/../../spec", "#{test_rigfolder}/spec")
|
26
|
-
FileUtils.copy_entry("#{Jasmine::Coverage.output_dir}/../../app", "#{test_rigfolder}/app")
|
19
|
+
copy_assets_to_test_dir(test_rigfolder, '../fixtures', 'target/fixtures')
|
20
|
+
copy_assets_to_test_dir(test_rigfolder, '../views', 'target/views')
|
21
|
+
# Here we must also copy the spec and app folders so that we have access to all the files if we need them for the test rig
|
22
|
+
copy_assets_to_test_dir(test_rigfolder, '../../spec', 'spec')
|
23
|
+
copy_assets_to_test_dir(test_rigfolder, '../../app', 'app')
|
27
24
|
|
28
25
|
jss = str.scan(/<script type="text\/javascript" src="(.*)"><\/script>/)
|
29
26
|
jss << str.scan(/<link rel="stylesheet" href="(.*)" type="text\/css" \/>/)
|
@@ -52,6 +49,15 @@ module Jasmine::Headless
|
|
52
49
|
|
53
50
|
ret
|
54
51
|
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def copy_assets_to_test_dir(test_rigfolder, from_dir, to_dir)
|
56
|
+
if File.exists? "#{Jasmine::Coverage.output_dir}/#{from_dir}"
|
57
|
+
FileUtils.mkdir_p "#{test_rigfolder}/#{to_dir}"
|
58
|
+
FileUtils.copy_entry("#{Jasmine::Coverage.output_dir}/#{from_dir}", "#{test_rigfolder}/#{to_dir}")
|
59
|
+
end
|
60
|
+
end
|
55
61
|
end
|
56
62
|
end
|
57
63
|
|
@@ -63,7 +69,7 @@ module Jasmine::Headless
|
|
63
69
|
|
64
70
|
def to_html(files)
|
65
71
|
# Declare our test runner files
|
66
|
-
cov_files = ['/jscoverage.js', '/coverage_output_generator.js']
|
72
|
+
cov_files = ['/jscoverage.js', '/base64.min.js', '/coverage_output_generator.js']
|
67
73
|
|
68
74
|
# Add the original files, remapping to instrumented where necessary
|
69
75
|
tags = []
|
@@ -85,6 +91,8 @@ module Jasmine::Headless
|
|
85
91
|
# Attach the "in context" test runners
|
86
92
|
tags = tags + old_to_html(cov_files.map { |f| File.dirname(__FILE__)+f })
|
87
93
|
|
94
|
+
add_coverage_js_config(tags)
|
95
|
+
|
88
96
|
tags
|
89
97
|
end
|
90
98
|
|
@@ -97,5 +105,17 @@ module Jasmine::Headless
|
|
97
105
|
@sprockets_environment.append_path(File.dirname(__FILE__))
|
98
106
|
@sprockets_environment
|
99
107
|
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
# This method injects the config we have defined in Ruby that needs to be present in the JS context
|
112
|
+
def add_coverage_js_config(tags)
|
113
|
+
cov_conf = "#{Jasmine::Coverage.output_dir}/coverage_config.js"
|
114
|
+
tags << "<script type=\"text/javascript\" src=\"#{cov_conf}\"></script>"
|
115
|
+
|
116
|
+
aFile = File.new(cov_conf, "w")
|
117
|
+
aFile.write("var JasmineCoverage = { warnings: #{Jasmine::Coverage.warnings}}")
|
118
|
+
aFile.close
|
119
|
+
end
|
100
120
|
end
|
101
121
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jasmine-coverage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,56 +9,56 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
type: :runtime
|
16
16
|
name: jasmine-headless-webkit
|
17
17
|
prerelease: false
|
18
18
|
requirement: !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
19
20
|
requirements:
|
20
21
|
- - ! '>='
|
21
22
|
- !ruby/object:Gem::Version
|
22
23
|
version: 0.9.0.rc.2
|
23
|
-
none: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
25
26
|
requirements:
|
26
27
|
- - ! '>='
|
27
28
|
- !ruby/object:Gem::Version
|
28
29
|
version: 0.9.0.rc.2
|
29
|
-
none: false
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
type: :runtime
|
32
32
|
name: coffee-script-source
|
33
33
|
prerelease: false
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
35
36
|
requirements:
|
36
37
|
- - ! '>='
|
37
38
|
- !ruby/object:Gem::Version
|
38
39
|
version: '0'
|
39
|
-
none: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
41
42
|
requirements:
|
42
43
|
- - ! '>='
|
43
44
|
- !ruby/object:Gem::Version
|
44
45
|
version: '0'
|
45
|
-
none: false
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
type: :runtime
|
48
48
|
name: headless
|
49
49
|
prerelease: false
|
50
50
|
requirement: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
51
52
|
requirements:
|
52
53
|
- - ! '>='
|
53
54
|
- !ruby/object:Gem::Version
|
54
55
|
version: '0'
|
55
|
-
none: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
57
58
|
requirements:
|
58
59
|
- - ! '>='
|
59
60
|
- !ruby/object:Gem::Version
|
60
61
|
version: '0'
|
61
|
-
none: false
|
62
62
|
description:
|
63
63
|
email:
|
64
64
|
- harry@harrylascelles.com
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- lib/jasmine/coverage.rb
|
70
70
|
- lib/jasmine/coverage/version.rb
|
71
71
|
- lib/jasmine/coverage/jasmine_coverage_railtie.rb
|
72
|
+
- lib/tasks/base64.min.js
|
72
73
|
- lib/tasks/jasmine_headless_coverage_patches.rb
|
73
74
|
- lib/tasks/jasmine_coverage.rake
|
74
75
|
- lib/tasks/jscoverage.js
|
@@ -82,20 +83,20 @@ rdoc_options: []
|
|
82
83
|
require_paths:
|
83
84
|
- lib
|
84
85
|
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
85
87
|
requirements:
|
86
88
|
- - ! '>='
|
87
89
|
- !ruby/object:Gem::Version
|
88
90
|
version: '0'
|
89
|
-
none: false
|
90
91
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
91
93
|
requirements:
|
92
94
|
- - ! '>='
|
93
95
|
- !ruby/object:Gem::Version
|
94
96
|
version: '0'
|
95
|
-
none: false
|
96
97
|
requirements: []
|
97
98
|
rubyforge_project:
|
98
|
-
rubygems_version: 1.8.
|
99
|
+
rubygems_version: 1.8.25
|
99
100
|
signing_key:
|
100
101
|
specification_version: 3
|
101
102
|
summary: A blend of JS unit testing and coverage
|