jasmine-coverage-kikuchiyo-patch 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ module Jasmine
2
+ module Coverage
3
+ VERSION = '0.1.5'
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'coverage/version'
2
+
3
+ load 'tasks/jasmine_coverage.rake'
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Note, strictly speaking this isn't a spec.
3
+ * But we must run it like one so it occurs in the same context
4
+ * as the other tests that have run. In that way, we can
5
+ * call out in javascript and get the resulting coverage reports from the instrumented
6
+ * files.
7
+ *
8
+ * Further, when we log the results to console, the file logger captures that.
9
+ */
10
+ describe("jasmine-coverage", function () {
11
+
12
+ it("is generating a coverage report", function () {
13
+ // Output the complete line by line coverage report for capture by the file logger
14
+ generateEncodedCoverage();
15
+
16
+ // Get the simple percentages for each file
17
+ coverageForAllFiles();
18
+ });
19
+
20
+ });
21
+
22
+ String.prototype.lpad = function (padString, length) {
23
+ var str = this;
24
+ while (str.length < length)
25
+ str = padString + str;
26
+ return str;
27
+ };
28
+
29
+ function generateEncodedCoverage() {
30
+ var rv = {};
31
+ for (var file_name in window._$jscoverage) {
32
+ var jscov = window._$jscoverage[ file_name ];
33
+ var file_report = rv[ file_name ] = {
34
+ coverage:new Array(jscov.length),
35
+ source:new Array(jscov.length)
36
+ };
37
+ for (var i = 0; i < jscov.length; ++i) {
38
+ var hit_count = jscov[ i ] !== undefined ? jscov[ i ] : null;
39
+
40
+ file_report.coverage[ i ] = hit_count;
41
+ file_report.source[ i ] = jscov.source[ i ];
42
+ }
43
+ }
44
+ console.log("ENCODED-COVERAGE-EXPORT-STARTS:" + Base64.encode(JSON.stringify(rv)));
45
+ console.log("\nENCODED-COVERAGE-EXPORT-ENDS\n");
46
+ }
47
+
48
+ function coverageForAllFiles() {
49
+
50
+ var totals = { files:0, statements:0, executed:0 };
51
+
52
+ var output = "Coverage was:\n";
53
+
54
+ for (var file_name in window._$jscoverage) {
55
+ var jscov = window._$jscoverage[ file_name ];
56
+ var simple_file_coverage = coverageForFile(jscov);
57
+
58
+ totals['files']++;
59
+ totals['statements'] += simple_file_coverage['statements'];
60
+ totals['executed'] += simple_file_coverage['executed'];
61
+
62
+ var fraction = (simple_file_coverage['executed']+"/"+simple_file_coverage['statements']).lpad(' ', 10);
63
+ output += fraction + (" = " + simple_file_coverage['percentage'] + "").lpad(' ', 3) + "% for " + file_name + "\n";
64
+ }
65
+
66
+ var coverage = parseInt(100 * totals['executed'] / totals['statements']);
67
+ if (isNaN(coverage)) {
68
+ coverage = 0;
69
+ }
70
+
71
+ if (totals['statements'] === 0) {
72
+ log("No Javascript was found to test coverage for.");
73
+ } else {
74
+ output += ( totals['executed'] +"/"+totals['statements']+ " = "+ coverage + "").lpad(' ', 15) + "% Total\n";
75
+ log(output);
76
+ }
77
+
78
+ return coverage;
79
+ }
80
+
81
+
82
+ function coverageForFile(fileCC) {
83
+ var lineNumber;
84
+ var num_statements = 0;
85
+ var num_executed = 0;
86
+ var missing = [];
87
+ var length = fileCC.length;
88
+ var currentConditionalEnd = 0;
89
+ var conditionals = null;
90
+ if (fileCC.conditionals) {
91
+ conditionals = fileCC.conditionals;
92
+ }
93
+ for (lineNumber = 0; lineNumber < length; lineNumber++) {
94
+ var n = fileCC[lineNumber];
95
+
96
+ if (lineNumber === currentConditionalEnd) {
97
+ currentConditionalEnd = 0;
98
+ }
99
+ else if (currentConditionalEnd === 0 && conditionals && conditionals[lineNumber]) {
100
+ currentConditionalEnd = conditionals[lineNumber];
101
+ }
102
+
103
+ if (currentConditionalEnd !== 0) {
104
+ continue;
105
+ }
106
+
107
+ if (n === undefined || n === null) {
108
+ continue;
109
+ }
110
+
111
+ if (n === 0) {
112
+ missing.push(lineNumber);
113
+ }
114
+ else {
115
+ num_executed++;
116
+ }
117
+ num_statements++;
118
+ }
119
+
120
+ var percentage = ( num_statements === 0 ? 0 : parseInt(100 * num_executed / num_statements) );
121
+
122
+ return {
123
+ statements:num_statements,
124
+ executed:num_executed,
125
+ percentage:percentage
126
+ };
127
+ }
@@ -0,0 +1,136 @@
1
+ env = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
2
+ if env =~ /^(development|test)$/
3
+ require 'rake'
4
+ require 'base64'
5
+
6
+ namespace :jasmine do
7
+ desc 'Runs jasmine with a coverage report'
8
+ task :coverage do
9
+
10
+ require 'jasmine-headless-webkit'
11
+ # Instill our patches for jasmine-headless to work
12
+ require_relative 'jasmine_headless_coverage_patches'
13
+
14
+ # We use jasmine-headless-webkit, since it has excellent programmatic integration with Jasmine
15
+ # But... the 'headless' part of it doesn't work on TeamCity, so we use the headless gem
16
+ require 'headless'
17
+
18
+ headless = Headless.new
19
+ headless.start
20
+
21
+ # Preprocess the JS files to add instrumentation
22
+ output_dir = File.expand_path('target/jscoverage/')
23
+ instrumented_dir = output_dir+'/instrumented/'
24
+ FileUtils.rm_rf output_dir
25
+ FileUtils.mkdir_p instrumented_dir
26
+
27
+ # The reprocessing folder map
28
+ files_map = {
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
+ }
33
+
34
+ # Instrument the source files into the instrumented folders
35
+ files_map.keys.each do |folder|
36
+ instrument(folder, files_map[folder])
37
+ # Also hoist up the eventual viewing files
38
+ FileUtils.mv(Dir.glob(files_map[folder]+'/jscoverage*'), output_dir)
39
+ end
40
+
41
+ Jasmine::Coverage.resources = files_map
42
+ Jasmine::Coverage.output_dir = output_dir
43
+
44
+ puts "\nCoverage will now be run. Expect a large block of compiled coverage data. This will be processed for you into target/jscoverage.\n\n"
45
+
46
+ # Run Jasmine using the original config.
47
+ status_code = Jasmine::Headless::Runner.run(
48
+ # Any options from the options.rb file in jasmine-headless-webkit can be used here.
49
+
50
+ :reporters => [['File', "#{output_dir}/rawreport.txt"]]
51
+ )
52
+ errStr = "JSCoverage exited with error code: #{status_code}.\nThis implies one of five things:\n"
53
+ errStr = errStr +"0) Your JS files had exactly zero instructions. Are they all blank or just comments?\n"
54
+ errStr = errStr +"1) A test failed (run bundle exec jasmine:headless to see a better error)\n"
55
+ errStr = errStr +"2) The sourcecode has a syntax error (which JSLint should find)\n"
56
+ errStr = errStr +"3) An error occurred in a deferred block, eg a setTimeout or underscore _.defer. This caused a window error which Jasmine will never see.\n"
57
+ errStr = errStr +"4) The source files are being loaded out of sequence (so global variables are not being declared in order)\n"
58
+ errStr = errStr +" To check this, run bundle exec jasmine-headless-webkit -l to see the ordering\n"
59
+ errStr = errStr +"\nIn any case, try running the standard jasmine command to get better errors:\n\nbundle exec jasmine:headless\n"
60
+ errStr = errStr +"\nFinally, try opening the testrig in firefox to see the tests run in a browser and get a stacktrace. "
61
+ errStr = errStr +"Chrome has strict security settings that make this difficult since it accesses the local filesystem from Javascript (but you can switch the settings off at the command line)\n\n"
62
+ errStr = errStr +"\n**********************************************************************************************\n"
63
+ errStr = errStr +"\nThe 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.\n"
64
+ errStr = errStr +"\nThe file can be found here: #{Jasmine::Coverage.output_dir}/testrig/jscoverage-test-rig.html\n"
65
+ errStr = errStr +"\n**********************************************************************************************\n"
66
+ fail errStr if status_code == 1
67
+
68
+ # Obtain the console log, which includes the coverage report encoded within it
69
+ contents = File.open("#{output_dir}/rawreport.txt") { |f| f.read }
70
+ # Get our Base64.
71
+ json_report_enc = contents.split(/ENCODED-COVERAGE-EXPORT-STARTS:/m)[1]
72
+ # Remove the junk at the end
73
+ json_report_enc_stripped = json_report_enc[0, json_report_enc.index("\"")]
74
+
75
+ # Unpack it from Base64
76
+ json_report = Base64.decode64(json_report_enc_stripped)
77
+
78
+ # Save the coverage report where the GUI html expects it to be
79
+ File.open("#{output_dir}/jscoverage.json", 'w') { |f| f.write(json_report) }
80
+
81
+ # Modify the jscoverage.html so it knows it is showing a report, not running a test
82
+ File.open("#{output_dir}/jscoverage.js", 'a') { |f| f.write("\njscoverage_isReport = true;") }
83
+
84
+ if json_report_enc.index("No Javascript was found to test coverage for").nil?
85
+ # Check for coverage failure
86
+ total_location = json_report_enc.index("% Total")
87
+ coverage_pc = json_report_enc[total_location-3, 3].to_i
88
+
89
+ conf = (ENV['JSCOVERAGE_MINIMUM'] || ENV['JASMINE_COVERAGE_MINIMUM'])
90
+ fail "Coverage Fail: Javascript coverage was less than #{conf}%. It was #{coverage_pc}%." if conf && coverage_pc < conf.to_i
91
+ end
92
+
93
+ end
94
+
95
+ def instrument folder, instrumented_sub_dir
96
+ return if !File.directory? folder
97
+ FileUtils.mkdir_p instrumented_sub_dir
98
+ puts "Locating jscoverage..."
99
+ system "which jscoverage"
100
+ puts "Instrumenting JS files..."
101
+ jsc_status = system "jscoverage -v #{folder} #{instrumented_sub_dir}"
102
+ if jsc_status != true
103
+ puts "jscoverage failed with status '#{jsc_status}'. Is jscoverage on your path? Path follows:"
104
+ system "echo $PATH"
105
+ puts "Result of calling jscoverage with no arguments follows:"
106
+ system "jscoverage"
107
+ fail "Unable to use jscoverage"
108
+ end
109
+ end
110
+ end
111
+
112
+ module Jasmine
113
+ module Coverage
114
+ @resources
115
+
116
+ def self.resources= resources
117
+ @resources = resources
118
+ end
119
+
120
+ def self.resources
121
+ @resources
122
+ end
123
+
124
+ @output_dir
125
+
126
+ def self.output_dir= output_dir
127
+ @output_dir = output_dir
128
+ end
129
+
130
+ def self.output_dir
131
+ @output_dir
132
+ end
133
+ end
134
+ end
135
+
136
+ end
@@ -0,0 +1,99 @@
1
+ # This file holds the monkeypatches to open up jasmine headless for jasmine coverage.
2
+
3
+
4
+ # This patch writes out a copy of the file that was loaded into the JSCoverage context for testing.
5
+ # You can look at it to see if it included all the files and tests you expect.
6
+ require 'jasmine/headless/template_writer'
7
+ module Jasmine::Headless
8
+ class TemplateWriter
9
+ alias old_write :write
10
+
11
+ def write
12
+ ret = old_write
13
+ str = File.open(all_tests_filename, "rb").read
14
+
15
+ testrigfolder = Jasmine::Coverage.output_dir+"/testrig"
16
+ FileUtils.mkdir_p testrigfolder
17
+
18
+ p "\nCopying all view files and potential javascript fixture folders so the JS has access to the html fixtures.\n"
19
+ FileUtils.copy_entry("#{Jasmine::Coverage.output_dir}/../../spec", "#{testrigfolder}/spec")
20
+ FileUtils.copy_entry("#{Jasmine::Coverage.output_dir}/../../app", "#{testrigfolder}/app")
21
+ FileUtils.mkdir_p "#{testrigfolder}/target/fixtures"
22
+ FileUtils.copy_entry("#{Jasmine::Coverage.output_dir}/../fixtures", "#{testrigfolder}/target/fixtures")
23
+ FileUtils.mkdir_p "#{testrigfolder}/target/views"
24
+ FileUtils.copy_entry("#{Jasmine::Coverage.output_dir}/../views", "#{testrigfolder}/target/views")
25
+
26
+ jss = str.scan(/<script type="text\/javascript" src="(.*)"><\/script>/)
27
+ jss << str.scan(/<link rel="stylesheet" href="(.*)" type="text\/css" \/>/)
28
+ jss << str.scan(/\.coffee\.js'\] = '(.*)';<\/script>/)
29
+ jss.flatten!
30
+ jss.each { |s|
31
+ js = File.basename(s)
32
+ str.sub!(s, js)
33
+ if File.exists?("#{testrigfolder}/#{js}") && js != 'index.js'
34
+ s = "\n\n*******************************************************************\n"
35
+ s = s + "Cannot copy file '#{js}' into jasmine coverage test rig folder.\n"
36
+ s = s + "There is already another file of that name. You either have two files with the same name (but in different paths)\n"
37
+ s = s + "or your filename is the same as that from a third party vendor.\n"
38
+ s = s + "*******************************************************************\n\n"
39
+ raise s
40
+ end
41
+ FileUtils.cp(s, testrigfolder)
42
+ }
43
+
44
+ outfile = "#{testrigfolder}/jscoverage-test-rig.html"
45
+ aFile = File.new(outfile, "w")
46
+ aFile.write(str)
47
+ aFile.close
48
+
49
+ puts "A copy of the complete page that was used as the test environment can be found here:"
50
+ puts "#{outfile}"
51
+ ret
52
+ end
53
+ end
54
+ end
55
+
56
+ # Here we patch the resource handler to output the location of our instrumented files
57
+ module Jasmine::Headless
58
+ class FilesList
59
+
60
+ alias old_to_html :to_html
61
+
62
+ def to_html(files)
63
+ # Declare our test runner files
64
+ cov_files = ['/jscoverage.js', '/coverage_output_generator.js']
65
+
66
+ # Add the original files, remapping to instrumented where necessary
67
+ tags = []
68
+ (old_to_html files).each do |path|
69
+ files_map = Jasmine::Coverage.resources
70
+ files_map.keys.each do |folder|
71
+ path = path.sub(folder, files_map[folder])
72
+
73
+ # Here we must check the supplied config hasn't pulled in our jscoverage runner file.
74
+ # If it has, the tests will fire too early, capturing only minimal coverage
75
+ if cov_files.select { |f| path.include?(f) }.length > 0
76
+ fail "Assets defined by jasmine.yml must not include any of #{cov_files}: #{path}"
77
+ end
78
+
79
+ end
80
+ tags << path
81
+ end
82
+
83
+ # Attach the "in context" test runners
84
+ tags = tags + old_to_html(cov_files.map { |f| File.dirname(__FILE__)+f })
85
+
86
+ tags
87
+ end
88
+
89
+ alias old_sprockets_environment :sprockets_environment
90
+
91
+ def sprockets_environment
92
+ return @sprockets_environment if @sprockets_environment
93
+ old_sprockets_environment
94
+ # Add the location of our jscoverage.js
95
+ @sprockets_environment.append_path(File.dirname(__FILE__))
96
+ @sprockets_environment
97
+ end
98
+ end
99
+ end