4

I am looking into using capybara-webkit to do somewhat close-to-reality tests of app. This is absolutely neccessary as the app features a very rich JS-based UI and the Rails part is mostly API calls.

The question is: is there any tools to integrate into testing pipeline which could instrument Javascript code and report its coverage? The key here is the ability to integrate into testing workflow (just like rcov/simplecov) easily – I don't like the idea do it myself with jscoverage or analogue :)

Many thanks in advance.

Anton
  • 2,483
  • 2
  • 23
  • 35
  • 1
    I am also interested in this. Can't help but wonder if js coverage is possible to attain from request specs. – steve Feb 01 '13 at 05:17

2 Answers2

1

Update: Starting from JSCover version 1.05 the hacks I outlined in my previous answer are no longer needed. I've updated my answer to reflect this.

I managed to get JSCover working in the Rails + Capybara pipeline, but it did take some hacking to get it to work. I built a little rake task that:

  1. uses the rails asset pipeline to generate the scripts
  2. calls the java jar to instrument all the files and generate an empty report into a temp dir
  3. patches the jscover.js script to operate in "report mode" (simply add jscoverage_isReport=true at the end)
  4. copies the result to /public/assets so the tests pick it up without needing any changes and so the coverage report can be opened automatically in the browser

Then I added a setup task to clear out the browser's localStorage at the start of the tests and a teardown task that writes out the completed report at the end.

def setup
  unless $startup_once
    $startup_once=true
    puts 'Clearing localStorage'
    visit('/')
    page.execute_script('localStorage.removeItem("jscover");')
  end
end
def teardown
  out=page.evaluate_script("typeof(_$jscoverage)!='undefined' && jscoverage_serializeCoverageToJSON()")
  unless out.blank? then
    File.open(File.join(Rails.root,"public/assets/jscoverage.json"), 'w') {|f| f.write(out) }
  end
end

Anyway, the end result works nicely, the advantage of doing it this way is that it also works on headless browsers so it can also be included in CI.

*** Update 2: Here is a rake task that automates the steps, drop this in /lib/tasks

# Coverage testing for JavaScript
#
# Usage:
# Download JSCover from: http://tntim96.github.io/JSCover/ and move it to
#   ~/Applications/JSCover-1
# First instumentalize the javascript files:
#   rake assets:coverage
# Then run browser tests 
#   rake test
# See the results in the browser
#   http://localhost:3000/assets/jscoverage.html
# Don't forget to clean up instrumentalization afterwards:
#   rake assets:clobber
# Also don't forget to re-do instrumentalization after changing a JS file


namespace :assets do
  desc 'Instrument all the assets named in config.assets.precompile'
  task :coverage do
    Rake::Task["assets:coverage:primary"].execute
  end

  namespace :coverage do
    def jscoverage_loc;Dir.home+'/Applications/JSCover-1/';end
    def internal_instrumentalize

      config = Rails.application.config
      target=File.join(Rails.public_path,config.assets.prefix)

      environment = Sprockets::Environment.new
      environment.append_path 'app/assets/javascripts'
      `rm -rf #{tmp=File.join(Rails.root,'tmp','jscover')}`
      `mkdir #{tmp}`
      `rm -rf #{target}`
      `mkdir #{target}`

      print 'Generating assets'
      require File.join(Rails.root,'config','initializers','assets.rb')
      (%w{application.js}+config.assets.precompile.select{|f| f.is_a?(String) && f =~ /\.js$/}).each do |f|
        print '.';File.open(File.join(target,f), 'w') {|ff| ff.write(environment[f].to_s) }
      end
      puts "\nInstrumentalizing…"
      `java -Dfile.encoding=UTF-8 -jar #{jscoverage_loc}target/dist/JSCover-all.jar -fs #{target} #{tmp} #{'--no-branch' unless ENV['C1']} --local-storage`
      puts 'Copying into place…'
      `cp -R #{tmp}/ #{target}`
      `rm -rf #{tmp}`
      File.open("#{target}/jscoverage.js",'a'){|f| f.puts 'jscoverage_isReport = true' }

    end

    task :primary => %w(assets:environment) do
      unless Dir.exist?(jscoverage_loc)
        abort "Cannot find JSCover! Download from: http://tntim96.github.io/JSCover/ and put in #{jscoverage_loc}"
      end
      internal_instrumentalize
    end

  end

end
Jan M
  • 2,205
  • 21
  • 14
  • Would you mind sharing the rake task you wrote for the four steps above? – sameers Apr 18 '16 at 23:29
  • Ok, I added the rake task I use to automate this. In my real setup I've added a few switches to make coverage testing optional, I removed those for clarity – Jan M Apr 20 '16 at 07:05
  • Dang, that's nice! So, I don't understand Sprockets too well, I'll have to induce things a bit from your code, but assuming `precompile` already contains `application.js`, it seems like this task tries to instrument vendor JS files (like jQuery) as well... isn't that the case? – sameers Apr 20 '16 at 07:52
  • Ah, yes, it could be that the task also tries to instrument the vendored JS libraries (I don't have any vendored JS in my app because I link those from a CDN directly). I think it will probably work if you simply adjust the task to skip those, because missing files should fall through and get served by Sprockets unmodified. – Jan M Apr 20 '16 at 19:10
1

This has now been added to JSCover (in trunk) - the related thread at JSCover is here.

I managed to get JSCover working in the Rails + Capybara pipeline, but it did take quite a bit of hacking to get it to work

These changes are now in JSCover's trunk and will be part of version 1.0.5. There's working examples (including a Selenium IDE recorded example) and documentation in there too.

There is some additional work needed to get the branch detection to work since that uses objects that cannot be easily serialized to JSON

This is a function to do this which is used in the new code.

Anyway, the end result works nicely

I agree. This makes JSCover useable by higher level tools that don't work well with iFrames or multiple windows which are avoided by this approach. It also means code coverage can be added to existing Selenium tests with two adjustments:

  1. Make the tests run through the JSCover proxy
  2. Save the coverage report at the end of the test suite

See JSCover's documentation for more information. Version 1.0.5 containing these changes should be released in a few days.

tntim96
  • 331
  • 2
  • 2
  • Thanks, now that the new version is out I've updated my previous answer to reflect these changes – Jan M Sep 17 '13 at 12:10
  • @tntim96, do you have a config setup file in your project that you would be able to share. I'm currently working on the achieving the JS code coverage just by running Capybara specs in a rails project. Thanks in advance. – Anil Cherukuri Dec 14 '18 at 16:06