env = ENV || ENV || 'development' if env =~ /^(development|test)$/
require 'rake' require 'base64' namespace :jasmine do desc 'Runs jasmine with a coverage report' task :coverage do spec = Gem::Specification.find_by_name("jasmine-headless-webkit-firstbanco") specrunner_executable = "#{spec.gem_dir}/ext/jasmine-webkit-specrunner/jasmine-webkit-specrunner" raise "Gem jasmine-headless-webkit-firstbanco was not installed properly. A compiled executable is not available at #{specrunner_executable}" unless File.exist?(specrunner_executable) require 'jasmine-headless-webkit' # Instill our patches for jasmine-headless to work require_relative 'jasmine_headless_coverage_patches' # We use jasmine-headless-webkit, since it has excellent programmatic integration with Jasmine # But... the 'headless' part of it doesn't work on TeamCity, so we use the headless gem require 'headless' headless = Headless.new headless.start # Preprocess the JS files to add instrumentation output_dir = File.expand_path('target/jscoverage/') instrumented_dir = output_dir+'/instrumented/' FileUtils.rm_rf output_dir FileUtils.mkdir_p instrumented_dir # The reprocessing folder map. Use an environment variable if available. files_map = ENV['JS_SRC_PATH'] ? {ENV['JS_SRC_PATH'] => instrumented_dir+'public'} : { File.expand_path('app/assets/javascripts') => instrumented_dir+'app', File.expand_path('lib/assets/javascripts') => instrumented_dir+'lib', File.expand_path('public/javascripts') => instrumented_dir+'public', } # Instrument the source files into the instrumented folders files_map.keys.each do |folder| instrument(folder, files_map[folder]) # Also hoist up the eventual viewing files FileUtils.mv(Dir.glob(files_map[folder]+'/jscoverage*'), output_dir) end Jasmine::Coverage.warnings = ENV['JASMINE_COVERAGE_WARNINGS'] || 'false' Jasmine::Coverage.resources = files_map Jasmine::Coverage.output_dir = output_dir test_rig_folder = "#{Jasmine::Coverage.output_dir}/test-rig" rr_file = "#{output_dir}/rawreport.txt" puts "\nCoverage will now be run. Expect a large block of compiled coverage data. This will be processed for you into target/jscoverage (#{rr_file}).\n\n" # Check we can write to the output file begin File.open(rr_file, 'w') { |f| f.write('test-write') } File.delete(rr_file) rescue raise "There was an error writing to the report file #{rr_file}.\nDo you have permissions to do so?" end # Any options from the options.rb file in jasmine-headless-webkit can be used here. runner_options = { :reporters => [['Console'], ['File', rr_file]] } runner_options.merge!(:jasmine_config => ENV['JASMINE_CONFIG']) if ENV['JASMINE_CONFIG'] status_code = Jasmine::Headless::Runner.run(runner_options) errStr = <<-EOS
**********************************************************************************************
JSCoverage or Jasmine
Headless Webkit exited with error code: #{status_code}
This implies one of six things: 0) Your JS files had exactly zero instructions. Are they all blank or just comments? 1) The Jasmine
Headless gem failed. Run bundle exec rake jasmine:headless to see what it might be. 2) A test failed - you should be able to see the errors just above this text block (or run bundle exec rake jasmine:headless to see a simple error without coverage). 3) The sourcecode has a syntax error (which JSLint should find) 4) An error occurred in a deferred block, e.g. a setTimeout or underscore _.defer. This caused a window error which Jasmine
will never see. 5) The source files are being loaded out of sequence (so global variables are not being declared in order)
To check this, run bundle exec jasmine-headless-webkit -l to see the ordering
In any case, try running the standard jasmine command to get better errors:
bundle exec rake jasmine:headless
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 that make this difficult since it accesses the local filesystem from Javascript (but you can switch the settings off at the command line). 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.
The file can be found here: #{test_rig_folder}/jscoverage-test-rig.html
**********************************************************************************************
EOS p "Jasmine Coverage ran jasmine-headless-webkit and it returned status code #{status_code}" fail errStr if status_code == 1 || !File.exist?(rr_file) # Delete the test_rig folder if not required if ENV['JASMINE_COVERAGE_KEEP_TEST_RIG'] == 'false' FileUtils.rm_rf test_rig_folder else p "A copy of the page and files that were used as the jasmine test environment can be found here: #{test_rig_folder}" end # Obtain the console log, which includes the coverage report encoded within it contents = File.open(rr_file) { |f| f.read } # Get our Base64. json_report_enc = contents.split(/ENCODED-COVERAGE-EXPORT-STARTS:/m)[1] # Provide warnings to use warning_regex = /^CONSOLE\|\|.{1,6}WARNING.{4}(.*).{5}$/ warnings = contents.scan warning_regex if (warnings.length != 0) puts "Detected #{warnings.length} warnings:" puts warnings fail "Aborting. All lines must be covered by a test." if ENV['MUST_COVER_ALL'] else puts "No warnings detected." end # Remove the junk at the end begin json_report_enc_stripped = json_report_enc[0, json_report_enc.index("\"")] rescue => e raise "Unexpectedly got simple output from Jasmine. Are you trying to run two jasmine tests at once, even in different projects?\n\n#{contents}" end # Unpack it from Base64 json_report = Base64.decode64(json_report_enc_stripped) # Save the coverage report where the GUI html expects it to be File.open("#{output_dir}/jscoverage.json", 'w') { |f| f.write(json_report) } # Modify the jscoverage.html so it knows it is showing a report, not running a test File.open("#{output_dir}/jscoverage.js", 'a') { |f| f.write("\njscoverage_isReport = true;") } if json_report_enc.index("No Javascript was found to test coverage for").nil? # Check for coverage failure total_location = json_report_enc.index("% Total") coverage_pc = json_report_enc[total_location-3, 3].to_i conf = (ENV['JSCOVERAGE_MINIMUM'] || ENV['JASMINE_COVERAGE_MINIMUM']) fail "Coverage Fail: Javascript coverage was less than #{conf}%. It was #{coverage_pc}%." if conf && coverage_pc < conf.to_i end end def instrument folder, instrumented_sub_dir return if !File.directory? folder FileUtils.mkdir_p instrumented_sub_dir puts "Locating jscoverage..." system "which jscoverage" puts "Instrumenting JS files..." jsc_status = system "jscoverage -v #{folder} #{instrumented_sub_dir}" if jsc_status != true puts "jscoverage failed with status '#{jsc_status}'. Is jscoverage on your path? Path follows:" system "echo $PATH" puts "Result of calling jscoverage with no arguments follows:" system "jscoverage" fail "Unable to use jscoverage" end end end module Jasmine module Coverage @resources @output_dir @warnings def self.resources= resources @resources = resources end def self.resources @resources end def self.output_dir= output_dir @output_dir = output_dir end def self.output_dir @output_dir end def self.warnings= warnings @warnings = warnings end def self.warnings @warnings end end end
end