jasmine-coverage-kikuchiyo-patch 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.
@@ -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