├── .gitignore ├── .jshintignore ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── coverage.json.md ├── download-escodegen-browser.sh ├── generate-pages.sh ├── ignoring-code-for-coverage.md ├── index.js ├── lib ├── assets │ ├── base.css │ ├── sort-arrow-sprite.png │ ├── sorter.js │ └── vendor │ │ ├── prettify.css │ │ └── prettify.js ├── cli.js ├── collector.js ├── command │ ├── check-coverage.js │ ├── common │ │ └── run-with-cover.js │ ├── cover.js │ ├── help.js │ ├── index.js │ ├── instrument.js │ ├── report.js │ └── test.js ├── config.js ├── hook.js ├── instrumenter.js ├── object-utils.js ├── register-plugins.js ├── report │ ├── clover.js │ ├── cobertura.js │ ├── common │ │ └── defaults.js │ ├── html.js │ ├── index.js │ ├── json-summary.js │ ├── json.js │ ├── lcov.js │ ├── lcovonly.js │ ├── none.js │ ├── teamcity.js │ ├── templates │ │ ├── foot.txt │ │ └── head.txt │ ├── text-lcov.js │ ├── text-summary.js │ └── text.js ├── reporter.js ├── store │ ├── fslookup.js │ ├── index.js │ ├── memory.js │ └── tmp.js └── util │ ├── factory.js │ ├── file-matcher.js │ ├── file-writer.js │ ├── help-formatter.js │ ├── input-error.js │ ├── insertion-text.js │ ├── meta.js │ ├── tree-summarizer.js │ ├── writer.js │ └── yui-load-hook.js ├── misc ├── ast │ ├── assign.js │ ├── block-label.js │ ├── cond.js │ ├── defun-compact.js │ ├── defun.js │ ├── do-statement.js │ ├── dot.js │ ├── eq.js │ ├── expr.js │ ├── for-block.js │ ├── for-compact.js │ ├── for-multi.js │ ├── for-statement.js │ ├── forin-block.js │ ├── forin-compact.js │ ├── forin-statement.js │ ├── func.js │ ├── if-block.js │ ├── if-compact.js │ ├── if-only.js │ ├── if-statement.js │ ├── incr-slice.js │ ├── label.js │ ├── nested-if.js │ ├── pfcall.js │ ├── preamble.js │ ├── switch-statement.js │ ├── try-block.js │ ├── try-statement.js │ ├── while-block.js │ ├── while-compact.js │ ├── while-for.js │ └── while-statement.js ├── config │ ├── istanbul-config-alt.json │ └── istanbul-config.json └── samples │ └── coverage.js ├── package.json ├── test ├── browser │ ├── support │ │ ├── index.html │ │ ├── phantom-test.client.js │ │ ├── server.js │ │ └── vendor │ │ │ └── yui-support.js │ └── test-browser-instrumentation.js ├── cli-helper.js ├── cli │ ├── package.json │ ├── sample-project │ │ ├── .gitignore │ │ ├── amd │ │ │ ├── ipsum.js │ │ │ └── lorem.js │ │ ├── config-check-each.istanbul.yml │ │ ├── config-check-global.istanbul.yml │ │ ├── config-check-mixed.istanbul.yml │ │ ├── config.istanbul.yml │ │ ├── includeAllSources │ │ │ ├── unloadedFile.js │ │ │ └── unloadedFileWithFunctionDeclaration.js │ │ ├── lib │ │ │ ├── bar.js │ │ │ ├── foo.js │ │ │ └── util │ │ │ │ ├── bad.js │ │ │ │ ├── es-module.js │ │ │ │ ├── generate-names.js │ │ │ │ └── unused.js │ │ ├── node_modules │ │ │ ├── dependency │ │ │ │ └── index.js │ │ │ └── post-require │ │ │ │ ├── hook.js │ │ │ │ └── package.json │ │ ├── test │ │ │ ├── amd-run.js │ │ │ ├── global-leak.js │ │ │ └── run.js │ │ └── vendor │ │ │ └── dummy_vendor_lib.js │ ├── test-base-cli.js │ ├── test-check-coverage-command.js │ ├── test-clover-report.js │ ├── test-cobertura-report.js │ ├── test-cover-command.js │ ├── test-html-report.js │ ├── test-include-pid.js │ ├── test-instrument-command.js │ ├── test-json-report.js │ ├── test-json-summary-report.js │ ├── test-lcov-report.js │ ├── test-lcovonly-report.js │ ├── test-lots-of-files.js │ ├── test-none-report.js │ ├── test-report-command.js │ ├── test-teamcity-report.js │ ├── test-test-command.js │ └── test-text-lcov-report.js ├── common.js ├── es6.js ├── helper.js ├── instrumentation │ ├── test-do.js │ ├── test-es6-arrow-fn.js │ ├── test-es6-export.js │ ├── test-es6-forof.js │ ├── test-es6-import.js │ ├── test-es6-super.js │ ├── test-es6-yield.js │ ├── test-expressions.js │ ├── test-for.js │ ├── test-forin.js │ ├── test-functions.js │ ├── test-if-with-hints.js │ ├── test-if.js │ ├── test-locations.js │ ├── test-misc.js │ ├── test-statement-with-hints.js │ ├── test-statement.js │ ├── test-strict.js │ ├── test-switch.js │ ├── test-try.js │ ├── test-while.js │ └── test-with.js ├── loader.js ├── other │ ├── config-data │ │ ├── .istanbul.yml │ │ └── cfg.json │ ├── data-complete-copy │ │ ├── baz.js │ │ ├── fixture.xml │ │ ├── foo.js │ │ ├── myfile1 │ │ ├── myfile2 │ │ └── subdir │ │ │ └── x.css │ ├── data │ │ ├── bar.es6 │ │ ├── baz.js │ │ ├── foo.js │ │ └── matcher │ │ │ ├── .gitignore │ │ │ ├── general │ │ │ ├── .gitignore │ │ │ ├── general.js │ │ │ └── node_modules │ │ │ │ └── mod-file.js │ │ │ ├── lib │ │ │ └── lib-top.js │ │ │ ├── node_modules │ │ │ ├── dep │ │ │ │ └── mod-file.js │ │ │ └── mod-file.js │ │ │ └── top.js │ ├── test-collector.js │ ├── test-command-xface.js │ ├── test-config.js │ ├── test-file-writer.js │ ├── test-help-formatter.js │ ├── test-hook.js │ ├── test-index-xface.js │ ├── test-input-error.js │ ├── test-insertion-text.js │ ├── test-matcher.js │ ├── test-object-utils.js │ ├── test-store.js │ └── test-summarizer.js ├── run-again.js └── run.js ├── yui-coverage-comparison.md └── yuidoc.json /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | .DS_Store 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | 11 | pids 12 | logs 13 | results 14 | 15 | node_modules/ 16 | bower_components/ 17 | .idea/ 18 | html-report/ 19 | build/ 20 | public/ 21 | test/other/output/ 22 | test/cli/output/ 23 | 24 | npm-debug.log 25 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | lib/assets/vendor 2 | test/cli/sample-project 3 | test/cli/sample-project-link 4 | test/browser/support/vendor 5 | 6 | 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "camelcase": false, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "forin": true, 7 | "freeze": true, 8 | "immed": true, 9 | "latedef": true, 10 | "maxlen": 150, 11 | "newcap": true, 12 | "noarg": true, 13 | "nonbsp": true, 14 | "nonew": true, 15 | "plusplus": true, 16 | "trailing": true, 17 | "undef": true, 18 | "unused": true, 19 | 20 | "strict": false, 21 | 22 | "asi": false, 23 | "boss": false, 24 | "debug": false, 25 | "eqnull": false, 26 | "esnext": false, 27 | "evil": false, 28 | "expr": false, 29 | "funcscope": false, 30 | "globalstrict": false, 31 | "iterator": false, 32 | "lastsemic": false, 33 | "laxbreak": true, 34 | "laxcomma": false, 35 | "loopfunc": false, 36 | "multistr": false, 37 | "notypeof": false, 38 | "proto": false, 39 | "scripturl": false, 40 | "smarttabs": false, 41 | "shadow": false, 42 | "sub": false, 43 | "supernew": false, 44 | "validthis": false, 45 | "noyield": false, 46 | 47 | "browser": true, 48 | "node": true, 49 | 50 | "nomen": false, 51 | "onevar": true, 52 | "passfail": false, 53 | "white": false 54 | } 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "0.10" 5 | - "0.12" 6 | 7 | sudo: false 8 | 9 | branches: 10 | except: 11 | - gh-pages 12 | 13 | script: 14 | - npm test --cover 15 | 16 | after_script: 17 | - if [[ `node --version` == *v0.12* ]]; then cat ./build/coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js; fi 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2012 Yahoo! Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Yahoo! Inc. nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL YAHOO! INC. BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DEPRECATED 2 | This package was created a long time ago before istanbul had direct transpiler support. I suggest looking at https://istanbul.js.org/ for an updated and maintained solution for code coverage. 3 | 4 | ## babel-istanbul - [babel](https://github.com/babel/babel) + [istanbul](https://github.com/gotwarlost/istanbul) 5 | 6 | * [Features](#features) 7 | * [Getting started](#getting-started) 8 | 9 | ### Features 10 | 11 | * This package handles coverage for babel generated code by reconciling babel's output and its source map. 12 | * babel-istanbul is drop-in replacement for [istanbul](https://github.com/gotwarlost/istanbul), as it is a fork of istanbul with babel compilation inserted into the instrumentation layer. 13 | 14 | ### Getting started 15 | 16 | $ npm install babel-istanbul 17 | 18 | * babel-istanbul is run exactly like istanbul. For specifics on running istanbul, see [istanbul's README](https://github.com/gotwarlost/istanbul/blob/master/README.md). 19 | -------------------------------------------------------------------------------- /coverage.json.md: -------------------------------------------------------------------------------- 1 | # Format of coverage.json 2 | 3 | `coverage.json` contains a report object, which is a hash where keys are file names (absolute 4 | paths), and values are coverage data for that file (the result of 5 | `json.stringify(collector.fileCoverageFor(filename))`) Each entry consists of: 6 | 7 | * `path` - The path to the file. This is an absolute path, and should be the same as the 8 | key in the report object. 9 | * `s` - Hash of statement counts, where keys as statement IDs. 10 | * `b` - Hash of branch counts, where keys are branch IDs and values are arrays of counts. 11 | For an if statement, the value would have two counts; one for the if, and one for the 12 | else. Switch statements would have an array of values for each case. 13 | * `f` - Hash of function counts, where keys are function IDs. 14 | * `fnMap` - Hash of functions where keys are function IDs, and values are `{name, line, loc, skip}`, 15 | where `name` is the name of the function, `line` is the line the function is declared on, 16 | and `loc` is the `Location` of the function declaration (just the declaration, not the entire 17 | function body - see 'Location Objects' below.) If `skip` is present and true, then this 18 | indicates that this function was ignored by a `### instabul ignore ... ###` pragma. Note that 19 | if a function is not ignored the `skip` field will be missing entirely. 20 | * `statementMap` - Hash where keys are statement IDs, and values are `Location` objects for each 21 | statement. The `Location` for a function definition is really an assignment, and should 22 | include the entire function. In addition to the normal location object fields, a 23 | `statementMap` entry can also have an optional `skip` field. 24 | * `branchMap` - Hash where keys are branch IDs, and values are `{line, type, locations}` objects. 25 | `line` is the line the branch starts on. `type` is the type of the branch (e.g. "if", "switch"). 26 | `locations` is an array of `Location` objects, one for each possible outcome of the branch. 27 | Note for an `if` statement where there is no `else` clause, there will still be two `locations` 28 | generated. Istanbul does *not* generate coverage for the `default` case of a switch statement 29 | if `default` is not explicitly present in the source code. 30 | * `l` - Hash of line counts, where keys are the line number. 31 | 32 | `locations` for an if statement are always 0-length and located at the start of the `if` (even 33 | the location for the "else"). For a `switch` statement, `locations` start at the start of the 34 | `case` statement and go to the end of the line before the next case statement (note Istanbul 35 | does nothing clever here if a `case` is missing a `break`.) Each location in `locations` can 36 | also optionally have a `skip: true` field to indicate that this branch was ignored. 37 | 38 | IDs used in the fnMap, statementMap, and branchMap are sequential integers, starting at 1. 39 | 40 | ## Location Objects 41 | 42 | Location objects are a `{start: {line, column}, end: {line, column}}` object that describes 43 | the start and end of a piece of code. Note that `line` is 1-based, but `column` is 0-based. 44 | -------------------------------------------------------------------------------- /download-escodegen-browser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ESCG_DIR=node_modules/escodegen 4 | ESCG_VERSION=`grep '"version"' ${ESCG_DIR}/package.json | awk '{print $2}' | sed 's/[",]//g'` 5 | OUT_FILE=${ESCG_DIR}/escodegen.browser.min.js 6 | if [ ! -f ${OUT_FILE} ] 7 | then 8 | set -v 9 | rm -rf __escodegen_clone__ 10 | git clone --branch ${ESCG_VERSION} https://github.com/estools/escodegen.git __escodegen_clone__ 11 | cd __escodegen_clone__ 12 | 13 | # Temporarily ignore missing package, see #489 14 | perl -i -ne '/esprima\-moz/ or print' package.json 15 | 16 | npm i && npm run build-min 17 | mv escodegen.browser.min.js ../${OUT_FILE} 18 | cd - 19 | rm -rf __escodegen_clone__ 20 | fi 21 | 22 | -------------------------------------------------------------------------------- /generate-pages.sh: -------------------------------------------------------------------------------- 1 | set -ex 2 | export PAGES_DIR=../istanbul-pages 3 | npm test --coverage 4 | mkdir -p public/apidocs 5 | yuidoc . 6 | rsync -rvt ./public/apidocs/ ${PAGES_DIR}/public/apidocs/ 7 | rsync -rvt ./build/coverage/ ${PAGES_DIR}/public/coverage 8 | 9 | -------------------------------------------------------------------------------- /lib/assets/sort-arrow-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffrifwald/babel-istanbul/ced0465040d0dc464282012207ba3bc456634b74/lib/assets/sort-arrow-sprite.png -------------------------------------------------------------------------------- /lib/assets/vendor/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /lib/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* 4 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 5 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 6 | */ 7 | 8 | 9 | var async = require('async'), 10 | Command = require('./command'), 11 | inputError = require('./util/input-error'), 12 | exitProcess = process.exit; //hold a reference to original process.exit so that we are not affected even when a test changes it 13 | 14 | require('./register-plugins'); 15 | 16 | function findCommandPosition(args) { 17 | var i; 18 | 19 | for (i = 0; i < args.length; i += 1) { 20 | if (args[i].charAt(0) !== '-') { 21 | return i; 22 | } 23 | } 24 | 25 | return -1; 26 | } 27 | 28 | function exit(ex, code) { 29 | // flush output for Node.js Windows pipe bug 30 | // https://github.com/joyent/node/issues/6247 is just one bug example 31 | // https://github.com/visionmedia/mocha/issues/333 has a good discussion 32 | var streams = [process.stdout, process.stderr]; 33 | async.forEach(streams, function (stream, done) { 34 | // submit a write request and wait until it's written 35 | stream.write('', done); 36 | }, function () { 37 | if (ex) { 38 | if (typeof ex === 'string') { 39 | console.error(ex); 40 | exitProcess(1); 41 | } else { 42 | throw ex; // turn it into an uncaught exception 43 | } 44 | } else { 45 | exitProcess(code); 46 | } 47 | }); 48 | } 49 | 50 | function errHandler (ex) { 51 | if (!ex) { return; } 52 | if (!ex.inputError) { 53 | // exit with exception stack trace 54 | exit(ex); 55 | } else { 56 | //don't print nasty traces but still exit(1) 57 | console.error(ex.message); 58 | console.error('Try "babel-istanbul help" for usage'); 59 | exit(null, 1); 60 | } 61 | } 62 | 63 | function runCommand(args, callback) { 64 | var pos = findCommandPosition(args), 65 | command, 66 | commandArgs, 67 | commandObject; 68 | 69 | if (pos < 0) { 70 | return callback(inputError.create('Need a command to run')); 71 | } 72 | 73 | commandArgs = args.slice(0, pos); 74 | command = args[pos]; 75 | commandArgs.push.apply(commandArgs, args.slice(pos + 1)); 76 | 77 | try { 78 | commandObject = Command.create(command); 79 | } catch (ex) { 80 | errHandler(inputError.create(ex.message)); 81 | return; 82 | } 83 | commandObject.run(commandArgs, errHandler); 84 | } 85 | 86 | function runToCompletion(args) { 87 | runCommand(args, errHandler); 88 | } 89 | 90 | /* istanbul ignore if: untestable */ 91 | if (require.main === module) { 92 | var args = Array.prototype.slice.call(process.argv, 2); 93 | runToCompletion(args); 94 | } 95 | 96 | module.exports = { 97 | runToCompletion: runToCompletion 98 | }; 99 | 100 | -------------------------------------------------------------------------------- /lib/command/cover.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var runWithCover = require('./common/run-with-cover'), 7 | util = require('util'), 8 | Command = require('./index'); 9 | 10 | function CoverCommand() { 11 | Command.call(this); 12 | } 13 | 14 | CoverCommand.TYPE = 'cover'; 15 | util.inherits(CoverCommand, Command); 16 | 17 | Command.mix(CoverCommand, { 18 | synopsis: function () { 19 | return "transparently adds coverage information to a node command. Saves coverage.json and reports at the end of execution"; 20 | }, 21 | 22 | usage: function () { 23 | runWithCover.usage(this.toolName(), this.type()); 24 | }, 25 | 26 | run: function (args, callback) { 27 | runWithCover.run(args, this.type(), true, callback); 28 | } 29 | }); 30 | 31 | 32 | module.exports = CoverCommand; 33 | 34 | -------------------------------------------------------------------------------- /lib/command/help.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var Command = require('./index.js'), 7 | util = require('util'), 8 | formatOption = require('../util/help-formatter').formatOption, 9 | VERSION = require('../../index').VERSION, 10 | configuration = require('../config'), 11 | yaml = require('js-yaml'), 12 | formatPara = require('../util/help-formatter').formatPara; 13 | 14 | function showConfigHelp(toolName) { 15 | 16 | console.error('\nConfiguring ' + toolName); 17 | console.error('===================='); 18 | console.error('\n' + 19 | formatPara(toolName + ' can be configured globally using a .istanbul.yml YAML file ' + 20 | 'at the root of your source tree. Every command also accepts a --config= argument to ' + 21 | 'customize its location per command. The alternate config file can be in YAML, JSON or node.js ' + 22 | '(exporting the config object).')); 23 | console.error('\n' + 24 | formatPara('The config file currently has four sections for instrumentation, reporting, hooks, ' + 25 | 'and checking. Note that certain commands (like `cover`) use information from multiple sections.')); 26 | console.error('\n' + 27 | formatPara('Keys in the config file usually correspond to command line parameters with the same name. ' + 28 | 'The verbose option for every command shows you the exact configuration used. See the api ' + 29 | 'docs for an explanation of each key.')); 30 | 31 | console.error('\n' + 32 | formatPara('You only need to specify the keys that you want to override. Your overrides will be merged ' + 33 | 'with the default config.')); 34 | console.error('\nThe default configuration is as follows:\n'); 35 | console.error(yaml.safeDump(configuration.defaultConfig(), { indent: 4, flowLevel: 3 })); 36 | console.error('\n' + 37 | formatPara('The `watermarks` section does not have a command line equivalent. It allows you to set up ' + 38 | 'low and high watermark percentages for reporting. These are honored by all reporters that colorize ' + 39 | 'their output based on low/ medium/ high coverage.')); 40 | console.error('\n' + 41 | formatPara('The `reportConfig` section allows you to configure each report format independently ' + 42 | 'and has no command-line equivalent either.')); 43 | console.error('\n' + 44 | formatPara('The `check` section configures minimum threshold enforcement for coverage results. ' + 45 | '`global` applies to all files together and `each` on a per-file basis. A list of files can ' + 46 | 'be excluded from enforcement relative to root via the `exclude` property.')); 47 | console.error(''); 48 | } 49 | 50 | function HelpCommand() { 51 | Command.call(this); 52 | } 53 | 54 | HelpCommand.TYPE = 'help'; 55 | util.inherits(HelpCommand, Command); 56 | 57 | Command.mix(HelpCommand, { 58 | synopsis: function () { 59 | return "shows help"; 60 | }, 61 | 62 | usage: function () { 63 | 64 | console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' config | \n'); 65 | console.error('`config` provides help with istanbul configuration\n'); 66 | console.error('Available commands are:\n'); 67 | 68 | var commandObj; 69 | Command.getCommandList().forEach(function (cmd) { 70 | commandObj = Command.create(cmd); 71 | console.error(formatOption(cmd, commandObj.synopsis())); 72 | console.error("\n"); 73 | }); 74 | console.error("Command names can be abbreviated as long as the abbreviation is unambiguous"); 75 | console.error(this.toolName() + ' version:' + VERSION); 76 | console.error("\n"); 77 | }, 78 | run: function (args, callback) { 79 | var command; 80 | if (args.length === 0) { 81 | this.usage(); 82 | } else { 83 | if (args[0] === 'config') { 84 | showConfigHelp(this.toolName()); 85 | } else { 86 | try { 87 | command = Command.create(args[0]); 88 | command.usage('istanbul', Command.resolveCommandName(args[0])); 89 | } catch (ex) { 90 | console.error('Invalid command: ' + args[0]); 91 | this.usage(); 92 | } 93 | } 94 | } 95 | return callback(); 96 | } 97 | }); 98 | 99 | 100 | module.exports = HelpCommand; 101 | 102 | 103 | -------------------------------------------------------------------------------- /lib/command/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var Factory = require('../util/factory'), 7 | factory = new Factory('command', __dirname, true); 8 | 9 | function Command() {} 10 | // add register, create, mix, loadAll, getCommandList, resolveCommandName to the Command object 11 | factory.bindClassMethods(Command); 12 | 13 | Command.prototype = { 14 | toolName: function () { 15 | return require('../util/meta').NAME; 16 | }, 17 | 18 | type: function () { 19 | return this.constructor.TYPE; 20 | }, 21 | synopsis: /* istanbul ignore next: base method */ function () { 22 | return "the developer has not written a one-line summary of the " + this.type() + " command"; 23 | }, 24 | usage: /* istanbul ignore next: base method */ function () { 25 | console.error("the developer has not provided a usage for the " + this.type() + " command"); 26 | }, 27 | run: /* istanbul ignore next: abstract method */ function (args, callback) { 28 | return callback(new Error("run: must be overridden for the " + this.type() + " command")); 29 | } 30 | }; 31 | 32 | module.exports = Command; 33 | 34 | -------------------------------------------------------------------------------- /lib/command/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var runWithCover = require('./common/run-with-cover'), 7 | util = require('util'), 8 | Command = require('./index'); 9 | 10 | function TestCommand() { 11 | Command.call(this); 12 | } 13 | 14 | TestCommand.TYPE = 'test'; 15 | util.inherits(TestCommand, Command); 16 | 17 | Command.mix(TestCommand, { 18 | synopsis: function () { 19 | return "cover a node command only when npm_config_coverage is set. Use in an `npm test` script for conditional coverage"; 20 | }, 21 | 22 | usage: function () { 23 | runWithCover.usage(this.toolName(), this.type()); 24 | }, 25 | 26 | run: function (args, callback) { 27 | runWithCover.run(args, this.type(), !!process.env.npm_config_coverage, callback); 28 | } 29 | }); 30 | 31 | module.exports = TestCommand; 32 | -------------------------------------------------------------------------------- /lib/register-plugins.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 4 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 5 | */ 6 | var Store = require('./store'), 7 | Report = require('./report'), 8 | Command = require('./command'); 9 | 10 | Store.loadAll(); 11 | Report.loadAll(); 12 | Command.loadAll(); 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /lib/report/common/defaults.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var Report = require('../index'); 7 | var supportsColor = require('supports-color'); 8 | 9 | module.exports = { 10 | watermarks: function () { 11 | return { 12 | statements: [ 50, 80 ], 13 | lines: [ 50, 80 ], 14 | functions: [ 50, 80], 15 | branches: [ 50, 80 ] 16 | }; 17 | }, 18 | 19 | classFor: function (type, metrics, watermarks) { 20 | var mark = watermarks[type], 21 | value = metrics[type].pct; 22 | return value >= mark[1] ? 'high' : value >= mark[0] ? 'medium' : 'low'; 23 | }, 24 | 25 | colorize: function (str, clazz) { 26 | /* istanbul ignore if: untestable in batch mode */ 27 | var colors = { 28 | low: '31;1', 29 | medium: '33;1', 30 | high: '32;1' 31 | }; 32 | 33 | if (supportsColor && colors[clazz]) { 34 | return '\u001b[' + colors[clazz] + 'm' + str + '\u001b[0m'; 35 | } 36 | return str; 37 | }, 38 | 39 | defaultReportConfig: function () { 40 | var cfg = {}; 41 | Report.getReportList().forEach(function (type) { 42 | var rpt = Report.create(type), 43 | c = rpt.getDefaultConfig(); 44 | if (c) { 45 | cfg[type] = c; 46 | } 47 | }); 48 | return cfg; 49 | } 50 | }; 51 | 52 | -------------------------------------------------------------------------------- /lib/report/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var util = require('util'), 7 | EventEmitter = require('events').EventEmitter, 8 | Factory = require('../util/factory'), 9 | factory = new Factory('report', __dirname, false); 10 | /** 11 | * An abstraction for producing coverage reports. 12 | * This class is both the base class as well as a factory for `Report` implementations. 13 | * All reports are event emitters and are expected to emit a `done` event when 14 | * the report writing is complete. 15 | * 16 | * See also the `Reporter` class for easily producing multiple coverage reports 17 | * with a single call. 18 | * 19 | * Usage 20 | * ----- 21 | * 22 | * var Report = require('istanbul').Report, 23 | * report = Report.create('html'), 24 | * collector = new require('istanbul').Collector; 25 | * 26 | * collector.add(coverageObject); 27 | * report.on('done', function () { console.log('done'); }); 28 | * report.writeReport(collector); 29 | * 30 | * @class Report 31 | * @module report 32 | * @main report 33 | * @constructor 34 | * @protected 35 | * @param {Object} options Optional. The options supported by a specific store implementation. 36 | */ 37 | function Report(/* options */) { 38 | EventEmitter.call(this); 39 | } 40 | 41 | util.inherits(Report, EventEmitter); 42 | 43 | //add register, create, mix, loadAll, getReportList as class methods 44 | factory.bindClassMethods(Report); 45 | 46 | /** 47 | * registers a new report implementation. 48 | * @method register 49 | * @static 50 | * @param {Function} constructor the constructor function for the report. This function must have a 51 | * `TYPE` property of type String, that will be used in `Report.create()` 52 | */ 53 | /** 54 | * returns a report implementation of the specified type. 55 | * @method create 56 | * @static 57 | * @param {String} type the type of report to create 58 | * @param {Object} opts Optional. Options specific to the report implementation 59 | * @return {Report} a new store of the specified type 60 | */ 61 | /** 62 | * returns the list of available reports as an array of strings 63 | * @method getReportList 64 | * @static 65 | * @return an array of supported report formats 66 | */ 67 | 68 | var proto = { 69 | /** 70 | * returns a one-line summary of the report 71 | * @method synopsis 72 | * @return {String} a description of what the report is about 73 | */ 74 | synopsis: function () { 75 | throw new Error('synopsis must be overridden'); 76 | }, 77 | /** 78 | * returns a config object that has override-able keys settable via config 79 | * @method getDefaultConfig 80 | * @return {Object|null} an object representing keys that can be overridden via 81 | * the istanbul configuration where the values are the defaults used when 82 | * not specified. A null return implies no config attributes 83 | */ 84 | getDefaultConfig: function () { 85 | return null; 86 | }, 87 | /** 88 | * writes the report for a set of coverage objects added to a collector. 89 | * @method writeReport 90 | * @param {Collector} collector the collector for getting the set of files and coverage 91 | * @param {Boolean} sync true if reports must be written synchronously, false if they can be written using asynchronous means (e.g. stream.write) 92 | */ 93 | writeReport: function (/* collector, sync */) { 94 | throw new Error('writeReport: must be overridden'); 95 | } 96 | }; 97 | 98 | Object.keys(proto).forEach(function (k) { 99 | Report.prototype[k] = proto[k]; 100 | }); 101 | 102 | module.exports = Report; 103 | 104 | 105 | -------------------------------------------------------------------------------- /lib/report/json-summary.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var path = require('path'), 7 | objectUtils = require('../object-utils'), 8 | Writer = require('../util/file-writer'), 9 | util = require('util'), 10 | Report = require('./index'); 11 | /** 12 | * a `Report` implementation that produces a coverage JSON object with summary info only. 13 | * 14 | * Usage 15 | * ----- 16 | * 17 | * var report = require('istanbul').Report.create('json-summary'); 18 | * 19 | * 20 | * @class JsonSummaryReport 21 | * @extends Report 22 | * @module report 23 | * @constructor 24 | * @param {Object} opts optional 25 | * @param {String} [opts.dir] the directory in which to write the `coverage-summary.json` file. Defaults to `process.cwd()` 26 | */ 27 | function JsonSummaryReport(opts) { 28 | this.opts = opts || {}; 29 | this.opts.dir = this.opts.dir || process.cwd(); 30 | this.opts.file = this.opts.file || this.getDefaultConfig().file; 31 | this.opts.writer = this.opts.writer || null; 32 | } 33 | JsonSummaryReport.TYPE = 'json-summary'; 34 | util.inherits(JsonSummaryReport, Report); 35 | 36 | Report.mix(JsonSummaryReport, { 37 | synopsis: function () { 38 | return 'prints a summary coverage object as JSON to a file'; 39 | }, 40 | getDefaultConfig: function () { 41 | return { 42 | file: 'coverage-summary.json' 43 | }; 44 | }, 45 | writeReport: function (collector, sync) { 46 | var outputFile = path.resolve(this.opts.dir, this.opts.file), 47 | writer = this.opts.writer || new Writer(sync), 48 | that = this; 49 | 50 | var summaries = [], 51 | finalSummary; 52 | collector.files().forEach(function (file) { 53 | summaries.push(objectUtils.summarizeFileCoverage(collector.fileCoverageFor(file))); 54 | }); 55 | finalSummary = objectUtils.mergeSummaryObjects.apply(null, summaries); 56 | 57 | writer.on('done', function () { that.emit('done'); }); 58 | writer.writeFile(outputFile, function (contentWriter) { 59 | contentWriter.println("{"); 60 | contentWriter.write('"total":'); 61 | contentWriter.write(JSON.stringify(finalSummary)); 62 | 63 | collector.files().forEach(function (key) { 64 | contentWriter.println(","); 65 | contentWriter.write(JSON.stringify(key)); 66 | contentWriter.write(":"); 67 | contentWriter.write(JSON.stringify(objectUtils.summarizeFileCoverage(collector.fileCoverageFor(key)))); 68 | }); 69 | contentWriter.println("}"); 70 | }); 71 | writer.done(); 72 | } 73 | }); 74 | 75 | module.exports = JsonSummaryReport; 76 | -------------------------------------------------------------------------------- /lib/report/json.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var path = require('path'), 7 | Writer = require('../util/file-writer'), 8 | util = require('util'), 9 | Report = require('./index'); 10 | /** 11 | * a `Report` implementation that produces a coverage JSON object. 12 | * 13 | * Usage 14 | * ----- 15 | * 16 | * var report = require('istanbul').Report.create('json'); 17 | * 18 | * 19 | * @class JsonReport 20 | * @extends Report 21 | * @module report 22 | * @constructor 23 | * @param {Object} opts optional 24 | * @param {String} [opts.dir] the directory in which to write the `coverage-final.json` file. Defaults to `process.cwd()` 25 | */ 26 | function JsonReport(opts) { 27 | this.opts = opts || {}; 28 | this.opts.dir = this.opts.dir || process.cwd(); 29 | this.opts.file = this.opts.file || this.getDefaultConfig().file; 30 | this.opts.writer = this.opts.writer || null; 31 | } 32 | JsonReport.TYPE = 'json'; 33 | util.inherits(JsonReport, Report); 34 | 35 | Report.mix(JsonReport, { 36 | synopsis: function () { 37 | return 'prints the coverage object as JSON to a file'; 38 | }, 39 | getDefaultConfig: function () { 40 | return { 41 | file: 'coverage-final.json' 42 | }; 43 | }, 44 | writeReport: function (collector, sync) { 45 | var outputFile = path.resolve(this.opts.dir, this.opts.file), 46 | writer = this.opts.writer || new Writer(sync), 47 | that = this; 48 | 49 | writer.on('done', function () { that.emit('done'); }); 50 | writer.writeFile(outputFile, function (contentWriter) { 51 | var first = true; 52 | contentWriter.println("{"); 53 | collector.files().forEach(function (key) { 54 | if (first) { 55 | first = false; 56 | } else { 57 | contentWriter.println(","); 58 | } 59 | contentWriter.write(JSON.stringify(key)); 60 | contentWriter.write(":"); 61 | contentWriter.write(JSON.stringify(collector.fileCoverageFor(key))); 62 | }); 63 | contentWriter.println("}"); 64 | }); 65 | writer.done(); 66 | } 67 | }); 68 | 69 | module.exports = JsonReport; 70 | -------------------------------------------------------------------------------- /lib/report/lcov.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var path = require('path'), 7 | util = require('util'), 8 | mkdirp = require('mkdirp'), 9 | Report = require('./index'), 10 | LcovOnlyReport = require('./lcovonly'), 11 | HtmlReport = require('./html'); 12 | 13 | /** 14 | * a `Report` implementation that produces an LCOV coverage file and an associated HTML report from coverage objects. 15 | * The name and behavior of this report is designed to ease migration for projects that currently use `yuitest_coverage` 16 | * 17 | * Usage 18 | * ----- 19 | * 20 | * var report = require('istanbul').Report.create('lcov'); 21 | * 22 | * 23 | * @class LcovReport 24 | * @extends Report 25 | * @module report 26 | * @constructor 27 | * @param {Object} opts optional 28 | * @param {String} [opts.dir] the directory in which to the `lcov.info` file. 29 | * HTML files are written in a subdirectory called `lcov-report`. Defaults to `process.cwd()` 30 | */ 31 | function LcovReport(opts) { 32 | Report.call(this); 33 | opts = opts || {}; 34 | var baseDir = path.resolve(opts.dir || process.cwd()), 35 | htmlDir = path.resolve(baseDir, 'lcov-report'); 36 | 37 | mkdirp.sync(baseDir); 38 | this.lcov = new LcovOnlyReport({ dir: baseDir, watermarks: opts.watermarks }); 39 | this.html = new HtmlReport({ dir: htmlDir, watermarks: opts.watermarks, sourceStore: opts.sourceStore}); 40 | } 41 | 42 | LcovReport.TYPE = 'lcov'; 43 | util.inherits(LcovReport, Report); 44 | 45 | Report.mix(LcovReport, { 46 | synopsis: function () { 47 | return 'combined lcovonly and html report that generates an lcov.info file as well as HTML'; 48 | }, 49 | writeReport: function (collector, sync) { 50 | var handler = this.handleDone.bind(this); 51 | this.inProgress = 2; 52 | this.lcov.on('done', handler); 53 | this.html.on('done', handler); 54 | this.lcov.writeReport(collector, sync); 55 | this.html.writeReport(collector, sync); 56 | }, 57 | handleDone: function () { 58 | this.inProgress -= 1; 59 | if (this.inProgress === 0) { 60 | this.emit('done'); 61 | } 62 | } 63 | }); 64 | 65 | module.exports = LcovReport; 66 | -------------------------------------------------------------------------------- /lib/report/lcovonly.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var path = require('path'), 7 | Writer = require('../util/file-writer'), 8 | util = require('util'), 9 | Report = require('./index'), 10 | utils = require('../object-utils'); 11 | /** 12 | * a `Report` implementation that produces an LCOV coverage file from coverage objects. 13 | * 14 | * Usage 15 | * ----- 16 | * 17 | * var report = require('istanbul').Report.create('lcovonly'); 18 | * 19 | * 20 | * @class LcovOnlyReport 21 | * @extends Report 22 | * @module report 23 | * @constructor 24 | * @param {Object} opts optional 25 | * @param {String} [opts.dir] the directory in which to the `lcov.info` file. Defaults to `process.cwd()` 26 | */ 27 | function LcovOnlyReport(opts) { 28 | this.opts = opts || {}; 29 | this.opts.dir = this.opts.dir || process.cwd(); 30 | this.opts.file = this.opts.file || this.getDefaultConfig().file; 31 | this.opts.writer = this.opts.writer || null; 32 | } 33 | LcovOnlyReport.TYPE = 'lcovonly'; 34 | util.inherits(LcovOnlyReport, Report); 35 | 36 | Report.mix(LcovOnlyReport, { 37 | synopsis: function () { 38 | return 'lcov coverage report that can be consumed by the lcov tool'; 39 | }, 40 | getDefaultConfig: function () { 41 | return { file: 'lcov.info' }; 42 | }, 43 | writeFileCoverage: function (writer, fc) { 44 | var functions = fc.f, 45 | functionMap = fc.fnMap, 46 | lines = fc.l, 47 | branches = fc.b, 48 | branchMap = fc.branchMap, 49 | summary = utils.summarizeFileCoverage(fc); 50 | 51 | writer.println('TN:'); //no test name 52 | writer.println('SF:' + fc.path); 53 | 54 | Object.keys(functions).forEach(function (key) { 55 | var meta = functionMap[key]; 56 | writer.println('FN:' + [ meta.line, meta.name ].join(',')); 57 | }); 58 | writer.println('FNF:' + summary.functions.total); 59 | writer.println('FNH:' + summary.functions.covered); 60 | 61 | Object.keys(functions).forEach(function (key) { 62 | var stats = functions[key], 63 | meta = functionMap[key]; 64 | writer.println('FNDA:' + [ stats, meta.name ].join(',')); 65 | }); 66 | 67 | Object.keys(lines).forEach(function (key) { 68 | var stat = lines[key]; 69 | writer.println('DA:' + [ key, stat ].join(',')); 70 | }); 71 | writer.println('LF:' + summary.lines.total); 72 | writer.println('LH:' + summary.lines.covered); 73 | 74 | Object.keys(branches).forEach(function (key) { 75 | var branchArray = branches[key], 76 | meta = branchMap[key], 77 | line = meta.line, 78 | i = 0; 79 | branchArray.forEach(function (b) { 80 | writer.println('BRDA:' + [line, key, i, b].join(',')); 81 | i += 1; 82 | }); 83 | }); 84 | writer.println('BRF:' + summary.branches.total); 85 | writer.println('BRH:' + summary.branches.covered); 86 | writer.println('end_of_record'); 87 | }, 88 | 89 | writeReport: function (collector, sync) { 90 | var outputFile = path.resolve(this.opts.dir, this.opts.file), 91 | writer = this.opts.writer || new Writer(sync), 92 | that = this; 93 | writer.on('done', function () { that.emit('done'); }); 94 | writer.writeFile(outputFile, function (contentWriter) { 95 | collector.files().forEach(function (key) { 96 | that.writeFileCoverage(contentWriter, collector.fileCoverageFor(key)); 97 | }); 98 | }); 99 | writer.done(); 100 | } 101 | }); 102 | 103 | module.exports = LcovOnlyReport; 104 | -------------------------------------------------------------------------------- /lib/report/none.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var util = require('util'), 7 | Report = require('./index'); 8 | 9 | /** 10 | * a `Report` implementation that does nothing. Use to specify that no reporting 11 | * is needed. 12 | * 13 | * Usage 14 | * ----- 15 | * 16 | * var report = require('istanbul').Report.create('none'); 17 | * 18 | * 19 | * @class NoneReport 20 | * @extends Report 21 | * @module report 22 | * @constructor 23 | */ 24 | function NoneReport() { 25 | Report.call(this); 26 | } 27 | 28 | NoneReport.TYPE = 'none'; 29 | util.inherits(NoneReport, Report); 30 | 31 | Report.mix(NoneReport, { 32 | synopsis: function () { 33 | return 'Does nothing. Useful to override default behavior and suppress reporting entirely'; 34 | }, 35 | writeReport: function (/* collector, sync */) { 36 | //noop 37 | this.emit('done'); 38 | } 39 | }); 40 | 41 | module.exports = NoneReport; 42 | -------------------------------------------------------------------------------- /lib/report/teamcity.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var path = require('path'), 7 | util = require('util'), 8 | mkdirp = require('mkdirp'), 9 | fs = require('fs'), 10 | utils = require('../object-utils'), 11 | Report = require('./index'); 12 | 13 | /** 14 | * a `Report` implementation that produces system messages interpretable by TeamCity. 15 | * 16 | * Usage 17 | * ----- 18 | * 19 | * var report = require('istanbul').Report.create('teamcity'); 20 | * 21 | * @class TeamcityReport 22 | * @extends Report 23 | * @module report 24 | * @constructor 25 | * @param {Object} opts optional 26 | * @param {String} [opts.dir] the directory in which to the text coverage report will be written, when writing to a file 27 | * @param {String} [opts.file] the filename for the report. When omitted, the report is written to console 28 | */ 29 | function TeamcityReport(opts) { 30 | Report.call(this); 31 | opts = opts || {}; 32 | this.dir = opts.dir || process.cwd(); 33 | this.file = opts.file; 34 | this.blockName = opts.blockName || this.getDefaultConfig().blockName; 35 | } 36 | 37 | TeamcityReport.TYPE = 'teamcity'; 38 | util.inherits(TeamcityReport, Report); 39 | 40 | function lineForKey(value, teamcityVar) { 41 | return '##teamcity[buildStatisticValue key=\'' + teamcityVar + '\' value=\'' + value + '\']'; 42 | } 43 | 44 | Report.mix(TeamcityReport, { 45 | synopsis: function () { 46 | return 'report with system messages that can be interpreted with TeamCity'; 47 | }, 48 | getDefaultConfig: function () { 49 | return { file: null , blockName: 'Code Coverage Summary'}; 50 | }, 51 | writeReport: function (collector /*, sync */) { 52 | var summaries = [], 53 | finalSummary, 54 | lines = [], 55 | text; 56 | 57 | collector.files().forEach(function (file) { 58 | summaries.push(utils.summarizeFileCoverage(collector.fileCoverageFor(file))); 59 | }); 60 | 61 | finalSummary = utils.mergeSummaryObjects.apply(null, summaries); 62 | 63 | lines.push(''); 64 | lines.push('##teamcity[blockOpened name=\''+ this.blockName +'\']'); 65 | 66 | //Statements Covered 67 | lines.push(lineForKey(finalSummary.statements.pct, 'CodeCoverageB')); 68 | 69 | //Methods Covered 70 | lines.push(lineForKey(finalSummary.functions.covered, 'CodeCoverageAbsMCovered')); 71 | lines.push(lineForKey(finalSummary.functions.total, 'CodeCoverageAbsMTotal')); 72 | lines.push(lineForKey(finalSummary.functions.pct, 'CodeCoverageM')); 73 | 74 | //Lines Covered 75 | lines.push(lineForKey(finalSummary.lines.covered, 'CodeCoverageAbsLCovered')); 76 | lines.push(lineForKey(finalSummary.lines.total, 'CodeCoverageAbsLTotal')); 77 | lines.push(lineForKey(finalSummary.lines.pct, 'CodeCoverageL')); 78 | 79 | lines.push('##teamcity[blockClosed name=\''+ this.blockName +'\']'); 80 | 81 | text = lines.join('\n'); 82 | if (this.file) { 83 | mkdirp.sync(this.dir); 84 | fs.writeFileSync(path.join(this.dir, this.file), text, 'utf8'); 85 | } else { 86 | console.log(text); 87 | } 88 | this.emit('done'); 89 | } 90 | }); 91 | 92 | module.exports = TeamcityReport; 93 | -------------------------------------------------------------------------------- /lib/report/templates/foot.txt: -------------------------------------------------------------------------------- 1 |
2 | 3 | 7 | 8 | {{#if prettify}} 9 | 10 | 17 | {{/if}} 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/report/templates/head.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for {{entity}} 5 | 6 | {{#if prettify}} 7 | 8 | {{/if}} 9 | 10 | 11 | 16 | 17 | 18 |
19 |
20 |

21 | {{{pathHtml}}} 22 |

23 |
24 | {{#with metrics.statements}} 25 |
26 | {{pct}}% 27 | Statements 28 | {{covered}}/{{total}} 29 |
30 | {{/with}} 31 | {{#with metrics.branches}} 32 |
33 | {{pct}}% 34 | Branches 35 | {{covered}}/{{total}} 36 |
37 | {{/with}} 38 | {{#with metrics.functions}} 39 |
40 | {{pct}}% 41 | Functions 42 | {{covered}}/{{total}} 43 |
44 | {{/with}} 45 | {{#with metrics.lines}} 46 |
47 | {{pct}}% 48 | Lines 49 | {{covered}}/{{total}} 50 |
51 | {{/with}} 52 | {{#if_has_ignores metrics}} 53 |
54 | {{#show_ignores metrics}}{{/show_ignores}} 55 | Ignored      56 |
57 | {{/if_has_ignores}} 58 |
59 |
60 |
61 | -------------------------------------------------------------------------------- /lib/report/text-lcov.js: -------------------------------------------------------------------------------- 1 | var LcovOnly = require('./lcovonly'), 2 | util = require('util'); 3 | 4 | /** 5 | * a `Report` implementation that produces an LCOV coverage and prints it 6 | * to standard out. 7 | * 8 | * Usage 9 | * ----- 10 | * 11 | * var report = require('istanbul').Report.create('text-lcov'); 12 | * 13 | * @class TextLcov 14 | * @module report 15 | * @extends LcovOnly 16 | * @constructor 17 | * @param {Object} opts optional 18 | * @param {String} [opts.log] the method used to log to console. 19 | */ 20 | function TextLcov(opts) { 21 | var that = this; 22 | 23 | LcovOnly.call(this); 24 | 25 | this.opts = opts || {}; 26 | this.opts.log = this.opts.log || console.log; 27 | this.opts.writer = { 28 | println: function (ln) { 29 | that.opts.log(ln); 30 | } 31 | }; 32 | } 33 | 34 | TextLcov.TYPE = 'text-lcov'; 35 | util.inherits(TextLcov, LcovOnly); 36 | 37 | LcovOnly.super_.mix(TextLcov, { 38 | writeReport: function (collector) { 39 | var that = this, 40 | writer = this.opts.writer; 41 | 42 | collector.files().forEach(function (key) { 43 | that.writeFileCoverage(writer, collector.fileCoverageFor(key)); 44 | }); 45 | 46 | this.emit('done'); 47 | } 48 | }); 49 | 50 | module.exports = TextLcov; 51 | -------------------------------------------------------------------------------- /lib/report/text-summary.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var path = require('path'), 7 | util = require('util'), 8 | mkdirp = require('mkdirp'), 9 | defaults = require('./common/defaults'), 10 | fs = require('fs'), 11 | utils = require('../object-utils'), 12 | Report = require('./index'); 13 | 14 | /** 15 | * a `Report` implementation that produces text output for overall coverage in summary format. 16 | * 17 | * Usage 18 | * ----- 19 | * 20 | * var report = require('istanbul').Report.create('text-summary'); 21 | * 22 | * @class TextSummaryReport 23 | * @extends Report 24 | * @module report 25 | * @constructor 26 | * @param {Object} opts optional 27 | * @param {String} [opts.dir] the directory in which to the text coverage report will be written, when writing to a file 28 | * @param {String} [opts.file] the filename for the report. When omitted, the report is written to console 29 | */ 30 | function TextSummaryReport(opts) { 31 | Report.call(this); 32 | opts = opts || {}; 33 | this.dir = opts.dir || process.cwd(); 34 | this.file = opts.file; 35 | this.watermarks = opts.watermarks || defaults.watermarks(); 36 | } 37 | 38 | TextSummaryReport.TYPE = 'text-summary'; 39 | util.inherits(TextSummaryReport, Report); 40 | 41 | function lineForKey(summary, key, watermarks) { 42 | var metrics = summary[key], 43 | skipped, 44 | result, 45 | clazz = defaults.classFor(key, summary, watermarks); 46 | key = key.substring(0, 1).toUpperCase() + key.substring(1); 47 | if (key.length < 12) { key += ' '.substring(0, 12 - key.length); } 48 | result = [ key , ':', metrics.pct + '%', '(', metrics.covered + '/' + metrics.total, ')'].join(' '); 49 | skipped = metrics.skipped; 50 | if (skipped > 0) { 51 | result += ', ' + skipped + ' ignored'; 52 | } 53 | return defaults.colorize(result, clazz); 54 | } 55 | 56 | Report.mix(TextSummaryReport, { 57 | synopsis: function () { 58 | return 'text report that prints a coverage summary across all files, typically to console'; 59 | }, 60 | getDefaultConfig: function () { 61 | return { file: null }; 62 | }, 63 | writeReport: function (collector /*, sync */) { 64 | var summaries = [], 65 | finalSummary, 66 | lines = [], 67 | watermarks = this.watermarks, 68 | text; 69 | collector.files().forEach(function (file) { 70 | summaries.push(utils.summarizeFileCoverage(collector.fileCoverageFor(file))); 71 | }); 72 | finalSummary = utils.mergeSummaryObjects.apply(null, summaries); 73 | lines.push(''); 74 | lines.push('=============================== Coverage summary ==============================='); 75 | lines.push.apply(lines, [ 76 | lineForKey(finalSummary, 'statements', watermarks), 77 | lineForKey(finalSummary, 'branches', watermarks), 78 | lineForKey(finalSummary, 'functions', watermarks), 79 | lineForKey(finalSummary, 'lines', watermarks) 80 | ]); 81 | lines.push('================================================================================'); 82 | text = lines.join('\n'); 83 | if (this.file) { 84 | mkdirp.sync(this.dir); 85 | fs.writeFileSync(path.join(this.dir, this.file), text, 'utf8'); 86 | } else { 87 | console.log(text); 88 | } 89 | this.emit('done'); 90 | } 91 | }); 92 | 93 | module.exports = TextSummaryReport; 94 | -------------------------------------------------------------------------------- /lib/reporter.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | var Report = require('./report'), 6 | configuration = require('./config'), 7 | inputError = require('./util/input-error'); 8 | 9 | /** 10 | * convenience mechanism to write one or more reports ensuring that config 11 | * options are respected. 12 | * Usage 13 | * ----- 14 | * 15 | * var fs = require('fs'), 16 | * reporter = new require('istanbul').Reporter(), 17 | * collector = new require('istanbul').Collector(), 18 | * sync = true; 19 | * 20 | * collector.add(JSON.parse(fs.readFileSync('coverage.json', 'utf8'))); 21 | * reporter.add('lcovonly'); 22 | * reporter.addAll(['clover', 'cobertura']); 23 | * reporter.write(collector, sync, function () { console.log('done'); }); 24 | * 25 | * @class Reporter 26 | * @param {Configuration} cfg the config object, a falsy value will load the 27 | * default configuration instead 28 | * @param {String} dir the directory in which to write the reports, may be falsy 29 | * to use config or global defaults 30 | * @constructor 31 | * @module main 32 | */ 33 | function Reporter(cfg, dir) { 34 | this.config = cfg || configuration.loadFile(); 35 | this.dir = dir || this.config.reporting.dir(); 36 | this.reports = {}; 37 | } 38 | 39 | Reporter.prototype = { 40 | /** 41 | * adds a report to be generated. Must be one of the entries returned 42 | * by `Report.getReportList()` 43 | * @method add 44 | * @param {String} fmt the format of the report to generate 45 | */ 46 | add: function (fmt) { 47 | if (this.reports[fmt]) { // already added 48 | return; 49 | } 50 | var config = this.config, 51 | rptConfig = config.reporting.reportConfig()[fmt] || {}; 52 | rptConfig.verbose = config.verbose; 53 | rptConfig.dir = this.dir; 54 | rptConfig.watermarks = config.reporting.watermarks(); 55 | try { 56 | this.reports[fmt] = Report.create(fmt, rptConfig); 57 | } catch (ex) { 58 | throw inputError.create('Invalid report format [' + fmt + ']'); 59 | } 60 | }, 61 | /** 62 | * adds an array of report formats to be generated 63 | * @method addAll 64 | * @param {Array} fmts an array of report formats 65 | */ 66 | addAll: function (fmts) { 67 | var that = this; 68 | fmts.forEach(function (f) { 69 | that.add(f); 70 | }); 71 | }, 72 | /** 73 | * writes all reports added and calls the callback when done 74 | * @method write 75 | * @param {Collector} collector the collector having the coverage data 76 | * @param {Boolean} sync true to write reports synchronously 77 | * @param {Function} callback the callback to call when done. When `sync` 78 | * is true, the callback will be called in the same process tick. 79 | */ 80 | write: function (collector, sync, callback) { 81 | var reports = this.reports, 82 | verbose = this.config.verbose, 83 | handler = this.handleDone.bind(this, callback); 84 | 85 | this.inProgress = Object.keys(reports).length; 86 | 87 | Object.keys(reports).forEach(function (name) { 88 | var report = reports[name]; 89 | if (verbose) { 90 | console.error('Write report: ' + name); 91 | } 92 | report.on('done', handler); 93 | report.writeReport(collector, sync); 94 | }); 95 | }, 96 | /* 97 | * handles listening on all reports to be completed before calling the callback 98 | * @method handleDone 99 | * @private 100 | * @param {Function} callback the callback to call when all reports are 101 | * written 102 | */ 103 | handleDone: function (callback) { 104 | this.inProgress -= 1; 105 | if (this.inProgress === 0) { 106 | return callback(); 107 | } 108 | } 109 | }; 110 | 111 | module.exports = Reporter; 112 | -------------------------------------------------------------------------------- /lib/store/fslookup.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var util = require('util'), 7 | fs = require('fs'), 8 | Store = require('./index'); 9 | 10 | /** 11 | * a `Store` implementation that doesn't actually store anything. It assumes that keys 12 | * are absolute file paths, and contents are contents of those files. 13 | * Thus, `set` for this store is no-op, `get` returns the 14 | * contents of the filename that the key represents, `hasKey` returns true if the key 15 | * supplied is a valid file path and `keys` always returns an empty array. 16 | * 17 | * Usage 18 | * ----- 19 | * 20 | * var store = require('istanbul').Store.create('fslookup'); 21 | * 22 | * 23 | * @class LookupStore 24 | * @extends Store 25 | * @module store 26 | * @constructor 27 | */ 28 | function LookupStore(opts) { 29 | Store.call(this, opts); 30 | } 31 | 32 | LookupStore.TYPE = 'fslookup'; 33 | util.inherits(LookupStore, Store); 34 | 35 | Store.mix(LookupStore, { 36 | keys: function () { 37 | return []; 38 | }, 39 | get: function (key) { 40 | return fs.readFileSync(key, 'utf8'); 41 | }, 42 | hasKey: function (key) { 43 | var stats; 44 | try { 45 | stats = fs.statSync(key); 46 | return stats.isFile(); 47 | } catch (ex) { 48 | return false; 49 | } 50 | }, 51 | set: function (key /*, contents */) { 52 | if (!this.hasKey(key)) { 53 | throw new Error('Attempt to set contents for non-existent file [' + key + '] on a fslookup store'); 54 | } 55 | return key; 56 | } 57 | }); 58 | 59 | 60 | module.exports = LookupStore; 61 | 62 | -------------------------------------------------------------------------------- /lib/store/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var Factory = require('../util/factory'), 7 | factory = new Factory('store', __dirname, false); 8 | /** 9 | * An abstraction for keeping track of content against some keys (e.g. 10 | * original source, instrumented source, coverage objects against file names). 11 | * This class is both the base class as well as a factory for `Store` implementations. 12 | * 13 | * Usage 14 | * ----- 15 | * 16 | * var Store = require('istanbul').Store, 17 | * store = Store.create('memory'); 18 | * 19 | * //basic use 20 | * store.set('foo', 'foo-content'); 21 | * var content = store.get('foo'); 22 | * 23 | * //keys and values 24 | * store.keys().forEach(function (key) { 25 | * console.log(key + ':\n' + store.get(key); 26 | * }); 27 | * if (store.hasKey('bar') { console.log(store.get('bar'); } 28 | * 29 | * 30 | * //syntactic sugar 31 | * store.setObject('foo', { foo: true }); 32 | * console.log(store.getObject('foo').foo); 33 | * 34 | * store.dispose(); 35 | * 36 | * @class Store 37 | * @constructor 38 | * @module store 39 | * @param {Object} options Optional. The options supported by a specific store implementation. 40 | * @main store 41 | */ 42 | function Store(/* options */) {} 43 | 44 | //add register, create, mix, loadAll, getStoreList as class methods 45 | factory.bindClassMethods(Store); 46 | 47 | /** 48 | * registers a new store implementation. 49 | * @method register 50 | * @static 51 | * @param {Function} constructor the constructor function for the store. This function must have a 52 | * `TYPE` property of type String, that will be used in `Store.create()` 53 | */ 54 | /** 55 | * returns a store implementation of the specified type. 56 | * @method create 57 | * @static 58 | * @param {String} type the type of store to create 59 | * @param {Object} opts Optional. Options specific to the store implementation 60 | * @return {Store} a new store of the specified type 61 | */ 62 | 63 | Store.prototype = { 64 | /** 65 | * sets some content associated with a specific key. The manner in which 66 | * duplicate keys are handled for multiple `set()` calls with the same 67 | * key is implementation-specific. 68 | * 69 | * @method set 70 | * @param {String} key the key for the content 71 | * @param {String} contents the contents for the key 72 | */ 73 | set: function (/* key, contents */) { throw new Error("set: must be overridden"); }, 74 | /** 75 | * returns the content associated to a specific key or throws if the key 76 | * was not `set` 77 | * @method get 78 | * @param {String} key the key for which to get the content 79 | * @return {String} the content for the specified key 80 | */ 81 | get: function (/* key */) { throw new Error("get: must be overridden"); }, 82 | /** 83 | * returns a list of all known keys 84 | * @method keys 85 | * @return {Array} an array of seen keys 86 | */ 87 | keys: function () { throw new Error("keys: must be overridden"); }, 88 | /** 89 | * returns true if the key is one for which a `get()` call would work. 90 | * @method hasKey 91 | * @param {String} key 92 | * @return true if the key is valid for this store, false otherwise 93 | */ 94 | hasKey: function (/* key */) { throw new Error("hasKey: must be overridden"); }, 95 | /** 96 | * lifecycle method to dispose temporary resources associated with the store 97 | * @method dispose 98 | */ 99 | dispose: function () {}, 100 | /** 101 | * sugar method to return an object associated with a specific key. Throws 102 | * if the content set against the key was not a valid JSON string. 103 | * @method getObject 104 | * @param {String} key the key for which to return the associated object 105 | * @return {Object} the object corresponding to the key 106 | */ 107 | getObject: function (key) { 108 | return JSON.parse(this.get(key)); 109 | }, 110 | /** 111 | * sugar method to set an object against a specific key. 112 | * @method setObject 113 | * @param {String} key the key for the object 114 | * @param {Object} object the object to be stored 115 | */ 116 | setObject: function (key, object) { 117 | return this.set(key, JSON.stringify(object)); 118 | } 119 | }; 120 | 121 | module.exports = Store; 122 | 123 | 124 | -------------------------------------------------------------------------------- /lib/store/memory.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var util = require('util'), 7 | Store = require('./index'); 8 | 9 | /** 10 | * a `Store` implementation using an in-memory object. 11 | * 12 | * Usage 13 | * ----- 14 | * 15 | * var store = require('istanbul').Store.create('memory'); 16 | * 17 | * 18 | * @class MemoryStore 19 | * @extends Store 20 | * @module store 21 | * @constructor 22 | */ 23 | function MemoryStore() { 24 | Store.call(this); 25 | this.map = {}; 26 | } 27 | 28 | MemoryStore.TYPE = 'memory'; 29 | util.inherits(MemoryStore, Store); 30 | 31 | Store.mix(MemoryStore, { 32 | set: function (key, contents) { 33 | this.map[key] = contents; 34 | }, 35 | 36 | get: function (key) { 37 | if (!this.hasKey(key)) { 38 | throw new Error('Unable to find entry for [' + key + ']'); 39 | } 40 | return this.map[key]; 41 | }, 42 | 43 | hasKey: function (key) { 44 | return this.map.hasOwnProperty(key); 45 | }, 46 | 47 | keys: function () { 48 | return Object.keys(this.map); 49 | }, 50 | 51 | dispose: function () { 52 | this.map = {}; 53 | } 54 | }); 55 | 56 | module.exports = MemoryStore; 57 | -------------------------------------------------------------------------------- /lib/store/tmp.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var util = require('util'), 7 | path = require('path'), 8 | os = require('os'), 9 | fs = require('fs'), 10 | mkdirp = require('mkdirp'), 11 | Store = require('./index'); 12 | 13 | function makeTempDir() { 14 | var dir = path.join(os.tmpdir ? os.tmpdir() : /* istanbul ignore next */ (process.env.TMPDIR || '/tmp'), 'ts' + new Date().getTime()); 15 | mkdirp.sync(dir); 16 | return dir; 17 | } 18 | /** 19 | * a `Store` implementation using temporary files. 20 | * 21 | * Usage 22 | * ----- 23 | * 24 | * var store = require('istanbul').Store.create('tmp'); 25 | * 26 | * 27 | * @class TmpStore 28 | * @extends Store 29 | * @module store 30 | * @param {Object} opts Optional. 31 | * @param {String} [opts.tmp] a pre-existing directory to use as the `tmp` directory. When not specified, a random directory 32 | * is created under `os.tmpdir()` 33 | * @constructor 34 | */ 35 | function TmpStore(opts) { 36 | opts = opts || {}; 37 | this.tmp = opts.tmp || makeTempDir(); 38 | this.map = {}; 39 | this.seq = 0; 40 | this.prefix = 't' + new Date().getTime() + '-'; 41 | } 42 | 43 | TmpStore.TYPE = 'tmp'; 44 | util.inherits(TmpStore, Store); 45 | 46 | Store.mix(TmpStore, { 47 | generateTmpFileName: function () { 48 | this.seq += 1; 49 | return path.join(this.tmp, this.prefix + this.seq + '.tmp'); 50 | }, 51 | 52 | set: function (key, contents) { 53 | var tmpFile = this.generateTmpFileName(); 54 | fs.writeFileSync(tmpFile, contents, 'utf8'); 55 | this.map[key] = tmpFile; 56 | }, 57 | 58 | get: function (key) { 59 | var tmpFile = this.map[key]; 60 | if (!tmpFile) { throw new Error('Unable to find tmp entry for [' + tmpFile + ']'); } 61 | return fs.readFileSync(tmpFile, 'utf8'); 62 | }, 63 | 64 | hasKey: function (key) { 65 | return !!this.map[key]; 66 | }, 67 | 68 | keys: function () { 69 | return Object.keys(this.map); 70 | }, 71 | 72 | dispose: function () { 73 | var map = this.map; 74 | Object.keys(map).forEach(function (key) { 75 | fs.unlinkSync(map[key]); 76 | }); 77 | this.map = {}; 78 | } 79 | }); 80 | 81 | module.exports = TmpStore; 82 | -------------------------------------------------------------------------------- /lib/util/factory.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var util = require('util'), 7 | path = require('path'), 8 | fs = require('fs'), 9 | abbrev = require('abbrev'); 10 | 11 | function Factory(kind, dir, allowAbbreviations) { 12 | this.kind = kind; 13 | this.dir = dir; 14 | this.allowAbbreviations = allowAbbreviations; 15 | this.classMap = {}; 16 | this.abbreviations = null; 17 | } 18 | 19 | Factory.prototype = { 20 | 21 | knownTypes: function () { 22 | var keys = Object.keys(this.classMap); 23 | keys.sort(); 24 | return keys; 25 | }, 26 | 27 | resolve: function (abbreviatedType) { 28 | if (!this.abbreviations) { 29 | this.abbreviations = abbrev(this.knownTypes()); 30 | } 31 | return this.abbreviations[abbreviatedType]; 32 | }, 33 | 34 | register: function (constructor) { 35 | var type = constructor.TYPE; 36 | if (!type) { throw new Error('Could not register ' + this.kind + ' constructor [no TYPE property]: ' + util.inspect(constructor)); } 37 | this.classMap[type] = constructor; 38 | this.abbreviations = null; 39 | }, 40 | 41 | create: function (type, opts) { 42 | var allowAbbrev = this.allowAbbreviations, 43 | realType = allowAbbrev ? this.resolve(type) : type, 44 | Cons; 45 | 46 | Cons = realType ? this.classMap[realType] : null; 47 | if (!Cons) { throw new Error('Invalid ' + this.kind + ' [' + type + '], allowed values are ' + this.knownTypes().join(', ')); } 48 | return new Cons(opts); 49 | }, 50 | 51 | loadStandard: function (dir) { 52 | var that = this; 53 | fs.readdirSync(dir).forEach(function (file) { 54 | if (file !== 'index.js' && file.indexOf('.js') === file.length - 3) { 55 | try { 56 | that.register(require(path.resolve(dir, file))); 57 | } catch (ex) { 58 | console.error(ex.message); 59 | console.error(ex.stack); 60 | throw new Error('Could not register ' + that.kind + ' from file ' + file); 61 | } 62 | } 63 | }); 64 | }, 65 | 66 | bindClassMethods: function (Cons) { 67 | var tmpKind = this.kind.charAt(0).toUpperCase() + this.kind.substring(1), //ucfirst 68 | allowAbbrev = this.allowAbbreviations; 69 | 70 | Cons.mix = Factory.mix; 71 | Cons.register = this.register.bind(this); 72 | Cons.create = this.create.bind(this); 73 | Cons.loadAll = this.loadStandard.bind(this, this.dir); 74 | Cons['get' + tmpKind + 'List'] = this.knownTypes.bind(this); 75 | if (allowAbbrev) { 76 | Cons['resolve' + tmpKind + 'Name'] = this.resolve.bind(this); 77 | } 78 | } 79 | }; 80 | 81 | Factory.mix = function (cons, proto) { 82 | Object.keys(proto).forEach(function (key) { 83 | cons.prototype[key] = proto[key]; 84 | }); 85 | }; 86 | 87 | module.exports = Factory; 88 | 89 | -------------------------------------------------------------------------------- /lib/util/file-matcher.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var async = require('async'), 7 | glob = require('multi-glob').glob, 8 | fs = require('fs'), 9 | path = require('path'), 10 | seq = 0; 11 | 12 | function filesFor(options, callback) { 13 | if (!callback && typeof options === 'function') { 14 | callback = options; 15 | options = null; 16 | } 17 | options = options || {}; 18 | 19 | var root = options.root, 20 | includes = options.includes, 21 | excludes = options.excludes, 22 | realpath = options.realpath, 23 | relative = options.relative, 24 | opts; 25 | 26 | root = root || process.cwd(); 27 | includes = includes && Array.isArray(includes) ? includes : [ '**/*.js' ]; 28 | excludes = excludes && Array.isArray(excludes) ? excludes : [ '**/node_modules/**' ]; 29 | 30 | opts = { cwd: root, nodir: true, ignore: excludes }; 31 | seq += 1; 32 | opts['x' + seq + new Date().getTime()] = true; //cache buster for minimatch cache bug 33 | glob(includes, opts, function (err, files) { 34 | if (err) { return callback(err); } 35 | if (relative) { return callback(err, files); } 36 | 37 | if (!realpath) { 38 | files = files.map(function (file) { return path.resolve(root, file); }); 39 | return callback(err, files); 40 | } 41 | 42 | var realPathCache = module.constructor._realpathCache || {}; 43 | 44 | async.map(files, function (file, done) { 45 | fs.realpath(path.resolve(root, file), realPathCache, done); 46 | }, callback); 47 | }); 48 | } 49 | 50 | function matcherFor(options, callback) { 51 | 52 | if (!callback && typeof options === 'function') { 53 | callback = options; 54 | options = null; 55 | } 56 | options = options || {}; 57 | options.relative = false; //force absolute paths 58 | options.realpath = true; //force real paths (to match Node.js module paths) 59 | 60 | filesFor(options, function (err, files) { 61 | var fileMap = {}, 62 | matchFn; 63 | if (err) { return callback(err); } 64 | files.forEach(function (file) { fileMap[file] = true; }); 65 | 66 | matchFn = function (file) { return fileMap[file]; }; 67 | matchFn.files = Object.keys(fileMap); 68 | return callback(null, matchFn); 69 | }); 70 | } 71 | 72 | module.exports = { 73 | filesFor: filesFor, 74 | matcherFor: matcherFor 75 | }; 76 | 77 | -------------------------------------------------------------------------------- /lib/util/file-writer.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var path = require('path'), 7 | util = require('util'), 8 | fs = require('fs'), 9 | async = require('async'), 10 | mkdirp = require('mkdirp'), 11 | writer = require('./writer'), 12 | Writer = writer.Writer, 13 | ContentWriter = writer.ContentWriter; 14 | 15 | function extend(cons, proto) { 16 | Object.keys(proto).forEach(function (k) { 17 | cons.prototype[k] = proto[k]; 18 | }); 19 | } 20 | 21 | function BufferedContentWriter() { 22 | ContentWriter.call(this); 23 | this.content = ''; 24 | } 25 | util.inherits(BufferedContentWriter, ContentWriter); 26 | 27 | extend(BufferedContentWriter, { 28 | write: function (str) { 29 | this.content += str; 30 | }, 31 | getContent: function () { 32 | return this.content; 33 | } 34 | }); 35 | 36 | function StreamContentWriter(stream) { 37 | ContentWriter.call(this); 38 | this.stream = stream; 39 | } 40 | util.inherits(StreamContentWriter, ContentWriter); 41 | 42 | extend(StreamContentWriter, { 43 | write: function (str) { 44 | this.stream.write(str); 45 | } 46 | }); 47 | 48 | function SyncFileWriter() { 49 | Writer.call(this); 50 | } 51 | util.inherits(SyncFileWriter, Writer); 52 | 53 | extend(SyncFileWriter, { 54 | writeFile: function (file, callback) { 55 | mkdirp.sync(path.dirname(file)); 56 | var cw = new BufferedContentWriter(); 57 | callback(cw); 58 | fs.writeFileSync(file, cw.getContent(), 'utf8'); 59 | }, 60 | done: function () { 61 | this.emit('done'); //everything already done 62 | } 63 | }); 64 | 65 | function AsyncFileWriter() { 66 | this.queue = async.queue(this.processFile.bind(this), 20); 67 | this.openFileMap = {}; 68 | } 69 | 70 | util.inherits(AsyncFileWriter, Writer); 71 | 72 | extend(AsyncFileWriter, { 73 | writeFile: function (file, callback) { 74 | this.openFileMap[file] = true; 75 | this.queue.push({ file: file, callback: callback }); 76 | }, 77 | processFile: function (task, cb) { 78 | var file = task.file, 79 | userCallback = task.callback, 80 | that = this, 81 | stream, 82 | contentWriter; 83 | 84 | mkdirp.sync(path.dirname(file)); 85 | stream = fs.createWriteStream(file); 86 | stream.on('close', function () { 87 | delete that.openFileMap[file]; 88 | cb(); 89 | that.checkDone(); 90 | }); 91 | stream.on('error', function (err) { that.emit('error', err); }); 92 | contentWriter = new StreamContentWriter(stream); 93 | userCallback(contentWriter); 94 | stream.end(); 95 | }, 96 | done: function () { 97 | this.doneCalled = true; 98 | this.checkDone(); 99 | }, 100 | checkDone: function () { 101 | if (!this.doneCalled) { return; } 102 | if (Object.keys(this.openFileMap).length === 0) { 103 | this.emit('done'); 104 | } 105 | } 106 | }); 107 | /** 108 | * a concrete writer implementation that can write files synchronously or 109 | * asynchronously based on the constructor argument passed to it. 110 | * 111 | * Usage 112 | * ----- 113 | * 114 | * var sync = true, 115 | * fileWriter = new require('istanbul').FileWriter(sync); 116 | * 117 | * fileWriter.on('done', function () { console.log('done'); }); 118 | * fileWriter.copyFile('/foo/bar.jpg', '/baz/bar.jpg'); 119 | * fileWriter.writeFile('/foo/index.html', function (contentWriter) { 120 | * contentWriter.println(''); 121 | * contentWriter.println(''); 122 | * }); 123 | * fileWriter.done(); // will emit the `done` event when all files are written 124 | * 125 | * @class FileWriter 126 | * @extends Writer 127 | * @module io 128 | * @param sync 129 | * @constructor 130 | */ 131 | function FileWriter(sync) { 132 | Writer.call(this); 133 | var that = this; 134 | this.delegate = sync ? new SyncFileWriter() : new AsyncFileWriter(); 135 | this.delegate.on('error', function (err) { that.emit('error', err); }); 136 | this.delegate.on('done', function () { that.emit('done'); }); 137 | } 138 | 139 | util.inherits(FileWriter, Writer); 140 | 141 | extend(FileWriter, { 142 | copyFile: function (source, dest) { 143 | mkdirp.sync(path.dirname(dest)); 144 | fs.writeFileSync(dest, fs.readFileSync(source)); 145 | }, 146 | writeFile: function (file, callback) { 147 | this.delegate.writeFile(file, callback); 148 | }, 149 | done: function () { 150 | this.delegate.done(); 151 | } 152 | }); 153 | 154 | module.exports = FileWriter; -------------------------------------------------------------------------------- /lib/util/help-formatter.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var OPT_PREFIX = " ", 7 | OPT_START = OPT_PREFIX.length, 8 | TEXT_START = 14, 9 | STOP = 80, 10 | wrap = require('wordwrap')(TEXT_START, STOP), 11 | paraWrap = require('wordwrap')(1, STOP); 12 | 13 | function formatPara(text) { 14 | return paraWrap(text); 15 | } 16 | 17 | function formatOption(option, helpText) { 18 | var formattedText = wrap(helpText); 19 | 20 | if (option.length > TEXT_START - OPT_START - 2) { 21 | return OPT_PREFIX + option + '\n' + formattedText; 22 | } else { 23 | return OPT_PREFIX + option + formattedText.substring((OPT_PREFIX + option).length); 24 | } 25 | } 26 | 27 | module.exports = { 28 | formatPara: formatPara, 29 | formatOption: formatOption 30 | }; -------------------------------------------------------------------------------- /lib/util/input-error.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | module.exports.create = function (message) { 7 | var err = new Error(message); 8 | err.inputError = true; 9 | return err; 10 | }; 11 | 12 | 13 | -------------------------------------------------------------------------------- /lib/util/insertion-text.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | function InsertionText(text, consumeBlanks) { 7 | this.text = text; 8 | this.origLength = text.length; 9 | this.offsets = []; 10 | this.consumeBlanks = consumeBlanks; 11 | this.startPos = this.findFirstNonBlank(); 12 | this.endPos = this.findLastNonBlank(); 13 | } 14 | 15 | var WHITE_RE = /[ \f\n\r\t\v\u00A0\u2028\u2029]/; 16 | 17 | InsertionText.prototype = { 18 | 19 | findFirstNonBlank: function () { 20 | var pos = -1, 21 | text = this.text, 22 | len = text.length, 23 | i; 24 | for (i = 0; i < len; i += 1) { 25 | if (!text.charAt(i).match(WHITE_RE)) { 26 | pos = i; 27 | break; 28 | } 29 | } 30 | return pos; 31 | }, 32 | findLastNonBlank: function () { 33 | var text = this.text, 34 | len = text.length, 35 | pos = text.length + 1, 36 | i; 37 | for (i = len - 1; i >= 0; i -= 1) { 38 | if (!text.charAt(i).match(WHITE_RE)) { 39 | pos = i; 40 | break; 41 | } 42 | } 43 | return pos; 44 | }, 45 | originalLength: function () { 46 | return this.origLength; 47 | }, 48 | 49 | insertAt: function (col, str, insertBefore, consumeBlanks) { 50 | consumeBlanks = typeof consumeBlanks === 'undefined' ? this.consumeBlanks : consumeBlanks; 51 | col = col > this.originalLength() ? this.originalLength() : col; 52 | col = col < 0 ? 0 : col; 53 | 54 | if (consumeBlanks) { 55 | if (col <= this.startPos) { 56 | col = 0; 57 | } 58 | if (col > this.endPos) { 59 | col = this.origLength; 60 | } 61 | } 62 | 63 | var len = str.length, 64 | offset = this.findOffset(col, len, insertBefore), 65 | realPos = col + offset, 66 | text = this.text; 67 | this.text = text.substring(0, realPos) + str + text.substring(realPos); 68 | return this; 69 | }, 70 | 71 | findOffset: function (pos, len, insertBefore) { 72 | var offsets = this.offsets, 73 | offsetObj, 74 | cumulativeOffset = 0, 75 | i; 76 | 77 | for (i = 0; i < offsets.length; i += 1) { 78 | offsetObj = offsets[i]; 79 | if (offsetObj.pos < pos || (offsetObj.pos === pos && !insertBefore)) { 80 | cumulativeOffset += offsetObj.len; 81 | } 82 | if (offsetObj.pos >= pos) { 83 | break; 84 | } 85 | } 86 | if (offsetObj && offsetObj.pos === pos) { 87 | offsetObj.len += len; 88 | } else { 89 | offsets.splice(i, 0, { pos: pos, len: len }); 90 | } 91 | return cumulativeOffset; 92 | }, 93 | 94 | wrap: function (startPos, startText, endPos, endText, consumeBlanks) { 95 | this.insertAt(startPos, startText, true, consumeBlanks); 96 | this.insertAt(endPos, endText, false, consumeBlanks); 97 | return this; 98 | }, 99 | 100 | wrapLine: function (startText, endText) { 101 | this.wrap(0, startText, this.originalLength(), endText); 102 | }, 103 | 104 | toString: function () { 105 | return this.text; 106 | } 107 | }; 108 | 109 | module.exports = InsertionText; -------------------------------------------------------------------------------- /lib/util/meta.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | var path = require('path'), 6 | fs = require('fs'), 7 | pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', '..', 'package.json'), 'utf8')); 8 | 9 | module.exports = { 10 | NAME: pkg.name, 11 | VERSION: pkg.version 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /lib/util/writer.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | var util = require('util'), 7 | EventEmitter = require('events').EventEmitter; 8 | 9 | function extend(cons, proto) { 10 | Object.keys(proto).forEach(function (k) { 11 | cons.prototype[k] = proto[k]; 12 | }); 13 | } 14 | 15 | /** 16 | * abstract interfaces for writing content 17 | * @class ContentWriter 18 | * @module io 19 | * @main io 20 | * @constructor 21 | */ 22 | //abstract interface for writing content 23 | function ContentWriter() { 24 | } 25 | 26 | ContentWriter.prototype = { 27 | /** 28 | * writes the specified string as-is 29 | * @method write 30 | * @param {String} str the string to write 31 | */ 32 | write: /* istanbul ignore next: abstract method */ function (/* str */) { 33 | throw new Error('write: must be overridden'); 34 | }, 35 | /** 36 | * writes the specified string with a newline at the end 37 | * @method println 38 | * @param {String} str the string to write 39 | */ 40 | println: function (str) { this.write(str + '\n'); } 41 | }; 42 | 43 | /** 44 | * abstract interface for writing files and assets. The caller is expected to 45 | * call `done` on the writer after it has finished writing all the required 46 | * files. The writer is an event-emitter that emits a `done` event when `done` 47 | * is called on it *and* all files have successfully been written. 48 | * 49 | * @class Writer 50 | * @constructor 51 | */ 52 | function Writer() { 53 | EventEmitter.call(this); 54 | } 55 | 56 | util.inherits(Writer, EventEmitter); 57 | 58 | extend(Writer, { 59 | /** 60 | * allows writing content to a file using a callback that is passed a content writer 61 | * @method writeFile 62 | * @param {String} file the name of the file to write 63 | * @param {Function} callback the callback that is called as `callback(contentWriter)` 64 | */ 65 | writeFile: /* istanbul ignore next: abstract method */ function (/* file, callback */) { 66 | throw new Error('writeFile: must be overridden'); 67 | }, 68 | /** 69 | * copies a file from source to destination 70 | * @method copyFile 71 | * @param {String} source the file to copy, found on the file system 72 | * @param {String} dest the destination path 73 | */ 74 | copyFile: /* istanbul ignore next: abstract method */ function (/* source, dest */) { 75 | throw new Error('copyFile: must be overridden'); 76 | }, 77 | /** 78 | * marker method to indicate that the caller is done with this writer object 79 | * The writer is expected to emit a `done` event only after this method is called 80 | * and it is truly done. 81 | * @method done 82 | */ 83 | done: /* istanbul ignore next: abstract method */ function () { 84 | throw new Error('done: must be overridden'); 85 | } 86 | }); 87 | 88 | module.exports = { 89 | Writer: Writer, 90 | ContentWriter: ContentWriter 91 | }; 92 | 93 | -------------------------------------------------------------------------------- /lib/util/yui-load-hook.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2012, Yahoo! Inc. All rights reserved. 3 | Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. 4 | */ 5 | 6 | //EXPERIMENTAL code: do not rely on this in anyway until the docs say it is allowed 7 | 8 | var path = require('path'), 9 | yuiRegexp = /yui-nodejs\.js$/; 10 | 11 | module.exports = function (matchFn, transformFn, verbose) { 12 | return function (file) { 13 | if (!file.match(yuiRegexp)) { 14 | return; 15 | } 16 | var YMain = require(file), 17 | YUI, 18 | loaderFn, 19 | origGet; 20 | 21 | if (YMain.YUI) { 22 | YUI = YMain.YUI; 23 | loaderFn = YUI.Env && YUI.Env.mods && YUI.Env.mods['loader-base'] ? YUI.Env.mods['loader-base'].fn : null; 24 | if (!loaderFn) { return; } 25 | if (verbose) { console.log('Applying YUI load post-hook'); } 26 | YUI.Env.mods['loader-base'].fn = function (Y) { 27 | loaderFn.call(null, Y); 28 | origGet = Y.Get._exec; 29 | Y.Get._exec = function (data, url, cb) { 30 | if (matchFn(url) || matchFn(path.resolve(url))) { //allow for relative paths as well 31 | if (verbose) { 32 | console.log('Transforming [' + url + ']'); 33 | } 34 | try { 35 | data = transformFn(data, url); 36 | } catch (ex) { 37 | console.error('Error transforming: ' + url + ' return original code'); 38 | console.error(ex.message || ex); 39 | if (ex.stack) { console.error(ex.stack); } 40 | } 41 | } 42 | return origGet.call(Y, data, url, cb); 43 | }; 44 | return Y; 45 | }; 46 | } 47 | }; 48 | }; 49 | 50 | -------------------------------------------------------------------------------- /misc/ast/assign.js: -------------------------------------------------------------------------------- 1 | var x = args[0]; 2 | output = x; 3 | 4 | -------------------------------------------------------------------------------- /misc/ast/block-label.js: -------------------------------------------------------------------------------- 1 | 2 | var tok = { type: 'keyword', length: 7 }, 3 | max_line_length = 6, 4 | prev_token = { type: "keyword" }; 5 | 6 | out: { 7 | if (tok.length > max_line_length) { 8 | if (prev_token) { 9 | if (prev_token.type == "keyword") break out; 10 | } 11 | switch (tok.type) { 12 | case "keyword": 13 | case "atom": 14 | case "name": 15 | case "punc": 16 | console.log(tok); 17 | break out; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /misc/ast/cond.js: -------------------------------------------------------------------------------- 1 | var x = 10, y, z; 2 | 3 | y = (z=10,x < 10) ? (z--,1) : (z++,2); 4 | console.log('Y=' + y); 5 | console.log('Z=' + z); 6 | -------------------------------------------------------------------------------- /misc/ast/defun-compact.js: -------------------------------------------------------------------------------- 1 | var foo =function () { var ret = 'foo'; 2 | return ret; 3 | }; 4 | 5 | function bar() { return 'bar'; } 6 | -------------------------------------------------------------------------------- /misc/ast/defun.js: -------------------------------------------------------------------------------- 1 | var foo =function () { 2 | var ret = 'foo'; 3 | return ret; 4 | }; 5 | 6 | function bar() { 7 | return 'bar'; 8 | } 9 | 10 | console.log(foo() + foo() + bar()); 11 | -------------------------------------------------------------------------------- /misc/ast/do-statement.js: -------------------------------------------------------------------------------- 1 | var i = 5; 2 | do 3 | i--; 4 | while (i > 0); 5 | 6 | -------------------------------------------------------------------------------- /misc/ast/dot.js: -------------------------------------------------------------------------------- 1 | var foo = { bar: { baz: [ 1, 2 ] } }; 2 | foo.bar.baz[0] = 3; 3 | 4 | -------------------------------------------------------------------------------- /misc/ast/eq.js: -------------------------------------------------------------------------------- 1 | if (1 === 2) { console.log('foo'); } 2 | -------------------------------------------------------------------------------- /misc/ast/expr.js: -------------------------------------------------------------------------------- 1 | var a = 5, 2 | b = a > 0 && a < 4; 3 | -------------------------------------------------------------------------------- /misc/ast/for-block.js: -------------------------------------------------------------------------------- 1 | for (var i =0; i<10; i++) { 2 | console.log(i); 3 | } 4 | -------------------------------------------------------------------------------- /misc/ast/for-compact.js: -------------------------------------------------------------------------------- 1 | for (var i =0; i<10; i++) console.log(i); 2 | -------------------------------------------------------------------------------- /misc/ast/for-multi.js: -------------------------------------------------------------------------------- 1 | for (var i = 0, j=0; i<10; i++, j++) 2 | console.log(i + j); 3 | -------------------------------------------------------------------------------- /misc/ast/for-statement.js: -------------------------------------------------------------------------------- 1 | var i; 2 | for (i =0; i<10; i++) 3 | console.log(i); 4 | -------------------------------------------------------------------------------- /misc/ast/forin-block.js: -------------------------------------------------------------------------------- 1 | var foo = { a: 10, b: 20 }; 2 | for (var k in foo) { 3 | console.log(k + '=' + foo[k]); 4 | } 5 | -------------------------------------------------------------------------------- /misc/ast/forin-compact.js: -------------------------------------------------------------------------------- 1 | var foo = { a: 10, b: 20 }; 2 | for (var k in foo) console.log(k + '=' + foo[k]); 3 | 4 | -------------------------------------------------------------------------------- /misc/ast/forin-statement.js: -------------------------------------------------------------------------------- 1 | var foo = { a: 10, b: 20 }; 2 | for (var k in foo) 3 | console.log(k + '=' + foo[k]); 4 | 5 | -------------------------------------------------------------------------------- /misc/ast/func.js: -------------------------------------------------------------------------------- 1 | var x = 2, output; 2 | output = x < 5 ? 3 | (function() { return 42; }()) 4 | : 15; 5 | -------------------------------------------------------------------------------- /misc/ast/if-block.js: -------------------------------------------------------------------------------- 1 | if (20 > 10) { 2 | console.log('>10'); 3 | } else if (x < 10) { 4 | console.log('<10'); 5 | } else { 6 | console.log('10'); 7 | } -------------------------------------------------------------------------------- /misc/ast/if-compact.js: -------------------------------------------------------------------------------- 1 | if (20 > 10) console.log('>10'); else if (x < 10) console.log('<10'); else console.log('10'); 2 | -------------------------------------------------------------------------------- /misc/ast/if-only.js: -------------------------------------------------------------------------------- 1 | if (20 > 10) console.log('Yay!'); 2 | -------------------------------------------------------------------------------- /misc/ast/if-statement.js: -------------------------------------------------------------------------------- 1 | var x = 25; 2 | if (x > 10) 3 | console.log('>10'); 4 | else if (x < 10) 5 | console.log('<10'); 6 | else 7 | console.log('10'); 8 | -------------------------------------------------------------------------------- /misc/ast/incr-slice.js: -------------------------------------------------------------------------------- 1 | var foo = { lineMap: { 1: 0 }}, bar = { 2 | 2: 0 3 | }; 4 | foo.lineMap[1]++; 5 | -------------------------------------------------------------------------------- /misc/ast/label.js: -------------------------------------------------------------------------------- 1 | var items= [],itemsPassed = 0; 2 | var i, j; 3 | 4 | function foo() 5 | { 6 | top: 7 | for (i = 0; i < items.length; i++){ 8 | for (j = 0; j < tests.length; j++) 9 | if (!tests[j].pass(items[i])) 10 | continue top; 11 | itemsPassed++; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /misc/ast/nested-if.js: -------------------------------------------------------------------------------- 1 | if (process.argv[2]) 2 | if (process.argv[3]) 3 | console.log('TT'); 4 | 5 | -------------------------------------------------------------------------------- /misc/ast/pfcall.js: -------------------------------------------------------------------------------- 1 | (function () { (function() { return 1; })(); })(); 2 | -------------------------------------------------------------------------------- /misc/ast/preamble.js: -------------------------------------------------------------------------------- 1 | if (typeof __yct__ === 'undefined') { 2 | __yct__ = {}; 3 | } 4 | var __ycf14f8be05b4467d47fccdd98300cc2c30 = __yct__['/Users/ananthk/code/ananthk/cover/lib/cover.js'] = {"path":"/Users/ananthk/code/ananthk/cover/lib/cover.js", "lines":{ 5 | "2":0, 6 | "11":0, 7 | "12":0, 8 | "15":0, 9 | "16":0, 10 | "18":0, 11 | "19":0, 12 | "20":0, 13 | "23":0, 14 | "24":0, 15 | "31":0, 16 | "32":0, 17 | "35":0, 18 | "36":0, 19 | "37":0, 20 | "38":0, 21 | "39":0, 22 | "42":0, 23 | "45":0, 24 | "46":0, 25 | "48":0, 26 | "49":0, 27 | "50":0, 28 | "52":0, 29 | "63":0, 30 | "67":0, 31 | "68":0, 32 | "73":0, 33 | "76":0, 34 | "77":0, 35 | "78":0, 36 | "82":0, 37 | "84":0, 38 | "85":0, 39 | "86":0, 40 | "87":0, 41 | "92":0, 42 | "94":0, 43 | "95":0, 44 | "96":0, 45 | "97":0, 46 | "98":0, 47 | "104":0, 48 | "105":0, 49 | "106":0, 50 | "107":0, 51 | "108":0, 52 | "110":0, 53 | "111":0, 54 | "112":0, 55 | "123":0, 56 | "124":0, 57 | "127":0, 58 | "133":0, 59 | "134":0, 60 | "138":0, 61 | "162":0, 62 | "164":0, 63 | "165":0, 64 | "166":0, 65 | "170":0, 66 | "172":0, 67 | "178":0, 68 | "179":0, 69 | "180":0, 70 | "182":0, 71 | "183":0, 72 | "184":0, 73 | "185":0, 74 | "186":0, 75 | "188":0, 76 | "192":0, 77 | "193":0, 78 | "194":0, 79 | "195":0, 80 | "196":0, 81 | "197":0, 82 | "199":0, 83 | "203":0, 84 | "204":0, 85 | "205":0, 86 | "206":0, 87 | "212":0, 88 | "213":0, 89 | "214":0, 90 | "215":0, 91 | "216":0, 92 | "217":0, 93 | "218":0, 94 | "219":0, 95 | "220":0, 96 | "222":0, 97 | "224":0, 98 | "226":0, 99 | "229":0 100 | }, "functions":{ 101 | "1":0, 102 | "2":0, 103 | "3":0, 104 | "4":0, 105 | "5":0, 106 | "6":0, 107 | "7":0, 108 | "8":0, 109 | "9":0, 110 | "10":0, 111 | "11":0, 112 | "12":0, 113 | "13":0, 114 | "14":0, 115 | "15":0, 116 | "16":0, 117 | "17":0, 118 | "18":0, 119 | "19":0, 120 | "20":0, 121 | "21":0, 122 | "22":0, 123 | "23":0, 124 | "24":0, 125 | "25":0, 126 | "26":0, 127 | "27":0 128 | }, "fnMap":{ 129 | "1":{ 130 | "name":"getCoverageVar", 131 | "line":11 132 | }, 133 | "2":{ 134 | "name":"getTrackerVar", 135 | "line":15 136 | }, 137 | "3":{ 138 | "name":"preamble", 139 | "line":23 140 | }, 141 | "4":{ 142 | "name":"protectedWalk", 143 | "line":31 144 | }, 145 | "5":{ 146 | "name":"coverStatementAst", 147 | "line":45 148 | }, 149 | "6":{ 150 | "name":"maybeCoverLine", 151 | "line":67 152 | }, 153 | "7":{ 154 | "name":"statementTracker", 155 | "line":76 156 | }, 157 | "8":{ 158 | "name":"anonymous (8)", 159 | "line":77 160 | }, 161 | "9":{ 162 | "name":"anonymous (9)", 163 | "line":78 164 | }, 165 | "10":{ 166 | "name":"lineCoverageInjector", 167 | "line":82 168 | }, 169 | "11":{ 170 | "name":"anonymous (11)", 171 | "line":84 172 | }, 173 | "12":{ 174 | "name":"anonymous (12)", 175 | "line":86 176 | }, 177 | "13":{ 178 | "name":"anonymous (13)", 179 | "line":87 180 | }, 181 | "14":{ 182 | "name":"funcCoverageInjector", 183 | "line":92 184 | }, 185 | "15":{ 186 | "name":"anonymous (15)", 187 | "line":94 188 | }, 189 | "16":{ 190 | "name":"anonymous (16)", 191 | "line":96 192 | }, 193 | "17":{ 194 | "name":"anonymous (17)", 195 | "line":97 196 | }, 197 | "18":{ 198 | "name":"getWalkers", 199 | "line":133 200 | }, 201 | "19":{ 202 | "name":"transformAst", 203 | "line":162 204 | }, 205 | "20":{ 206 | "name":"anonymous (20)", 207 | "line":165 208 | }, 209 | "21":{ 210 | "name":"instrument", 211 | "line":170 212 | }, 213 | "22":{ 214 | "name":"anonymous (22)", 215 | "line":195 216 | }, 217 | "23":{ 218 | "name":"convertToYuiFormat", 219 | "line":203 220 | }, 221 | "24":{ 222 | "name":"anonymous (24)", 223 | "line":205 224 | }, 225 | "25":{ 226 | "name":"anonymous (25)", 227 | "line":216 228 | }, 229 | "26":{ 230 | "name":"anonymous (26)", 231 | "line":217 232 | }, 233 | "27":{ 234 | "name":"anonymous (27)", 235 | "line":219 236 | } 237 | }}; -------------------------------------------------------------------------------- /misc/ast/switch-statement.js: -------------------------------------------------------------------------------- 1 | var foo = '10'; 2 | 3 | switch (foo) { 4 | case 5: 5 | case 10: 6 | console.log('10'); 7 | break; 8 | case 20: 9 | console.log('20'); 10 | break; 11 | default: 12 | console.log('other'); 13 | break; 14 | } 15 | -------------------------------------------------------------------------------- /misc/ast/try-block.js: -------------------------------------------------------------------------------- 1 | try { 2 | if (process.argv[2]) { 3 | throw "foo"; 4 | } 5 | } catch(ex) { 6 | console.log(ex); 7 | } 8 | //finally { 9 | // console.log('done'); 10 | //} -------------------------------------------------------------------------------- /misc/ast/try-statement.js: -------------------------------------------------------------------------------- 1 | try { if (process.argv[2]) throw "foo"; } catch(ex) { console.log(ex); } finally { console.log('done'); } -------------------------------------------------------------------------------- /misc/ast/while-block.js: -------------------------------------------------------------------------------- 1 | var i = 10; 2 | while (i--) { 3 | console.log(i); 4 | } -------------------------------------------------------------------------------- /misc/ast/while-compact.js: -------------------------------------------------------------------------------- 1 | var i=10; 2 | while (i--) console.log(i); -------------------------------------------------------------------------------- /misc/ast/while-for.js: -------------------------------------------------------------------------------- 1 | var i = 10, 2 | j; 3 | while (i--) 4 | for (j=0; j= 10 ? input : input + 100; 4 | }, 5 | depBar: function () { 6 | throw new Error("Let's not call this function in tests"); 7 | } 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /test/cli/sample-project/node_modules/post-require/hook.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | module.exports = function (matchFn, transformFn, verbose) { 3 | console.error('PRH: MatchFn was a ' + typeof matchFn); 4 | console.error('PRH: TransformFn was a ' + typeof transformFn); 5 | console.error('PRH: Verbose was ' + verbose); 6 | return function (file) { 7 | var base = path.basename(file); 8 | console.error('PRH: Saw ' + base); 9 | }; 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /test/cli/sample-project/node_modules/post-require/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "post-require", 3 | "version": "0.0.1", 4 | "main": "./hook.js" 5 | } -------------------------------------------------------------------------------- /test/cli/sample-project/test/amd-run.js: -------------------------------------------------------------------------------- 1 | // RequireJS uses `vm.runInThisContext` see issue #23 2 | // make sure we add hooks for it as well 3 | 4 | var rjs = require('requirejs'), 5 | assert = require('assert'); 6 | 7 | rjs.config({ 8 | baseUrl : __dirname, 9 | nodeRequire : require 10 | }); 11 | 12 | rjs(['../amd/lorem'], function(lorem){ 13 | var result = lorem(1, 2, 3); 14 | assert.equal(9, result); 15 | }); 16 | 17 | -------------------------------------------------------------------------------- /test/cli/sample-project/test/global-leak.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /*globals global */ 4 | var assert = require('assert'), 5 | g1 = {}, 6 | g2 = {}, 7 | keys; 8 | 9 | function copyGlobals(target) { 10 | Object.keys(global).forEach(function (k) { 11 | target[k] = true; 12 | }); 13 | } 14 | 15 | copyGlobals(g1); 16 | //the intention is to ensure that no extra globals are created by the require below 17 | //and that the coverage global is alive and well before the first piece of instrumented 18 | //code is required 19 | require('../lib/foo'); 20 | copyGlobals(g2); 21 | 22 | Object.keys(g2).forEach(function (k) { 23 | if (g1[k]) { delete g2[k]; } 24 | }); 25 | 26 | console.log(Object.keys(g2)); 27 | assert.equal(0, Object.keys(g2).length, 'New global var introduced in test'); 28 | keys = Object.keys(g1).filter(function (k) { return k.match(/\$\$cov_\d+\$\$/); }); 29 | assert.equal(1, keys.length, 'Coverage var not found!'); 30 | 31 | -------------------------------------------------------------------------------- /test/cli/sample-project/test/run.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var assert = require('assert'), 4 | foo = require('../lib/foo'), 5 | bar = require('../lib/bar'), 6 | shouldFail = !!process.argv[2] && process.argv[2] !== '0'; 7 | 8 | var r1 = foo(3, true); 9 | assert.ok(r1.name); 10 | assert.ok(r1.name.match(/^gen/)); 11 | assert.equal(103, r1.value); 12 | 13 | var r2 = foo(3, false); 14 | assert.ok(r2.name); 15 | assert.ok(r2.name.match(/^gen/)); 16 | assert.equal(3, r2.value); 17 | 18 | var r3 = foo(30, false); 19 | assert.ok(r3.name); 20 | assert.ok(r3.name.match(/^gen/)); 21 | if (shouldFail) { 22 | assert.equal(40, r3.value); 23 | } else { 24 | assert.equal(130, r3.value); 25 | } 26 | -------------------------------------------------------------------------------- /test/cli/sample-project/vendor/dummy_vendor_lib.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | vendorFoo: function (input) { 3 | return input < 10 ? input : input + 100; 4 | }, 5 | vendorBar: function () { 6 | throw new Error("Let's not call this function in tests"); 7 | } 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /test/cli/test-base-cli.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../cli-helper'); 3 | 4 | module.exports = { 5 | setUp: function (cb) { 6 | helper.resetOpts(); 7 | cb(); 8 | }, 9 | "should provide helpful errors when nothing passed": function (test) { 10 | helper.runCommand(null, [], function (results) { 11 | test.ok(!results.succeeded()); 12 | test.ok(results.grepError(/Try "istanbul help" for usage/)); 13 | test.done(); 14 | }); 15 | }, 16 | "should provide helpful errors when only flags passed in": function (test) { 17 | helper.runCommand(null, [ '-v', '-x' ], function (results) { 18 | test.ok(!results.succeeded()); 19 | test.ok(results.grepError(/Try "istanbul help" for usage/)); 20 | test.done(); 21 | }); 22 | }, 23 | "should provide a good message on an invalid command": function (test) { 24 | helper.runCommand('instrumentation', [ '--root', 'a/nonexistent/path' ], function (results) { 25 | test.ok(!results.succeeded()); 26 | test.ok(results.grepError(/Invalid command \[instrumentation\], allowed values/)); 27 | test.ok(results.grepError(/Try "istanbul help" for usage/)); 28 | test.done(); 29 | }); 30 | }, 31 | "should print a stack trace on uncaught exception": function (test) { 32 | helper.runCommand('instrument', [ '--root', 'a/nonexistent/path' ], function (results) { 33 | test.ok(!results.succeeded()); 34 | test.ok(!results.grepError(/Try "istanbul help" for usage/)); 35 | test.ok(results.grepError(/ENOENT/)); 36 | test.done(); 37 | }); 38 | }, 39 | "should provide configuration help": function (test) { 40 | helper.runCommand('help', [ 'config' ], function (results) { 41 | test.ok(results.succeeded()); 42 | test.ok(results.grepError(/Configuring istanbul/)); 43 | test.done(); 44 | }); 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /test/cli/test-clover-report.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | fs = require('fs'), 4 | rimraf = require('rimraf'), 5 | mkdirp = require('mkdirp'), 6 | helper = require('../cli-helper'), 7 | DIR = path.resolve(__dirname, 'sample-project'), 8 | OUTPUT_DIR = path.resolve(DIR, 'coverage'), 9 | COVER_COMMAND = 'cover', 10 | runCover = helper.runCommand.bind(null, COVER_COMMAND), 11 | Reporter = require('../../lib/report/clover'), 12 | Collector = require('../../lib/collector'), 13 | existsSync = fs.existsSync || path.existsSync; 14 | 15 | module.exports = { 16 | setUp: function (cb) { 17 | rimraf.sync(OUTPUT_DIR); 18 | mkdirp.sync(OUTPUT_DIR); 19 | helper.resetOpts(); 20 | runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) { 21 | cb(); 22 | }); 23 | }, 24 | tearDown: function (cb) { 25 | rimraf.sync(OUTPUT_DIR); 26 | cb(); 27 | }, 28 | "should produce clover report consuming coverage file": function (test) { 29 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 30 | reportFile = path.resolve(OUTPUT_DIR, 'clover.xml'), 31 | reporter = new Reporter({ dir: OUTPUT_DIR }), 32 | obj, 33 | collector = new Collector(); 34 | 35 | obj = JSON.parse(fs.readFileSync(file, 'utf8')); 36 | collector.add(obj); 37 | reporter.writeReport(collector, true); 38 | test.ok(existsSync(reportFile)); 39 | test.done(); 40 | }, 41 | "should produce clover.xml at cwd when no options specified": function (test) { 42 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 43 | reportFile = path.resolve('clover.xml'), 44 | reporter = new Reporter(), 45 | collector = new Collector(); 46 | 47 | collector.add(JSON.parse(fs.readFileSync(file, 'utf8'))); 48 | reporter.writeReport(collector, true); 49 | test.ok(existsSync(reportFile)); 50 | fs.unlinkSync(reportFile); 51 | test.done(); 52 | } 53 | }; 54 | 55 | -------------------------------------------------------------------------------- /test/cli/test-cobertura-report.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | fs = require('fs'), 4 | rimraf = require('rimraf'), 5 | mkdirp = require('mkdirp'), 6 | helper = require('../cli-helper'), 7 | DIR = path.resolve(__dirname, 'sample-project'), 8 | OUTPUT_DIR = path.resolve(DIR, 'coverage'), 9 | COVER_COMMAND = 'cover', 10 | runCover = helper.runCommand.bind(null, COVER_COMMAND), 11 | Reporter = require('../../lib/report/cobertura'), 12 | Collector = require('../../lib/collector'), 13 | existsSync = fs.existsSync || path.existsSync; 14 | 15 | module.exports = { 16 | setUp: function (cb) { 17 | rimraf.sync(OUTPUT_DIR); 18 | mkdirp.sync(OUTPUT_DIR); 19 | helper.resetOpts(); 20 | runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) { 21 | cb(); 22 | }); 23 | }, 24 | tearDown: function (cb) { 25 | rimraf.sync(OUTPUT_DIR); 26 | cb(); 27 | }, 28 | "should produce cobertura report consuming coverage file": function (test) { 29 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 30 | reportFile = path.resolve(OUTPUT_DIR, 'cobertura-coverage.xml'), 31 | reporter = new Reporter({ dir: OUTPUT_DIR }), 32 | obj, 33 | collector = new Collector(); 34 | 35 | obj = JSON.parse(fs.readFileSync(file, 'utf8')); 36 | collector.add(obj); 37 | reporter.writeReport(collector, true); 38 | test.ok(existsSync(reportFile)); 39 | test.done(); 40 | }, 41 | "should produce coverage.xml at cwd when no options specified": function (test) { 42 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 43 | reportFile = path.resolve('cobertura-coverage.xml'), 44 | reporter = new Reporter(), 45 | collector = new Collector(); 46 | 47 | collector.add(JSON.parse(fs.readFileSync(file, 'utf8'))); 48 | reporter.writeReport(collector, true); 49 | test.ok(existsSync(reportFile)); 50 | fs.unlinkSync(reportFile); 51 | test.done(); 52 | } 53 | }; 54 | 55 | -------------------------------------------------------------------------------- /test/cli/test-include-pid.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | fs = require('fs'), 4 | glob = require('glob'), 5 | rimraf = require('rimraf'), 6 | mkdirp = require('mkdirp'), 7 | COMMAND = 'cover', 8 | DIR = path.resolve(__dirname, 'sample-project'), 9 | OUTPUT_DIR = path.resolve(DIR, 'coverage'), 10 | helper = require('../cli-helper'), 11 | existsSync = fs.existsSync || path.existsSync, 12 | run = helper.runCommand.bind(null, COMMAND); 13 | 14 | module.exports = { 15 | setUp: function (cb) { 16 | rimraf.sync(OUTPUT_DIR); 17 | mkdirp.sync(OUTPUT_DIR); 18 | helper.resetOpts(); 19 | cb(); 20 | }, 21 | tearDown: function (cb) { 22 | rimraf.sync(OUTPUT_DIR); 23 | cb(); 24 | }, 25 | "should have no pid by default": function (test) { 26 | helper.setOpts({ lazyHook : true }); 27 | run([ 'test/run.js', '-v'], function (results) { 28 | test.ok(results.succeeded()); 29 | test.ok(results.grepError(/Module load hook:/)); 30 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info'))); 31 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report'))); 32 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json'))); 33 | var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')), 34 | filtered; 35 | filtered = Object.keys(coverage).filter(function (k) { return k.match(/foo/) || k.match(/bar/); }); 36 | test.ok(filtered.length === 2); 37 | test.done(); 38 | }); 39 | }, 40 | "should have no pid if requested": function (test) { 41 | helper.setOpts({ lazyHook : true }); 42 | run([ 'test/run.js', '-v', '--no-include-pid'], function (results) { 43 | test.ok(results.succeeded()); 44 | test.ok(results.grepError(/Module load hook:/)); 45 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info'))); 46 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report'))); 47 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json'))); 48 | var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')), 49 | filtered; 50 | filtered = Object.keys(coverage).filter(function (k) { return k.match(/foo/) || k.match(/bar/); }); 51 | test.ok(filtered.length === 2); 52 | test.done(); 53 | }); 54 | }, 55 | "should have pid if requested": function (test) { 56 | helper.setOpts({ lazyHook : true }); 57 | run([ 'test/run.js', '-v', '--include-pid'], function (results) { 58 | test.ok(results.succeeded()); 59 | test.ok(results.grepError(/Module load hook:/)); 60 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info'))); 61 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report'))); 62 | glob(path.resolve(OUTPUT_DIR, 'coverage-*.json'), function(err, matches) { 63 | test.ifError(err, 'pid coverage file glob error'); 64 | test.ok(matches.length !== 0, 'pid coverage file not found'); 65 | matches.forEach(function(file) { 66 | test.ok(existsSync(file)); 67 | var coverage = JSON.parse(fs.readFileSync(file), 'utf8'), 68 | filtered; 69 | filtered = Object.keys(coverage).filter(function (k) { return k.match(/foo/) || k.match(/bar/); }); 70 | test.ok(filtered.length === 2); 71 | }); 72 | test.done(); 73 | }); 74 | }); 75 | } 76 | }; 77 | -------------------------------------------------------------------------------- /test/cli/test-json-report.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | fs = require('fs'), 4 | rimraf = require('rimraf'), 5 | mkdirp = require('mkdirp'), 6 | helper = require('../cli-helper'), 7 | DIR = path.resolve(__dirname, 'sample-project'), 8 | OUTPUT_DIR = path.resolve(DIR, 'coverage'), 9 | COVER_COMMAND = 'cover', 10 | runCover = helper.runCommand.bind(null, COVER_COMMAND), 11 | Reporter = require('../../lib/report/json'), 12 | Collector = require('../../lib/collector'), 13 | existsSync = fs.existsSync || path.existsSync; 14 | 15 | module.exports = { 16 | setUp: function (cb) { 17 | rimraf.sync(OUTPUT_DIR); 18 | mkdirp.sync(OUTPUT_DIR); 19 | helper.resetOpts(); 20 | runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) { 21 | cb(); 22 | }); 23 | }, 24 | tearDown: function (cb) { 25 | rimraf.sync(OUTPUT_DIR); 26 | cb(); 27 | }, 28 | "should produce json report consuming coverage file": function (test) { 29 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 30 | jsonFile = path.resolve(OUTPUT_DIR, 'coverage-final.json'), 31 | reporter = new Reporter({ dir: OUTPUT_DIR }), 32 | obj, 33 | reportObj, 34 | collector = new Collector(); 35 | 36 | obj = JSON.parse(fs.readFileSync(file, 'utf8')); 37 | collector.add(obj); 38 | reporter.writeReport(collector, true); 39 | test.ok(existsSync(jsonFile)); 40 | reportObj = JSON.parse(fs.readFileSync(jsonFile, 'utf8')); 41 | Object.keys(reportObj).forEach(function (k) { 42 | delete reportObj[k].l; 43 | }); 44 | test.deepEqual(obj, reportObj); 45 | test.done(); 46 | }, 47 | "should produce coverage-final.json at cwd when no options specified": function (test) { 48 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 49 | jsonFile = path.resolve('coverage-final.json'), 50 | reporter = new Reporter(), 51 | collector = new Collector(); 52 | 53 | collector.add(JSON.parse(fs.readFileSync(file, 'utf8'))); 54 | reporter.writeReport(collector, true); 55 | test.ok(existsSync(jsonFile)); 56 | fs.unlinkSync(jsonFile); 57 | test.done(); 58 | } 59 | }; 60 | 61 | -------------------------------------------------------------------------------- /test/cli/test-json-summary-report.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | fs = require('fs'), 4 | rimraf = require('rimraf'), 5 | mkdirp = require('mkdirp'), 6 | helper = require('../cli-helper'), 7 | DIR = path.resolve(__dirname, 'sample-project'), 8 | OUTPUT_DIR = path.resolve(DIR, 'coverage'), 9 | COVER_COMMAND = 'cover', 10 | runCover = helper.runCommand.bind(null, COVER_COMMAND), 11 | Reporter = require('../../lib/report/json-summary'), 12 | Collector = require('../../lib/collector'), 13 | existsSync = fs.existsSync || path.existsSync, 14 | objectUtils = require('../../lib/object-utils'); 15 | 16 | module.exports = { 17 | setUp: function (cb) { 18 | rimraf.sync(OUTPUT_DIR); 19 | mkdirp.sync(OUTPUT_DIR); 20 | helper.resetOpts(); 21 | runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) { 22 | cb(); 23 | }); 24 | }, 25 | tearDown: function (cb) { 26 | rimraf.sync(OUTPUT_DIR); 27 | cb(); 28 | }, 29 | "should produce json report consuming coverage file": function (test) { 30 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 31 | jsonFile = path.resolve(OUTPUT_DIR, 'coverage-summary.json'), 32 | reporter = new Reporter({ dir: OUTPUT_DIR }), 33 | obj, 34 | reportObj, 35 | collector = new Collector(); 36 | 37 | obj = JSON.parse(fs.readFileSync(file, 'utf8')); 38 | collector.add(obj); 39 | reporter.writeReport(collector, true); 40 | 41 | var summaries = [], 42 | finalSummary; 43 | collector.files().forEach(function (file) { 44 | summaries.push(objectUtils.summarizeFileCoverage(collector.fileCoverageFor(file))); 45 | }); 46 | finalSummary = objectUtils.mergeSummaryObjects.apply(null, summaries); 47 | obj.total = finalSummary; 48 | 49 | test.ok(existsSync(jsonFile)); 50 | reportObj = JSON.parse(fs.readFileSync(jsonFile, 'utf8')); 51 | test.deepEqual(Object.keys(obj).sort(), Object.keys(reportObj).sort()); 52 | test.done(); 53 | }, 54 | "should produce coverage-summary.json at cwd when no options specified": function (test) { 55 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 56 | jsonFile = path.resolve('coverage-summary.json'), 57 | reporter = new Reporter(), 58 | collector = new Collector(); 59 | 60 | collector.add(JSON.parse(fs.readFileSync(file, 'utf8'))); 61 | reporter.writeReport(collector, true); 62 | test.ok(existsSync(jsonFile)); 63 | fs.unlinkSync(jsonFile); 64 | test.done(); 65 | } 66 | }; 67 | 68 | -------------------------------------------------------------------------------- /test/cli/test-lcov-report.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | fs = require('fs'), 4 | rimraf = require('rimraf'), 5 | mkdirp = require('mkdirp'), 6 | helper = require('../cli-helper'), 7 | DIR = path.resolve(__dirname, 'sample-project'), 8 | OUTPUT_DIR = path.resolve(DIR, 'coverage'), 9 | COVER_COMMAND = 'cover', 10 | runCover = helper.runCommand.bind(null, COVER_COMMAND), 11 | Reporter = require('../../lib/report/lcov'), 12 | Collector = require('../../lib/collector'), 13 | existsSync = fs.existsSync || path.existsSync; 14 | 15 | module.exports = { 16 | setUp: function (cb) { 17 | rimraf.sync(OUTPUT_DIR); 18 | mkdirp.sync(OUTPUT_DIR); 19 | helper.resetOpts(); 20 | runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) { 21 | cb(); 22 | }); 23 | }, 24 | tearDown: function (cb) { 25 | rimraf.sync(OUTPUT_DIR); 26 | cb(); 27 | }, 28 | "should produce lcov report consuming coverage file": function (test) { 29 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 30 | lcovFile = path.resolve(OUTPUT_DIR, 'lcov.info'), 31 | lcovReport = path.resolve(OUTPUT_DIR, 'lcov-report'), 32 | reporter = new Reporter({ dir: OUTPUT_DIR }), 33 | obj, 34 | collector = new Collector(); 35 | 36 | obj = JSON.parse(fs.readFileSync(file, 'utf8')); 37 | collector.add(obj); 38 | reporter.writeReport(collector, true); 39 | test.ok(existsSync(lcovFile)); 40 | test.ok(existsSync(lcovReport)); 41 | test.done(); 42 | }, 43 | "should produce lcov.info and lcov-report at cwd when no options specified": function (test) { 44 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 45 | lcovFile = path.resolve('lcov.info'), 46 | lcovReport = path.resolve('lcov-report'), 47 | reporter = new Reporter(), 48 | collector = new Collector(); 49 | 50 | collector.add(JSON.parse(fs.readFileSync(file, 'utf8'))); 51 | reporter.writeReport(collector, true); 52 | test.ok(existsSync(lcovFile)); 53 | fs.unlinkSync(lcovFile); 54 | rimraf.sync(lcovReport); 55 | test.done(); 56 | } 57 | }; 58 | 59 | -------------------------------------------------------------------------------- /test/cli/test-lcovonly-report.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | fs = require('fs'), 4 | rimraf = require('rimraf'), 5 | mkdirp = require('mkdirp'), 6 | helper = require('../cli-helper'), 7 | DIR = path.resolve(__dirname, 'sample-project'), 8 | OUTPUT_DIR = path.resolve(DIR, 'coverage'), 9 | COVER_COMMAND = 'cover', 10 | runCover = helper.runCommand.bind(null, COVER_COMMAND), 11 | Reporter = require('../../lib/report/lcovonly'), 12 | Collector = require('../../lib/collector'), 13 | existsSync = fs.existsSync || path.existsSync; 14 | 15 | module.exports = { 16 | setUp: function (cb) { 17 | rimraf.sync(OUTPUT_DIR); 18 | mkdirp.sync(OUTPUT_DIR); 19 | helper.resetOpts(); 20 | runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) { 21 | cb(); 22 | }); 23 | }, 24 | tearDown: function (cb) { 25 | rimraf.sync(OUTPUT_DIR); 26 | cb(); 27 | }, 28 | "should produce lcovonly report consuming coverage file": function (test) { 29 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 30 | lcovFile = path.resolve(OUTPUT_DIR, 'lcov.info'), 31 | reporter = new Reporter({ dir: OUTPUT_DIR }), 32 | obj, 33 | collector = new Collector(), 34 | lines, 35 | numFiles; 36 | 37 | obj = JSON.parse(fs.readFileSync(file, 'utf8')); 38 | numFiles = Object.keys(obj).length; 39 | collector.add(obj); 40 | reporter.writeReport(collector, true); 41 | test.ok(existsSync(lcovFile)); 42 | lines = fs.readFileSync(lcovFile, 'utf8').split(/\r?\n/); 43 | test.ok(lines.filter(function (line) { return line.indexOf('SF:') === 0; }).length, numFiles); 44 | test.ok(lines.filter(function (line) { return line.indexOf('TN:') === 0; }).length, numFiles); 45 | test.ok(lines.filter(function (line) { return line.indexOf('end_of_record') === 0; }).length, numFiles); 46 | test.done(); 47 | }, 48 | "should produce lcov.info at cwd when no options specified": function (test) { 49 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 50 | lcovFile = path.resolve('lcov.info'), 51 | reporter = new Reporter(), 52 | collector = new Collector(); 53 | 54 | collector.add(JSON.parse(fs.readFileSync(file, 'utf8'))); 55 | reporter.writeReport(collector, true); 56 | test.ok(existsSync(lcovFile)); 57 | fs.unlinkSync(lcovFile); 58 | test.done(); 59 | } 60 | }; 61 | 62 | -------------------------------------------------------------------------------- /test/cli/test-lots-of-files.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | /*jslint nomen: true */ 3 | var path = require('path'), 4 | fs = require('fs'), 5 | rimraf = require('rimraf'), 6 | mkdirp = require('mkdirp'), 7 | COMMAND = 'report', 8 | COVER_COMMAND = 'cover', 9 | outputDir = path.resolve(__dirname, 'output'), 10 | helper = require('../cli-helper'), 11 | existsSync = fs.existsSync || path.existsSync, 12 | run = helper.runCommand.bind(null, COMMAND), 13 | runCover = helper.runCommand.bind(null, COVER_COMMAND), 14 | MAX_FILES = 1000; 15 | 16 | function fileFor(i) { 17 | return 'f' + i + '.js'; 18 | } 19 | 20 | function createCode() { 21 | var i, 22 | libDir = path.resolve(outputDir, 'lib'), 23 | code; 24 | 25 | mkdirp.sync(libDir); 26 | 27 | for (i = 0; i < MAX_FILES; i += 1) { 28 | code = 'module.exports = function () { return "hello-' + i + '"; };\n'; 29 | fs.writeFileSync(path.resolve(libDir, fileFor(i)), code, 'utf8'); 30 | } 31 | } 32 | 33 | function createTest() { 34 | var i, 35 | testDir = path.resolve(outputDir, 'test'), 36 | code = 'var assert = require("assert");\n'; 37 | 38 | mkdirp.sync(testDir); 39 | 40 | for (i = 0; i < MAX_FILES; i += 1) { 41 | code += 'assert.equal("hello-' + i + '", require("../lib/' + fileFor(i) + '")());\n'; 42 | } 43 | fs.writeFileSync(path.resolve(testDir, 'test.js'), code, 'utf8'); 44 | } 45 | 46 | module.exports = { 47 | setUp: function (cb) { 48 | helper.resetOpts(); 49 | helper.setOpts({ cwd: outputDir }); 50 | mkdirp.sync(outputDir); 51 | cb(); 52 | }, 53 | tearDown: function (cb) { 54 | rimraf.sync(outputDir); 55 | cb(); 56 | }, 57 | 58 | 'should report correctly with 1000 code files': function (test) { 59 | var coverageDir = path.resolve(outputDir, 'coverage'), 60 | lcovFile = path.resolve(coverageDir, 'lcov.info'), 61 | lcovDir = path.resolve(coverageDir, 'lcov-report'), 62 | jsonFile = path.resolve(coverageDir, 'coverage.json'); 63 | createCode(); 64 | createTest(); 65 | runCover([ 'test/test.js' ], function (results) { 66 | test.ok(results.succeeded()); 67 | test.ok(existsSync(lcovFile)); 68 | test.ok(existsSync(lcovDir)); 69 | test.ok(existsSync(jsonFile)); 70 | var obj = JSON.parse(fs.readFileSync(jsonFile, 'utf8')); 71 | test.equal(MAX_FILES, Object.keys(obj).length); 72 | rimraf.sync(lcovDir); 73 | fs.unlinkSync(lcovFile); 74 | run([], function (results) { 75 | test.ok(results.succeeded()); 76 | test.ok(existsSync(lcovFile)); 77 | test.ok(existsSync(lcovDir)); 78 | test.ok(existsSync(jsonFile)); 79 | test.ok(fs.readdirSync(path.resolve(lcovDir, 'lib')).length >= MAX_FILES); 80 | test.done(); 81 | }); 82 | }); 83 | } 84 | }; 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /test/cli/test-none-report.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var Reporter = require('../../lib/report/none'), 3 | Report = require('../../lib/report'); 4 | 5 | module.exports = { 6 | "should be a noop without any failures": function (test) { 7 | var reporter = new Reporter(); 8 | test.doesNotThrow(function () { reporter.writeReport(); }); 9 | test.done(); 10 | }, 11 | "should throw when Base report class writeMethod called": function (test) { 12 | var r = new Report(); 13 | test.throws(function () { r.writeReport(); }, /must be overridden/); 14 | test.done(); 15 | } 16 | }; 17 | 18 | -------------------------------------------------------------------------------- /test/cli/test-report-command.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | fs = require('fs'), 4 | rimraf = require('rimraf'), 5 | mkdirp = require('mkdirp'), 6 | COMMAND = 'report', 7 | COVER_COMMAND = 'cover', 8 | DIR = path.resolve(__dirname, 'sample-project'), 9 | OUTPUT_DIR = path.resolve(DIR, 'coverage'), 10 | helper = require('../cli-helper'), 11 | existsSync = fs.existsSync || path.existsSync, 12 | run = helper.runCommand.bind(null, COMMAND), 13 | runCover = helper.runCommand.bind(null, COVER_COMMAND), 14 | Report = require('../../lib/report'); 15 | 16 | module.exports = { 17 | setUp: function (cb) { 18 | rimraf.sync(OUTPUT_DIR); 19 | mkdirp.sync(OUTPUT_DIR); 20 | helper.resetOpts(); 21 | runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) { 22 | cb(); 23 | }); 24 | }, 25 | tearDown: function (cb) { 26 | rimraf.sync(OUTPUT_DIR); 27 | cb(); 28 | }, 29 | "should run reports consuming coverage file with lcov default": function (test) { 30 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json'))); 31 | run([], function (results) { 32 | test.ok(results.succeeded()); 33 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info'))); 34 | test.ok(fs.readFileSync(path.resolve(OUTPUT_DIR, 'lcov.info'), 'utf8') !== ''); 35 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report'))); 36 | test.done(); 37 | }); 38 | }, 39 | "should run reports with specific format": function (test) { 40 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json'))); 41 | run([ 'html' ], function (results) { 42 | test.ok(results.succeeded()); 43 | test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov.info'))); 44 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'index.html'))); 45 | test.done(); 46 | }); 47 | }, 48 | "should barf on invalid format": function (test) { 49 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json'))); 50 | run([ 'gcov' ], function (results) { 51 | test.ok(!results.succeeded()); 52 | test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov.info'))); 53 | test.ok(results.grepError(/Invalid report format/)); 54 | test.done(); 55 | }); 56 | }, 57 | "should respect input pattern": function (test) { 58 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json'))); 59 | run([ 'lcovonly', '--include', '**/foobar.json' ], function (results) { 60 | test.ok(results.succeeded()); 61 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info'))); 62 | test.equal('', fs.readFileSync(path.resolve(OUTPUT_DIR, 'lcov.info'), 'utf8')); 63 | test.done(); 64 | }); 65 | }, 66 | "should respect legacy input pattern": function (test) { 67 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json'))); 68 | run([ 'lcovonly', '**/foobar.json' ], function (results) { 69 | test.ok(results.succeeded()); 70 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info'))); 71 | test.equal('', fs.readFileSync(path.resolve(OUTPUT_DIR, 'lcov.info'), 'utf8')); 72 | test.ok(results.grepError(/DEPRECATION WARNING/)); 73 | test.done(); 74 | }); 75 | }, 76 | "should run all possible reports when requested": function (test) { 77 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json'))); 78 | run([ '-v' ].concat(Report.getReportList()), function (results) { 79 | test.ok(results.succeeded()); 80 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info'))); 81 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'cobertura-coverage.xml'))); 82 | test.done(); 83 | }); 84 | }, 85 | "should default to configuration value": function (test) { 86 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json'))); 87 | run([ '--config', 'config.istanbul.yml' ], function (results) { 88 | test.ok(results.succeeded()); 89 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'foo.xml'))); 90 | test.done(); 91 | }); 92 | } 93 | }; -------------------------------------------------------------------------------- /test/cli/test-teamcity-report.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | fs = require('fs'), 4 | rimraf = require('rimraf'), 5 | mkdirp = require('mkdirp'), 6 | helper = require('../cli-helper'), 7 | DIR = path.resolve(__dirname, 'sample-project'), 8 | OUTPUT_DIR = path.resolve(DIR, 'coverage'), 9 | COVER_COMMAND = 'cover', 10 | runCover = helper.runCommand.bind(null, COVER_COMMAND), 11 | Reporter = require('../../lib/report/teamcity'), 12 | Collector = require('../../lib/collector'), 13 | existsSync = fs.existsSync || path.existsSync; 14 | 15 | module.exports = { 16 | setUp: function (cb) { 17 | rimraf.sync(OUTPUT_DIR); 18 | mkdirp.sync(OUTPUT_DIR); 19 | helper.resetOpts(); 20 | runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) { 21 | cb(); 22 | }); 23 | }, 24 | tearDown: function (cb) { 25 | rimraf.sync(OUTPUT_DIR); 26 | cb(); 27 | }, 28 | "should produce teamcity service messages": function (test) { 29 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 30 | outFile = path.resolve(OUTPUT_DIR, 'teamcity.txt'), 31 | reporter = new Reporter({ dir: OUTPUT_DIR, file: "teamcity.txt" }), 32 | obj, 33 | reportLines, 34 | collector = new Collector(); 35 | 36 | obj = JSON.parse(fs.readFileSync(file, 'utf8')); 37 | collector.add(obj); 38 | reporter.writeReport(collector, true); 39 | test.ok(existsSync(outFile)); 40 | reportLines = fs.readFileSync(outFile, 'utf8'); 41 | 42 | test.ok(reportLines.indexOf('Code Coverage Summary') > 0); 43 | test.ok(reportLines.indexOf('CodeCoverageB') > 0); 44 | test.ok(reportLines.indexOf('CodeCoverageAbsMCovered') > 0); 45 | test.ok(reportLines.indexOf('CodeCoverageAbsMTotal') > 0); 46 | test.ok(reportLines.indexOf('CodeCoverageM') > 0); 47 | test.ok(reportLines.indexOf('CodeCoverageAbsLCovered') > 0); 48 | test.ok(reportLines.indexOf('CodeCoverageAbsLTotal') > 0); 49 | test.ok(reportLines.indexOf('CodeCoverageL') > 0); 50 | 51 | test.done(); 52 | }, 53 | "should allow to set custom Block name": function(test){ 54 | var file = path.resolve(OUTPUT_DIR, 'coverage.json'), 55 | outFile = path.resolve(OUTPUT_DIR, 'teamcity-named.txt'), 56 | reporter = new Reporter({ dir: OUTPUT_DIR, file: "teamcity-named.txt", blockName: 'My Custom Block Name' }), 57 | obj, 58 | reportLines, 59 | collector = new Collector(); 60 | 61 | obj = JSON.parse(fs.readFileSync(file, 'utf8')); 62 | collector.add(obj); 63 | reporter.writeReport(collector, true); 64 | test.ok(existsSync(outFile)); 65 | reportLines = fs.readFileSync(outFile, 'utf8'); 66 | 67 | test.ok(reportLines.indexOf('##teamcity[blockOpened name=\'My Custom Block Name\']') > 0); 68 | test.ok(reportLines.indexOf('##teamcity[blockClosed name=\'My Custom Block Name\']') > 0); 69 | test.done(); 70 | } 71 | }; 72 | 73 | -------------------------------------------------------------------------------- /test/cli/test-test-command.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | fs = require('fs'), 4 | rimraf = require('rimraf'), 5 | mkdirp = require('mkdirp'), 6 | COMMAND = 'test', 7 | DIR = path.resolve(__dirname, 'sample-project'), 8 | OUTPUT_DIR = path.resolve(DIR, 'coverage'), 9 | helper = require('../cli-helper'), 10 | existsSync = fs.existsSync || path.existsSync, 11 | run = helper.runCommand.bind(null, COMMAND); 12 | 13 | module.exports = { 14 | setUp: function (cb) { 15 | rimraf.sync(OUTPUT_DIR); 16 | mkdirp.sync(OUTPUT_DIR); 17 | helper.resetOpts(); 18 | cb(); 19 | }, 20 | tearDown: function (cb) { 21 | rimraf.sync(OUTPUT_DIR); 22 | cb(); 23 | }, 24 | "should skip coverage when npm coverage is disabled": function (test) { 25 | run([ 'test/run.js' ], { npm_config_coverage: '' }, function (results) { 26 | test.ok(results.succeeded()); 27 | test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov.info'))); 28 | test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov-report'))); 29 | test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'coverage.json'))); 30 | test.done(); 31 | }); 32 | }, 33 | "should run coverage when npm coverage is enabled": function (test) { 34 | helper.setOpts({ lazyHook : true }); 35 | run([ 'test/run.js' ], { npm_config_coverage: '1' }, function (results) { 36 | test.ok(results.succeeded()); 37 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info'))); 38 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report'))); 39 | test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json'))); 40 | test.done(); 41 | }); 42 | } 43 | }; -------------------------------------------------------------------------------- /test/cli/test-text-lcov-report.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | fs = require('fs'), 4 | mkdirp = require('mkdirp'), 5 | rimraf = require('rimraf'), 6 | helper = require('../cli-helper'), 7 | DIR = path.resolve(__dirname, 'sample-project'), 8 | OUTPUT_DIR = path.resolve(DIR, 'coverage'), 9 | COVER_COMMAND = 'cover', 10 | runCover = helper.runCommand.bind(null, COVER_COMMAND), 11 | Reporter = require('../../lib/report/text-lcov'), 12 | Collector = require('../../lib/collector'); 13 | 14 | module.exports = { 15 | setUp: function (cb) { 16 | rimraf.sync(OUTPUT_DIR); 17 | mkdirp.sync(OUTPUT_DIR); 18 | helper.resetOpts(); 19 | runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) { 20 | cb(); 21 | }); 22 | }, 23 | tearDown: function (cb) { 24 | rimraf.sync(OUTPUT_DIR); 25 | cb(); 26 | }, 27 | "should print lcov.info to standard out": function (test) { 28 | var output = '', 29 | file = path.resolve(OUTPUT_DIR, 'coverage.json'), 30 | reporter = new Reporter({ 31 | log: function (ln) { 32 | output += ln; 33 | } 34 | }), 35 | collector = new Collector(); 36 | 37 | collector.add(JSON.parse(fs.readFileSync(file, 'utf8'))); 38 | reporter.writeReport(collector, true); 39 | test.ok(output.match('TN:SF:'), 'failed to output report'); 40 | test.done(); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen:true*/ 2 | var path = require('path'), 3 | buildDir = path.resolve(__dirname, '..', 'build'); 4 | 5 | module.exports = { 6 | setSelfCover: function (flag) { 7 | process.env.SELF_COVER = flag ? 1 : ''; 8 | }, 9 | isSelfCover: function () { 10 | return process.env.SELF_COVER; 11 | }, 12 | getBuildDir: function () { 13 | return buildDir; 14 | }, 15 | getCoverageDir: function () { 16 | return path.resolve(buildDir, 'coverage'); 17 | } 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /test/es6.js: -------------------------------------------------------------------------------- 1 | var esprima = require('esprima'); 2 | 3 | function tryThis(str, feature) { 4 | try { 5 | /*jshint evil: true */ 6 | eval(str); 7 | } catch (ex) { 8 | console.error('ES6 feature [' + feature + '] is not available in this environment'); 9 | return false; 10 | } 11 | 12 | // esprima parses sources with sourceType 'script' per default. 13 | // The only way to enable `import`/`export` is to parse as sourceType 'module'. 14 | try { 15 | try { 16 | esprima.parse(str); 17 | } catch (ex) { 18 | esprima.parse(str, { sourceType: 'module' }); 19 | } 20 | } catch (ex) { 21 | console.error('ES6 feature [' + feature + '] is not yet supported by esprima mainline'); 22 | return false; 23 | } 24 | 25 | return true; 26 | } 27 | 28 | module.exports = { 29 | isYieldAvailable: function () { 30 | return tryThis('function *foo() { yield 1; }', 'yield'); 31 | }, 32 | 33 | isSuperAvailable: function () { 34 | return tryThis('class Test extends Object { constructor() { super(); } }\nnew Test();', 'super'); 35 | }, 36 | 37 | isForOfAvailable: function () { 38 | return tryThis('function *foo() { yield 1; }\n' + 39 | 'for (var k of foo()) {}', 'for-of'); 40 | }, 41 | 42 | isArrowFnAvailable: function () { 43 | return tryThis('[1 ,2, 3].map(x => x * x)', 'arrow function'); 44 | }, 45 | 46 | isImportAvailable: function () { 47 | return tryThis('import fs from "fs"', 'import'); 48 | }, 49 | 50 | isExportAvailable: function () { 51 | // We can test instrumentation of exports even if the environment doesn't support them. 52 | return true; 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /test/instrumentation/test-do.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | /*jshint maxlen: 500 */ 7 | module.exports = { 8 | "with a simple do-while": { 9 | setUp: function (cb) { 10 | code = [ 11 | 'var x = args[0], i=0;', 12 | 'do { i++; } while (i < x);', 13 | 'output = i;' 14 | ]; 15 | verifier = helper.verifier(__filename, code); 16 | cb(); 17 | }, 18 | 19 | "should provide correct line coverage": function (test) { 20 | verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 10, 3: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 10, '4': 1 } }); 21 | test.done(); 22 | }, 23 | 24 | "should cover single-entry into while": function (test) { 25 | verifier.verify(test, [ -1 ], 1, { lines: { 1: 1, 2: 1, 3: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1 } }); 26 | test.done(); 27 | } 28 | }, 29 | "with a block do-while on separate line": { 30 | setUp: function (cb) { 31 | code = [ 32 | 'var x = args[0], i=0;', 33 | 'do { ', 34 | ' i++; ', 35 | '} while (i < x);', 36 | 'output = i;' 37 | ]; 38 | verifier = helper.verifier(__filename, code); 39 | cb(); 40 | }, 41 | 42 | "should provide correct line coverage": function (test) { 43 | verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 1, 3: 10, 5: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 10, '4': 1 } }); 44 | test.done(); 45 | }, 46 | 47 | "should cover single-entry into while": function (test) { 48 | verifier.verify(test, [ -1 ], 1, { lines: { 1: 1, 2: 1, 3: 1, 5: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1 } }); 49 | test.done(); 50 | } 51 | }, 52 | "with an ignore inside a do-while": { 53 | setUp: function (cb) { 54 | code = [ 55 | 'do { ', 56 | ' /* istanbul ignore next */ ', 57 | ' console.log("hi");', 58 | '} while (false);', 59 | 'output = 10' 60 | ]; 61 | verifier = helper.verifier(__filename, code); 62 | cb(); 63 | }, 64 | 65 | "should mark statement as skipped": function (test) { 66 | verifier.verify(test, [], 10, { lines: { '1': 1, '3': 1, '5': 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 1 } }); 67 | var cov = verifier.getFileCoverage(); 68 | test.equal(true, cov.statementMap[2].skip); 69 | test.done(); 70 | } 71 | } 72 | }; 73 | 74 | -------------------------------------------------------------------------------- /test/instrumentation/test-es6-arrow-fn.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | /*jshint maxlen: 500 */ 7 | if (require('../es6').isArrowFnAvailable()) { 8 | module.exports = { 9 | "with an expression arrow expression": { 10 | setUp: function (cb) { 11 | code = [ 12 | 'var input = args', 13 | 'output = input.map(x => x * x)' 14 | ]; 15 | verifier = helper.verifier(__filename, code); 16 | cb(); 17 | }, 18 | 19 | "should cover it correctly": function (test) { 20 | verifier.verify(test, [1, 2, 3, 4], [1, 4, 9, 16], { lines: { '1': 1, '2': 4 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 4} }); 21 | test.done(); 22 | }, 23 | 24 | "should report no calls correctly": function (test) { 25 | verifier.verify(test, [], [], { lines: { '1': 1, '2': 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 0} }); 26 | test.done(); 27 | } 28 | }, 29 | "with a block arrow expression": { 30 | setUp: function (cb) { 31 | code = [ 32 | 'var input = args', 33 | 'output = input.map(x => { return x * x; })' 34 | ]; 35 | verifier = helper.verifier(__filename, code); 36 | cb(); 37 | }, 38 | 39 | "should cover it correctly": function (test) { 40 | verifier.verify(test, [1, 2, 3, 4], [1, 4, 9, 16], { lines: { '1': 1, '2': 4 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 4} }); 41 | test.done(); 42 | }, 43 | 44 | "should report no calls correctly": function (test) { 45 | verifier.verify(test, [], [], { lines: { '1': 1, '2': 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 0} }); 46 | test.done(); 47 | } 48 | } 49 | }; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /test/instrumentation/test-es6-export.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | if (require('../es6').isExportAvailable()) { 7 | module.exports = { 8 | 'should cover export statements': function (test) { 9 | code = [ 10 | 'export function bar() { return 2 }', 11 | 'output = bar()' 12 | ]; 13 | verifier = helper.verifier(__filename, code, { 14 | esModules: true, 15 | noAutoWrap: true 16 | }); 17 | verifier.verify(test, [], 2, { 18 | lines: {'1': 1, '2': 1}, 19 | branches: {}, 20 | functions: {'1': 1}, 21 | statements: {'1': 1, '2': 1, '3': 1} 22 | }); 23 | test.done(); 24 | }, 25 | 26 | 'should cover export declarations': function (test) { 27 | code = [ 28 | 'export var a = 2, b = 3;', 29 | 'output = a + b' 30 | ]; 31 | verifier = helper.verifier(__filename, code, { 32 | esModules: true, 33 | noAutoWrap: true 34 | }); 35 | verifier.verify(test, [], 5, { 36 | lines: {'1':1, '2': 1}, 37 | branches: {}, 38 | functions: {}, 39 | statements: {'1': 1, '2': 1} 40 | }); 41 | test.done(); 42 | } 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /test/instrumentation/test-es6-forof.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | /*jshint maxlen: 500 */ 7 | if (require('../es6').isForOfAvailable()) { 8 | module.exports = { 9 | "with a simple for-in": { 10 | setUp: function (cb) { 11 | code = [ 12 | 'function *x() { yield 1; yield 2; };', 13 | 'var k;', 14 | 'output = 0;', 15 | 'for (k of x()) {', 16 | ' output += k;', 17 | '}' 18 | ]; 19 | verifier = helper.verifier(__filename, code); 20 | cb(); 21 | }, 22 | 23 | "should cover loop exactly once": function (test) { 24 | verifier.verify(test, [], 3, { lines: { '1': 1, '2': 1, '3': 1, '4': 1, '5': 2 }, branches: {}, functions: { '1': 1 }, statements: { '1': 1, '2': 1, '3': 1, '4': 1, '5': 1, '6': 1, '7': 2 } }); 25 | test.done(); 26 | } 27 | }, 28 | "with a simple for-of declaring the loop initializer": { 29 | setUp: function (cb) { 30 | code = [ 31 | 'function *x() { yield 1; yield 2; };', 32 | 'output = 0;', 33 | 'for (var k of x()) {', 34 | ' output += k;', 35 | '}' 36 | ]; 37 | verifier = helper.verifier(__filename, code); 38 | cb(); 39 | }, 40 | 41 | "should cover loop exactly once": function (test) { 42 | verifier.verify(test, [], 3, { lines: { '1': 1, '2': 1, '3': 1, '4': 2 }, branches: {}, functions: { '1': 1 }, statements: { '1': 1, '2': 1, '3': 1, '4': 1, '5': 1, '6': 2 } }); 43 | test.done(); 44 | } 45 | } 46 | }; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /test/instrumentation/test-es6-import.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | if (require('../es6').isImportAvailable()) { 7 | module.exports = { 8 | 'should cover import statements': function (test) { 9 | code = [ 10 | 'import util from "util";', 11 | 'output = util.format(args[0], args[1]);' 12 | ]; 13 | verifier = helper.verifier(__filename, code, { 14 | esModules: true, 15 | noAutoWrap: true 16 | }); 17 | verifier.verify(test, ['foo:%s', 'bar'], 'foo:bar', { 18 | lines: {'2': 1}, 19 | branches: {}, 20 | functions: {}, 21 | statements: {'1': 1} 22 | }); 23 | test.done(); 24 | } 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /test/instrumentation/test-es6-super.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | if (require('../es6').isSuperAvailable()) { 7 | module.exports = { 8 | 'should cover super in constructor': function (test) { 9 | code = [ 10 | 'class A {', 11 | ' constructor(x) {', 12 | ' this.x = x;', 13 | ' }', 14 | ' getX() {', 15 | ' return "x";', 16 | ' }', 17 | '}', 18 | 'class B extends A {', 19 | ' constructor(x) {', 20 | ' super(x);', 21 | ' }', 22 | ' getX() {', 23 | ' return this.x;', 24 | ' }', 25 | '}', 26 | 'output = (new B("super")).getX();' 27 | ]; 28 | verifier = helper.verifier(__filename, code); 29 | verifier.verify(test, ['super'], 'super', { 30 | lines: { 3: 1, 6: 0, 11: 1, 14: 1, 17: 1 }, 31 | branches: {}, 32 | functions: { 1: 1, 2: 0, 3: 1, 4: 1 }, 33 | statements: { 1: 1, 2: 0, 3: 1, 4: 1, 5: 1 } 34 | }); 35 | test.done(); 36 | }, 37 | 'should cover super in method': function (test) { 38 | code = [ 39 | 'class A {', 40 | ' constructor(x) {', 41 | ' this.x = x;', 42 | ' }', 43 | ' getX() {', 44 | ' return "x";', 45 | ' }', 46 | '}', 47 | 'class B extends A {', 48 | ' constructor(x) {', 49 | ' super(x);', 50 | ' }', 51 | ' getX() {', 52 | ' return super.getX();', 53 | ' }', 54 | '}', 55 | 'output = (new B("super")).getX();' 56 | ]; 57 | verifier = helper.verifier(__filename, code); 58 | verifier.verify(test, ['super'], 'x', { 59 | lines: { 3: 1, 6: 1, 11: 1, 14: 1, 17: 1 }, 60 | branches: {}, 61 | functions: { 1: 1, 2: 1, 3: 1, 4: 1 }, 62 | statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 1 } 63 | }); 64 | test.done(); 65 | } 66 | }; 67 | } 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /test/instrumentation/test-es6-yield.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | if (require('../es6').isYieldAvailable()) { 7 | module.exports = { 8 | 'should cover yield statements in generators': function (test) { 9 | code = [ 10 | 'function *yielder() {', 11 | ' yield 1;', 12 | ' yield 2;', 13 | ' yield 3;', 14 | '}', 15 | 'var x = 0, y = yielder();', 16 | 'for (var i = 0; i < 2; i += 1 ) {', 17 | ' x += y.next().value;', 18 | '}', 19 | 'output = x;' 20 | ]; 21 | verifier = helper.verifier(__filename, code); 22 | verifier.verify(test, [], 3, { 23 | lines: { '1': 1, '2': 1, '3': 1, '4': 0, '6': 1, '7': 1, '8': 2, '10': 1 }, 24 | branches: {}, 25 | functions: { 1: 1 }, 26 | statements: { '1': 1, '2': 1, '3': 1, '4': 0, '5': 1, '6': 1, '7': 2, '8': 1 } 27 | }); 28 | test.done(); 29 | } 30 | }; 31 | } 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/instrumentation/test-expressions.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | /*jshint maxlen: 500 */ 7 | module.exports = { 8 | "with a simple expression": { 9 | setUp: function (cb) { 10 | code = [ 11 | 'var x = args[0] > 0 && args[0] < 5;', 12 | 'output = x;' 13 | ]; 14 | verifier = helper.verifier(__filename, code); 15 | cb(); 16 | }, 17 | 18 | "should cover line and one branch": function (test) { 19 | verifier.verify(test, [ -1 ], false, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 0 ]}, functions: {}, statements: { '1': 1, '2': 1 } }); 20 | test.done(); 21 | }, 22 | "should cover line, both branches but return false": function (test) { 23 | verifier.verify(test, [ 10 ], false, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 1 ]}, functions: {}, statements: { '1': 1, '2': 1 } }); 24 | test.done(); 25 | }, 26 | "should cover line, both branches and return true": function (test) { 27 | verifier.verify(test, [ 3 ], true, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 1 ]}, functions: {}, statements: { '1': 1, '2': 1 } }); 28 | test.done(); 29 | } 30 | }, 31 | "with a complex expression": { 32 | setUp: function (cb) { 33 | code = [ 34 | 'var x = args[0] > 0 && (args[0] < 5 || args[0] > 10);', 35 | 'output = x;' 36 | ]; 37 | verifier = helper.verifier(__filename, code); 38 | cb(); 39 | }, 40 | 41 | "should cover line and one branch": function (test) { 42 | verifier.verify(test, [ -1 ], false, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 0, 0 ]}, functions: {}, statements: { '1': 1, '2': 1 } }); 43 | test.done(); 44 | }, 45 | "should cover line, both branches but return false": function (test) { 46 | verifier.verify(test, [ 9 ], false, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 1, 1 ]}, functions: {}, statements: { '1': 1, '2': 1 } }); 47 | test.done(); 48 | }, 49 | "should cover line, both branches and return true": function (test) { 50 | verifier.verify(test, [ 3 ], true, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 1, 0 ]}, functions: {}, statements: { '1': 1, '2': 1 } }); 51 | test.done(); 52 | } 53 | }, 54 | "with an array expression with empty positions": { 55 | setUp: function (cb) { 56 | code = [ 57 | 'var x = [args[0], , args[1], ];', 58 | 'output = x.indexOf(args[1]) === x.length - 1 && x[0] !== x[1];' 59 | ]; 60 | verifier = helper.verifier(__filename, code); 61 | cb(); 62 | }, 63 | 64 | "should not barf in any way": function (test) { 65 | verifier.verify(test, [ 1, 5 ], true, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 1 ]}, functions: {}, statements: { '1': 1, '2': 1 } }); 66 | test.done(); 67 | } 68 | } 69 | }; 70 | 71 | -------------------------------------------------------------------------------- /test/instrumentation/test-forin.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | /*jshint maxlen: 500 */ 7 | module.exports = { 8 | "with a simple for-in": { 9 | setUp: function (cb) { 10 | code = [ 11 | 'var x = { a: args[0], b: args[1] }, k;', 12 | 'output = 0;', 13 | 'for (k in x) {', 14 | ' if (x.hasOwnProperty(k) && x[k]) {', 15 | ' output += x[k];', 16 | ' }', 17 | '}' 18 | ]; 19 | verifier = helper.verifier(__filename, code); 20 | cb(); 21 | }, 22 | 23 | "should cover loop exactly once": function (test) { 24 | verifier.verify(test, [ 10, 0 ], 10, { lines: { 1: 1, 2: 1, 3: 1, 4: 2, 5: 1 }, branches: { 1: [1, 1], 2: [ 2, 2 ] }, functions: {}, statements: { 1: 1, 2: 1, 3: 1, 4: 2, 5: 1 } }); 25 | test.done(); 26 | } 27 | }, 28 | 29 | "with a simple for-in declaring the loop initializer": { 30 | setUp: function (cb) { 31 | code = [ 32 | 'var x = { a: args[0], b: args[1] };', 33 | 'output = 0;', 34 | 'for (var k in x) {', 35 | ' if (x.hasOwnProperty(k) && x[k]) {', 36 | ' output += x[k];', 37 | ' }', 38 | '}' 39 | ]; 40 | verifier = helper.verifier(__filename, code); 41 | cb(); 42 | }, 43 | 44 | "should cover loop exactly once": function (test) { 45 | verifier.verify(test, [ 10, 0 ], 10, { lines: { 1: 1, 2: 1, 3: 1, 4: 2, 5: 1 }, branches: { 1: [1, 1], 2: [ 2, 2 ] }, functions: {}, statements: { 1: 1, 2: 1, 3: 1, 4: 2, 5: 1 } }); 46 | test.done(); 47 | } 48 | } 49 | }; 50 | 51 | -------------------------------------------------------------------------------- /test/instrumentation/test-functions.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | /*jshint maxlen: 500 */ 7 | module.exports = { 8 | "with a simple function": { 9 | setUp: function (cb) { 10 | code = [ 11 | 'var x = args[0];', 12 | 'function foo() {', 13 | ' return 42;', 14 | '}', 15 | 'output = x < 5 ? foo() : 15;' 16 | ]; 17 | verifier = helper.verifier(__filename, code); 18 | cb(); 19 | }, 20 | 21 | "should cover line and function": function (test) { 22 | verifier.verify(test, [ 2 ], 42, { lines: { 1: 1, 2: 1, 3: 1, 5: 1 }, branches: { 1: [1, 0 ] }, functions: { 1: 1 }, statements: { '1': 1, '2': 1, '3': 1, '4': 1 } }); 23 | test.done(); 24 | }, 25 | 26 | "should not cover function": function (test) { 27 | verifier.verify(test, [ 10 ], 15, { lines: { 1: 1, 2: 1, 3: 0, 5: 1 }, branches: { 1: [ 0, 1 ]}, functions: { 1: 0 }, statements: { '1': 1, '2': 1, '3': 0, '4': 1 } }); 28 | test.done(); 29 | } 30 | }, 31 | 32 | "with an anonymous function": { 33 | setUp: function (cb) { 34 | code = [ 35 | 'var x = args[0];', 36 | 'output = x < 5 ? (function() { return 42; }()) : 15;' 37 | ]; 38 | verifier = helper.verifier(__filename, code); 39 | cb(); 40 | }, 41 | 42 | "should cover line and function": function (test) { 43 | verifier.verify(test, [ 2 ], 42, { lines: { 1: 1, 2: 1 }, branches: { 1: [1, 0 ] }, functions: { 1: 1 }, statements: { '1': 1, '2': 1, '3': 1 } }); 44 | test.done(); 45 | }, 46 | 47 | "should not cover function": function (test) { 48 | verifier.verify(test, [ 10 ], 15, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 0, 1 ]}, functions: { 1: 0 }, statements: { '1': 1, '2': 1, '3': 0 } }); 49 | test.done(); 50 | } 51 | }, 52 | 53 | "with an anonymous function on newline": { 54 | setUp: function (cb) { 55 | code = [ 56 | 'var x = args[0];', 57 | 'output = x < 5 ? ', 58 | ' (function() { ', 59 | ' return 42; ', 60 | '}())', 61 | ' : 15;' 62 | ]; 63 | verifier = helper.verifier(__filename, code); 64 | cb(); 65 | }, 66 | 67 | "should cover line and function": function (test) { 68 | verifier.verify(test, [ 2 ], 42, { lines: { 1: 1, 2: 1, 4: 1 }, branches: { 1: [1, 0 ] }, functions: { 1: 1 }, statements: { 1: 1, 2: 1, 3: 1} }); 69 | test.done(); 70 | }, 71 | 72 | "should not cover function": function (test) { 73 | verifier.verify(test, [ 10 ], 15, { lines: { 1: 1, 2: 1, 4: 0 }, branches: { 1: [ 0, 1 ]}, functions: { 1: 0 }, statements: { 1: 1, 2: 1, 3: 0 } }); 74 | test.done(); 75 | } 76 | }, 77 | 78 | "with a function declared in an 'unreachable' place": { 79 | setUp: function (cb) { 80 | code = [ 81 | 'function foo(x) {', 82 | ' return bar(x);', 83 | ' function bar(y) { return y * 2 }', 84 | '}', 85 | 'output = args[0] < 2 ? 2: foo(args[0]);' 86 | ]; 87 | verifier = helper.verifier(__filename, code); 88 | cb(); 89 | }, 90 | 91 | "should not cover functions but should cover declarations": function (test) { 92 | verifier.verify(test, [ 1 ], 2, { lines: { 1: 1, 2: 0, 3: 1, 5: 1 }, branches: { 1: [1, 0 ] }, functions: { 1: 0, 2: 0 }, statements: { 1: 1, 2: 0, 3: 1, 4: 0, 5: 1} }); 93 | test.done(); 94 | }, 95 | 96 | "should cover functions and declarations": function (test) { 97 | verifier.verify(test, [ 10 ], 20, { lines: { 1: 1, 2: 1, 3: 1, 5: 1 }, branches: { 1: [ 0, 1 ]}, functions: { 1: 1, 2: 1 }, statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 1 } }); 98 | test.done(); 99 | } 100 | } 101 | }; 102 | 103 | -------------------------------------------------------------------------------- /test/instrumentation/test-locations.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | verifier, 4 | code; 5 | 6 | /*jshint maxlen: 500 */ 7 | module.exports = { 8 | "with a function and if branch all in one line": { 9 | setUp: function (cb) { 10 | code = [ 11 | 'function foo() { if (true) { return "bar"; } else { return "baz"; } } output = foo();' 12 | ]; 13 | verifier = helper.verifier(__filename, code); 14 | cb(); 15 | }, 16 | "should report locations correctly": function (test) { 17 | verifier.verify(test, [], 'bar', { lines: { '1': 1 }, branches: { 1: [ 1, 0 ] }, functions: { 1: 1 }, statements: { '1': 1, '2': 1, '3': 1, '4': 0, '5': 1 }}); 18 | var cov = verifier.getFileCoverage(); 19 | test.deepEqual({ 20 | "1": { 21 | "name": "foo", 22 | "line": 1, 23 | "loc": { 24 | "start": { 25 | "line": 1, 26 | "column": 0 27 | }, 28 | "end": { 29 | "line": 1, 30 | "column": 15 31 | } 32 | } 33 | } 34 | }, cov.fnMap); 35 | test.deepEqual({ 36 | "1": { 37 | "line": 1, 38 | "type": "if", 39 | "locations": [ 40 | { 41 | "start": { 42 | "line": 1, 43 | "column": 17 44 | }, 45 | "end": { 46 | "line": 1, 47 | "column": 17 48 | } 49 | }, 50 | { 51 | "start": { 52 | "line": 1, 53 | "column": 17 54 | }, 55 | "end": { 56 | "line": 1, 57 | "column": 17 58 | } 59 | } 60 | ] 61 | } 62 | }, cov.branchMap); 63 | test.deepEqual({ 64 | "start": { 65 | "line": 1, 66 | "column": 0 67 | }, 68 | "end": { 69 | "line": 1, 70 | "column": 69 71 | } 72 | }, cov.statementMap[1]); 73 | test.done(); 74 | } 75 | }, 76 | "with a switch statement all in one line": { 77 | setUp: function (cb) { 78 | code = [ 79 | [ 80 | 'output = "unknown";', 81 | 'switch (args[0]) {', 82 | ' case "1": output = "one"; break;', 83 | ' case "2": output = "two"; break;', 84 | '}' 85 | ].join(' ') 86 | ]; 87 | verifier = helper.verifier(__filename, code); 88 | cb(); 89 | }, 90 | "should report locations correctly": function (test) { 91 | verifier.verify(test, [ "1" ], "one", { lines: { 1: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 0, 6: 0 } }); 92 | var cov = verifier.getFileCoverage(); 93 | test.deepEqual({ 94 | "1": { 95 | "line": 1, 96 | "type": "switch", 97 | "locations": [ 98 | { 99 | "start": { 100 | "line": 1, 101 | "column": 42 102 | }, 103 | "end": { 104 | "line": 1, 105 | "column": 74 106 | } 107 | }, 108 | { 109 | "start": { 110 | "line": 1, 111 | "column": 78 112 | }, 113 | "end": { 114 | "line": 1, 115 | "column": 110 116 | } 117 | } 118 | ] 119 | } 120 | }, cov.branchMap); 121 | test.done(); 122 | } 123 | } 124 | }; 125 | 126 | -------------------------------------------------------------------------------- /test/instrumentation/test-misc.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var Instrumenter = require('../../lib/instrumenter'), 3 | esprima = require('esprima'), 4 | instrumenter; 5 | 6 | module.exports = { 7 | "missing type attributes": { 8 | setUp: function (cb) { 9 | instrumenter = new Instrumenter(); 10 | cb(); 11 | }, 12 | "barfs when a type attr is missing for a general node": function (test) { 13 | var ast = esprima.parse('var foo = 1;', { loc: true }); 14 | delete ast.body[0].type; 15 | try { 16 | instrumenter.instrumentASTSync(ast); 17 | test.fail('instrumentation succeeded when it should not have'); 18 | } catch (ex) { 19 | //ok 20 | } 21 | test.done(); 22 | }, 23 | "but succeeds when a property node in an AST does not have the type attr": function (test) { 24 | var ast = esprima.parse('var foo = { a: 1 };', { loc: true }); 25 | delete ast.body[0].declarations[0].init.properties[0].type; 26 | try { 27 | instrumenter.instrumentASTSync(ast); 28 | } catch (ex) { 29 | test.fail('instrumentation should have succeeded but did not'); 30 | } 31 | test.done(); 32 | } 33 | }, 34 | "sourcemap generation": { 35 | setUp: function (cb) { 36 | instrumenter = new Instrumenter(); 37 | cb(); 38 | }, 39 | "lastSourceMap returns sourcemap (if available) for last instrumented file": function (test) { 40 | var instrumenterOpts = instrumenter.opts.codegenerationoptions; 41 | instrumenter.opts.codegenerationoptions = { 42 | sourceMap: 'bar', 43 | sourceMapWithCode: true 44 | }; 45 | try { 46 | instrumenter.instrumentSync('var foo = { a: 1 };'); 47 | if (typeof instrumenter.lastSourceMap() !== 'object') { 48 | test.fail('sourcemap should be available'); 49 | } 50 | 51 | instrumenter.opts.codegenerationoptions = instrumenterOpts; 52 | instrumenter.instrumentSync('var foo = { a: 1 };'); 53 | if (instrumenter.lastSourceMap() !== null) { 54 | test.fail('sourcemap should not be available'); 55 | } 56 | } catch (ex) { 57 | test.fail('instrumentation should have succeeded but did not'); 58 | } 59 | test.done(); 60 | } 61 | } 62 | }; 63 | 64 | -------------------------------------------------------------------------------- /test/instrumentation/test-strict.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | module.exports = { 7 | "with a function expression that uses strict": { 8 | setUp: function (cb) { 9 | code = [ 10 | '(function () {', 11 | ' "use strict";', 12 | ' var x = Object.freeze({ foo: 1 });', 13 | ' try {', 14 | ' x.foo = 2;', 15 | ' output = "fail";', 16 | ' } catch (ex) {', 17 | ' output = "pass";', 18 | ' }', 19 | '}());' 20 | ]; 21 | verifier = helper.verifier(__filename, code); 22 | cb(); 23 | }, 24 | 25 | "should cover one statement less": function (test) { 26 | verifier.verify(test, [], "pass", { 27 | statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 0, 6: 1 }, 28 | lines: { 1: 1, 3: 1, 4: 1, 5: 1, 6: 0, 8: 1 }, 29 | branches: {}, 30 | functions: { 1: 1} 31 | }); 32 | test.done(); 33 | } 34 | }, 35 | "with a function declaration that uses strict": { 36 | setUp: function (cb) { 37 | code = [ 38 | 'function foo() {', 39 | ' "use strict";', 40 | ' var x = Object.freeze({ foo: 1 });', 41 | ' try {', 42 | ' x.foo = 2;', 43 | ' output = "fail";', 44 | ' } catch (ex) {', 45 | ' output = "pass";', 46 | ' }', 47 | '}', 48 | 'foo();' 49 | ]; 50 | verifier = helper.verifier(__filename, code); 51 | cb(); 52 | }, 53 | 54 | "should cover one statement less": function (test) { 55 | verifier.verify(test, [], "pass", { 56 | statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 0, 6: 1, 7: 1 }, 57 | lines: { 1: 1, 3: 1, 4: 1, 5: 1, 6: 0, 8: 1, 11: 1 }, 58 | branches: {}, 59 | functions: { 1: 1} 60 | }); 61 | test.done(); 62 | } 63 | }, 64 | "with a function declaration that looks like strict but is not": { 65 | setUp: function (cb) { 66 | code = [ 67 | 'function foo() {', 68 | ' 1;', 69 | ' "use strict";', 70 | ' var x = Object.freeze({ foo: 1 });', 71 | ' try {', 72 | ' x.foo = 2;', 73 | ' output = "fail";', 74 | ' } catch (ex) {', 75 | ' output = "pass";', 76 | ' }', 77 | '}', 78 | 'foo();' 79 | ]; 80 | verifier = helper.verifier(__filename, code); 81 | cb(); 82 | }, 83 | 84 | "should cover all statements as usual": function (test) { 85 | verifier.verify(test, [], "fail", { 86 | statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0, 9: 1 }, 87 | lines: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 9: 0, 12: 1 }, 88 | branches: {}, 89 | functions: { 1: 1} 90 | }); 91 | test.done(); 92 | } 93 | }, 94 | "with a file-level strict declaration": { 95 | setUp: function (cb) { 96 | code = [ 97 | ' "use strict";', 98 | ' var x = Object.freeze({ foo: 1 });', 99 | ' try {', 100 | ' x.foo = 2;', 101 | ' output = "fail";', 102 | ' } catch (ex) {', 103 | ' output = "pass";', 104 | ' }' 105 | ]; 106 | verifier = helper.verifier(__filename, code); 107 | cb(); 108 | }, 109 | 110 | "should correctly interpret the strict statement": function (test) { 111 | // use strict semantics still do not work since it is not top-level but called from vm.runInThisContext 112 | verifier.verify(test, [], "fail", { 113 | //however statements and lines should line up 114 | statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 0 }, 115 | lines: { 2: 1, 3: 1, 4: 1, 5: 1, 7: 0 }, 116 | branches: {}, 117 | functions: {} 118 | }); 119 | test.done(); 120 | } 121 | } 122 | }; 123 | 124 | -------------------------------------------------------------------------------- /test/instrumentation/test-try.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | /*jshint maxlen: 500 */ 7 | module.exports = { 8 | "with an simple try catch": { 9 | setUp: function (cb) { 10 | code = [ 11 | 'try {', 12 | ' if (args[0] === "X") { throw "foo"; }', 13 | ' output = args[0];', 14 | '} catch (ex) {', 15 | ' output="Y";', 16 | '} finally {', 17 | ' output += 1;', 18 | '}' 19 | ]; 20 | verifier = helper.verifier(__filename, code); 21 | cb(); 22 | }, 23 | 24 | "should cover happy path correctly": function (test) { 25 | verifier.verify(test, [1], 2, { lines: { '1': 1, '2': 1, 3: 1, 5: 0, 7: 1 }, branches: { 1: [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, 4: 1, 5: 0, 6: 1} }); 26 | test.done(); 27 | }, 28 | "should cover sad path correctly": function (test) { 29 | verifier.verify(test, ['X'], 'Y1', { lines: { '1': 1, '2': 1, 3: 0, 5: 1, 7: 1 }, branches: { 1: [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, 4: 0, 5: 1, 6: 1} }); 30 | test.done(); 31 | } 32 | } 33 | }; 34 | 35 | -------------------------------------------------------------------------------- /test/instrumentation/test-while.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | /*jshint maxlen: 500 */ 7 | module.exports = { 8 | "with a simple while": { 9 | setUp: function (cb) { 10 | code = [ 11 | 'var x = args[0], i=0;', 12 | 'while (i < x) i++;', 13 | 'output = i;' 14 | ]; 15 | verifier = helper.verifier(__filename, code); 16 | cb(); 17 | }, 18 | 19 | "should cover loop exactly once": function (test) { 20 | verifier.verify(test, [ 1 ], 1, { lines: { 1: 1, 2: 1, 3: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1 } }); 21 | test.done(); 22 | }, 23 | "should cover loop multiple times": function (test) { 24 | verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 10, 3: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 10, '4': 1 } }); 25 | test.done(); 26 | } 27 | }, 28 | "with a simple while - statement on a different line": { 29 | setUp: function (cb) { 30 | code = [ 31 | 'var x = args[0], i=0;', 32 | 'while (i < x)', 33 | ' i++;', 34 | 'output = i;' 35 | ]; 36 | verifier = helper.verifier(__filename, code); 37 | cb(); 38 | }, 39 | 40 | "should cover loop one time": function (test) { 41 | verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 1, 3: 10, 4: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 10, '4': 1 } }); 42 | test.done(); 43 | }, 44 | 45 | "should not cover loop at all": function (test) { 46 | verifier.verify(test, [ -1 ], 0, { lines: { 1: 1, 2: 1, 3: 0, 4: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 1 } }); 47 | test.done(); 48 | } 49 | }, 50 | "with a simple while in block": { 51 | setUp: function (cb) { 52 | code = [ 53 | 'var x = args[0], i=0;', 54 | 'while (i < x) { i++; }', 55 | 'output = i;' 56 | ]; 57 | verifier = helper.verifier(__filename, code); 58 | cb(); 59 | }, 60 | 61 | "should cover multi-loop exactly once": function (test) { 62 | verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 10, 3: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 10, '4': 1 } }); 63 | test.done(); 64 | } 65 | }, 66 | "with a labeled while": { 67 | setUp: function (cb) { 68 | code = [ 69 | 'var x = args[0], i=0, j=0, output = 0;', 70 | 'outer:', 71 | ' while (i++ < x) {', 72 | ' j =0;', 73 | ' while (j++ < i) {', 74 | ' output++;', 75 | ' if (j === 2) continue outer;', 76 | ' }', 77 | ' }' 78 | ]; 79 | verifier = helper.verifier(__filename, code); 80 | cb(); 81 | }, 82 | 83 | "should provide line/branch coverage when all branches exercised": function (test) { 84 | verifier.verify(test, [ 10 ], 19, { 85 | lines: { '1': 1, '2': 1, '3': 1, '4': 10, '5': 10, '6': 19, '7': 19 }, 86 | branches: { '1': [ 9, 10 ] }, 87 | functions: {}, 88 | statements: { '1': 1, '2': 1, '3': 1, '4': 10, '5': 10, '6': 19, '7': 19, '8': 9 } 89 | }); 90 | test.done(); 91 | }, 92 | 93 | "should provide line/branch coverage when nothing exercised": function (test) { 94 | verifier.verify(test, [ -1 ], 0, { 95 | lines: { '1': 1, '2': 1, '3': 1, '4': 0, '5': 0, '6': 0, '7': 0 }, 96 | branches: { '1': [ 0, 0 ] }, 97 | functions: {}, 98 | statements: { '1': 1, '2': 1, '3': 1, '4': 0, '5': 0, '6': 0, '7': 0, '8': 0 } 99 | }); 100 | test.done(); 101 | } 102 | } 103 | }; 104 | 105 | -------------------------------------------------------------------------------- /test/instrumentation/test-with.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var helper = require('../helper'), 3 | code, 4 | verifier; 5 | 6 | /*jshint maxlen: 500 */ 7 | module.exports = { 8 | "with a with statement - no blocks": { 9 | setUp: function (cb) { 10 | code = [ 11 | 'with (Math) output = abs(args[0]);' 12 | ]; 13 | verifier = helper.verifier(__filename, code); 14 | cb(); 15 | }, 16 | 17 | "should cover everything correctly": function (test) { 18 | verifier.verify(test, [ -1 ], 1, { lines: { 1: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1 } }); 19 | test.done(); 20 | } 21 | }, 22 | "with a with statement - in a block": { 23 | setUp: function (cb) { 24 | code = [ 25 | 'with (Math) { output = abs(args[0]); }' 26 | ]; 27 | verifier = helper.verifier(__filename, code); 28 | cb(); 29 | }, 30 | 31 | "should cover everything correctly": function (test) { 32 | verifier.verify(test, [ -1 ], 1, { lines: { 1: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1 } }); 33 | test.done(); 34 | } 35 | } 36 | }; 37 | 38 | -------------------------------------------------------------------------------- /test/loader.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true, regexp: true */ 2 | var path = require('path'), 3 | fs = require('fs'); 4 | 5 | require('../lib/register-plugins'); 6 | 7 | function loadDirTests(dir, pat) { 8 | var files = fs.readdirSync(path.resolve(__dirname, dir)) 9 | .filter(function (f) { 10 | //return f.indexOf('browser') > 0; 11 | return pat.exec(f) && f.indexOf('.js') > 0 && f.indexOf('client.js') < 0 && f !== 'server.js'; 12 | }) 13 | .map(function (f) { return path.relative(process.cwd(), path.resolve(__dirname, dir, f)); }); 14 | return files; 15 | } 16 | 17 | function runTests(pat, reporter, opts, callback) { 18 | var files, 19 | files2, 20 | files3, 21 | files4; 22 | 23 | pat = pat || /(.*)+\.js$/; 24 | if (typeof pat === 'string') { pat = new RegExp(pat); } 25 | files = loadDirTests('instrumentation', pat); 26 | files2 = loadDirTests('other', pat); 27 | files3 = loadDirTests('cli', pat); 28 | files4 = loadDirTests('browser', pat); 29 | files.push.apply(files, files2); 30 | files.push.apply(files, files3); 31 | files.push.apply(files, files4); 32 | reporter.run(files, opts, callback); 33 | } 34 | 35 | module.exports = { 36 | runTests: runTests 37 | }; 38 | 39 | -------------------------------------------------------------------------------- /test/other/config-data/.istanbul.yml: -------------------------------------------------------------------------------- 1 | instrumentation: 2 | compact: false 3 | save-baseline: true 4 | reporting: 5 | reports: 6 | - lcov 7 | - cobertura 8 | -------------------------------------------------------------------------------- /test/other/config-data/cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "instrumentation": { 3 | "save-baseline": true 4 | }, 5 | "hooks": { 6 | "post-require-hook": "yui-istanbul" 7 | } 8 | } -------------------------------------------------------------------------------- /test/other/data-complete-copy/baz.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | baz: function () { return 'baz'; } 3 | }; 4 | -------------------------------------------------------------------------------- /test/other/data-complete-copy/fixture.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/other/data-complete-copy/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | foo: function () { return 'foo'; } 3 | }; 4 | 5 | -------------------------------------------------------------------------------- /test/other/data-complete-copy/myfile1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffrifwald/babel-istanbul/ced0465040d0dc464282012207ba3bc456634b74/test/other/data-complete-copy/myfile1 -------------------------------------------------------------------------------- /test/other/data-complete-copy/myfile2: -------------------------------------------------------------------------------- 1 | some content 2 | -------------------------------------------------------------------------------- /test/other/data-complete-copy/subdir/x.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: verdana; 3 | } -------------------------------------------------------------------------------- /test/other/data/bar.es6: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bar: function () { return 'bar'; } 3 | }; 4 | 5 | -------------------------------------------------------------------------------- /test/other/data/baz.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | baz: function () { return 'baz'; } 3 | }; 4 | -------------------------------------------------------------------------------- /test/other/data/foo.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | foo: function () { return 'foo'; } 3 | }; 4 | 5 | -------------------------------------------------------------------------------- /test/other/data/matcher/.gitignore: -------------------------------------------------------------------------------- 1 | !node_modules/ 2 | -------------------------------------------------------------------------------- /test/other/data/matcher/general/.gitignore: -------------------------------------------------------------------------------- 1 | !node_modules/ 2 | 3 | -------------------------------------------------------------------------------- /test/other/data/matcher/general/general.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { return 42; }; 2 | 3 | -------------------------------------------------------------------------------- /test/other/data/matcher/general/node_modules/mod-file.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { return 42; }; 2 | 3 | -------------------------------------------------------------------------------- /test/other/data/matcher/lib/lib-top.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { return 42; }; 2 | 3 | -------------------------------------------------------------------------------- /test/other/data/matcher/node_modules/dep/mod-file.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { return 42; }; 2 | 3 | -------------------------------------------------------------------------------- /test/other/data/matcher/node_modules/mod-file.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { return 42; }; 2 | 3 | -------------------------------------------------------------------------------- /test/other/data/matcher/top.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { return 32; }; 2 | 3 | -------------------------------------------------------------------------------- /test/other/test-collector.js: -------------------------------------------------------------------------------- 1 | var Collector = require('../../lib/collector'), 2 | utils = require('../../lib/object-utils'), 3 | coverageObj, 4 | coverageObj2, 5 | collector; 6 | 7 | module.exports = { 8 | "tearDown": function (cb) { collector.dispose(); cb(); }, 9 | "with a single coverage object": { 10 | setUp: function (cb) { 11 | var loc = { start: { line: 1, column: 5 }, end: { line: 1, column: 10 } }; 12 | coverageObj = { 13 | foo: { 14 | statementMap: { 1: loc, 2: loc, 3: loc }, 15 | s: { 1: 0, 2: 3, 3: 5 }, 16 | b: { 1: [5, 7], 2: [3, 0], 3: [0, 5] }, 17 | f: { 1: 4, 2: 0, 3: 1}, 18 | path: 'foo' 19 | }, 20 | bar: { 21 | statementMap: { 1: loc, 2: loc, 3: loc, 4: loc }, 22 | s: { 1: 0, 2: 0, 3: 17, 4: 3 }, 23 | b: { 1: [ 0, 8, 9 ], 2: [4, 1], 3: [0, 5] }, 24 | f: { 1: 7, 2: 78, 3: 9, 4: 0 }, 25 | path: 'bar' 26 | } 27 | }; 28 | collector = new Collector(); 29 | cb(); 30 | }, 31 | "collector should not molest any stats": function (test) { 32 | collector.add(JSON.parse(JSON.stringify(coverageObj))); 33 | utils.addDerivedInfo(coverageObj); 34 | test.deepEqual(['foo', 'bar'], collector.files()); 35 | test.deepEqual(coverageObj, collector.getFinalCoverage()); 36 | test.deepEqual(coverageObj.foo, collector.fileCoverageFor('foo')); 37 | test.deepEqual(coverageObj.bar, collector.fileCoverageFor('bar')); 38 | test.done(); 39 | } 40 | }, 41 | "with multiple coverage objects": { 42 | setUp: function (cb) { 43 | var loc = { start: { line: 1, column: 5 }, end: { line: 1, column: 10 } }; 44 | coverageObj = { 45 | foo: { 46 | s: { 1: 0, 2: 3, 3: 5 }, 47 | statementMap: { 1: loc, 2: loc, 3: loc }, 48 | b: { 1: [5, 7], 2: [3, 0], 3: [0, 5] }, 49 | f: { 1: 4, 2: 0, 3: 1}, 50 | path: 'foo' 51 | }, 52 | bar: { 53 | s: { 1: 0, 2: 0, 3: 17, 4: 3 }, 54 | statementMap: { 1: loc, 2: loc, 3: loc, 4: loc }, 55 | b: { 1: [ 0, 8, 9 ], 2: [4, 1], 3: [0, 5] }, 56 | f: { 1: 7, 2: 78, 3: 9, 4: 0 }, 57 | path: 'bar' 58 | } 59 | }; 60 | coverageObj2 = { 61 | foo: { 62 | s: { 1: 1, 2: 5, 3: 88 }, 63 | statementMap: { 1: loc, 2: loc, 3: loc }, 64 | b: { 1: [0, 9], 2: [5, 6], 3: [55, 8] }, 65 | f: { 1: 0, 2: 0, 3: 9 }, 66 | path: 'foo' 67 | } 68 | }; 69 | collector = new Collector(); 70 | cb(); 71 | }, 72 | "collector should merge foo coverage": function (test) { 73 | var mergedFoo = utils.mergeFileCoverage(coverageObj.foo, coverageObj2.foo), 74 | mergedFinal = { foo: mergedFoo, bar: coverageObj.bar }; 75 | collector.add(JSON.parse(JSON.stringify(coverageObj))); 76 | collector.add(JSON.parse(JSON.stringify(coverageObj2))); 77 | utils.addDerivedInfo(mergedFinal); 78 | test.deepEqual(['foo', 'bar'], collector.files()); 79 | test.deepEqual(mergedFinal, collector.getFinalCoverage()); 80 | test.deepEqual(mergedFinal.foo, collector.fileCoverageFor('foo')); 81 | test.deepEqual(mergedFinal.bar, collector.fileCoverageFor('bar')); 82 | test.done(); 83 | } 84 | } 85 | }; -------------------------------------------------------------------------------- /test/other/test-command-xface.js: -------------------------------------------------------------------------------- 1 | var Command = require('../../lib/command'); 2 | 3 | module.exports = { 4 | "should return command list": function (test) { 5 | var cmdList = Command.getCommandList(); 6 | test.ok(cmdList.length > 0); 7 | cmdList.forEach(function (name) { 8 | var command = Command.create(name); 9 | test.ok(command.synopsis && command.synopsis()); 10 | test.ok(command.usage && typeof command.usage === 'function'); 11 | command.usage(); 12 | test.ok(command.run && typeof command.run === 'function'); 13 | }); 14 | test.done(); 15 | }, 16 | "should run help for all commands without any barfs": function (test) { 17 | var cmdList = Command.getCommandList(), 18 | help = Command.create('help'), 19 | handler = function (err) { test.ok(!err); }; 20 | help.run([], handler); 21 | help.run(['foobar'], handler); 22 | help.run(['foobar', 'foobar', 'foobar'], handler); 23 | cmdList.forEach(function (name) { 24 | if (name !== 'help') { 25 | help.run([ name ], handler); 26 | } 27 | }); 28 | test.done(); 29 | }, 30 | "should throw on non-existent command": function (test) { 31 | test.throws(function () { Command.create('nonexistent-command'); }, 32 | /existent/); 33 | test.done(); 34 | } 35 | }; -------------------------------------------------------------------------------- /test/other/test-file-writer.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | 3 | var path = require('path'), 4 | fs = require('fs'), 5 | mkdirp = require('mkdirp'), 6 | rimraf = require('rimraf'), 7 | FileWriter = require('../../lib/util/file-writer'), 8 | outputDir = path.resolve(__dirname, 'output'), 9 | FOO = path.resolve(outputDir, 'foo-written.js'), 10 | writer, 11 | MAX_FILES = 1000, 12 | LINES = [ 'This is line 1', 'This is line 2', 'This is line 3' ]; 13 | 14 | function testContents(test) { 15 | test.equal(LINES.join('\n') + '\n', fs.readFileSync(FOO, 'utf8')); 16 | test.done(); 17 | } 18 | 19 | function fileNames() { 20 | var i, 21 | ret = []; 22 | for (i = 0; i < MAX_FILES; i += 1) { 23 | ret.push(path.resolve(outputDir, 'file' + i + '.txt')); 24 | } 25 | return ret; 26 | } 27 | 28 | function testAllFiles(test) { 29 | var files = fileNames(); 30 | files.forEach(function (file) { 31 | test.equal(file + '\n', fs.readFileSync(file, 'utf8')); 32 | }); 33 | test.done(); 34 | } 35 | 36 | function battery(sync) { 37 | return { 38 | setUp: function (cb) { 39 | mkdirp.sync(outputDir); 40 | writer = new FileWriter(sync); 41 | cb(); 42 | }, 43 | tearDown: function (cb) { 44 | writer = null; 45 | rimraf.sync(outputDir); 46 | cb(); 47 | }, 48 | "should write file in callback mode": function (test) { 49 | writer.on('done', testContents.bind(null, test)); 50 | writer.writeFile(FOO, function (w) { 51 | LINES.forEach(function (line) { w.println(line); }); 52 | }); 53 | writer.done(); 54 | }, 55 | "should write 1000 files": function (test) { 56 | var files = fileNames(); 57 | writer.on('done', testAllFiles.bind(null, test)); 58 | files.forEach(function (file) { 59 | writer.writeFile(file, function (w) { 60 | w.println(file); 61 | }); 62 | }); 63 | writer.done(); 64 | } 65 | }; 66 | } 67 | 68 | module.exports = { 69 | "when in sync mode": battery(true), 70 | "when in async mode": battery(false) 71 | }; 72 | -------------------------------------------------------------------------------- /test/other/test-help-formatter.js: -------------------------------------------------------------------------------- 1 | var formatOption = require('../../lib/util/help-formatter').formatOption, 2 | THRESHOLD = 20; 3 | 4 | module.exports = { 5 | "should format small option + text on a single line": function (test) { 6 | var formatted = formatOption('-f', 'specify force override of defaults'); 7 | console.log(formatted); 8 | test.ok(formatted.indexOf('\n') < 0); 9 | test.done(); 10 | }, 11 | "should format small option + big text on multiple lines": function (test) { 12 | var formatted = formatOption('-f', 'an option whose description should exceed the length of a line if we have done our job well'); 13 | console.log(formatted); 14 | test.ok(formatted.indexOf('\n') > THRESHOLD); 15 | test.done(); 16 | }, 17 | "should format long option + small text on multiple lines": function (test) { 18 | var formatted = formatOption('--[no]force', 'specify force override of defaults'); 19 | console.log(formatted); 20 | test.ok(formatted.indexOf('\n') < THRESHOLD); 21 | test.ok(formatted.lastIndexOf('\n') < THRESHOLD); 22 | test.done(); 23 | }, 24 | "should format long option + big text on 3 lines": function (test) { 25 | var formatted = formatOption('--[no]force', 'an option whose description should exceed the length of a line if we have done our job well'); 26 | console.log(formatted); 27 | test.ok(formatted.indexOf('\n') < THRESHOLD); 28 | test.ok(formatted.lastIndexOf('\n') > THRESHOLD); 29 | test.done(); 30 | } 31 | }; -------------------------------------------------------------------------------- /test/other/test-index-xface.js: -------------------------------------------------------------------------------- 1 | var main = require('../../index'); 2 | 3 | 4 | module.exports = { 5 | "xface": function (test) { 6 | [ 'Instrumenter', 'Store', 'Collector', 'Report', 'Reporter', '_yuiLoadHook'].forEach(function (key) { 7 | test.ok(main[key] && typeof main[key] === 'function', key + ' was not exported as a function!'); 8 | }); 9 | [ 'hook', 'utils', 'config' ].forEach(function (key) { 10 | test.ok(main[key] && typeof main[key] === 'object', key + ' was not exported as an object!'); 11 | }); 12 | [ 'assetsDir'].forEach(function (key) { 13 | test.ok(main[key] && typeof main[key] === 'string', key + ' was not exported as a string!'); 14 | }); 15 | test.done(); 16 | } 17 | }; -------------------------------------------------------------------------------- /test/other/test-input-error.js: -------------------------------------------------------------------------------- 1 | var inputError = require('../../lib/util/input-error'); 2 | 3 | module.exports = { 4 | "should produce an Error object distinguishable from unexpected errors": function (test) { 5 | var e = inputError.create('bad user!'); 6 | test.equal('bad user!', e.message); 7 | test.ok(e.inputError); 8 | test.done(); 9 | } 10 | }; -------------------------------------------------------------------------------- /test/other/test-matcher.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var path = require('path'), 3 | glob = require('glob'), 4 | root = path.resolve(__dirname, 'data', 'matcher'), 5 | src = '../../lib/util/file-matcher.js', 6 | fileMatcher = require(src), 7 | allFiles; 8 | 9 | module.exports = { 10 | setUp: function (cb) { 11 | if (!allFiles) { 12 | glob('**/*.js', { cwd: root}, function (err, files) { 13 | allFiles = files.map(function (file) { return path.resolve(root, file); }); 14 | cb(); 15 | }); 16 | } else { 17 | cb(); 18 | } 19 | }, 20 | "should return all files except those under node_modules by default": function (test) { 21 | fileMatcher.filesFor(function (err, files) { 22 | test.ok(!err); 23 | allFiles.forEach(function (file) { 24 | var matcher = function (f) { return f === file; }, 25 | shouldMatch = file.indexOf('file.js') < 0; 26 | if (shouldMatch) { 27 | test.ok(files.filter(matcher).length, 'Should match [' + file + '] but did not'); 28 | } else { 29 | test.ok(!files.filter(matcher).length, 'Should NOT match [' + file + '] but did'); 30 | } 31 | }); 32 | test.done(); 33 | }); 34 | }, 35 | "should return relative filenames when requested": function (test) { 36 | fileMatcher.filesFor({ root: root, relative: true }, function (err, files) { 37 | test.ok(!err); 38 | allFiles.forEach(function (file) { 39 | var matcher = function (f) { return path.resolve(root, f) === file; }, 40 | shouldMatch = file.indexOf('file.js') < 0; 41 | if (shouldMatch) { 42 | test.ok(files.filter(matcher).length, 'Should match [' + file + '] but did not'); 43 | } else { 44 | test.ok(!files.filter(matcher).length, 'Should NOT match [' + file + '] but did'); 45 | } 46 | }); 47 | test.done(); 48 | }); 49 | }, 50 | "should match stuff under cwd": function (test) { 51 | fileMatcher.matcherFor(function (err, matchFn) { 52 | test.ok(!err); 53 | test.ok(matchFn(path.resolve(__dirname, src)), 'should match itself'); 54 | test.done(); 55 | }); 56 | }, 57 | "should match stuff under cwd overriding relative opts passed in": function (test) { 58 | fileMatcher.matcherFor({ relative: true }, function (err, matchFn) { 59 | test.ok(!err); 60 | test.ok(matchFn(path.resolve(__dirname, src)), 'should match itself'); 61 | test.done(); 62 | }); 63 | }, 64 | "should ignore node_modules": function (test) { 65 | fileMatcher.matcherFor({ root: root }, function (err, matchFn) { 66 | test.ok(!err); 67 | test.ok(matchFn.files); 68 | test.deepEqual(allFiles.filter(function (f) { return !f.match(/node_modules/); }).sort(), 69 | matchFn.files.sort()); 70 | allFiles.forEach(function (file) { 71 | var shouldMatch = file.indexOf('file.js') < 0; 72 | if (shouldMatch) { 73 | test.ok(matchFn(file), 'Should match [' + file + '] but did not'); 74 | } else { 75 | test.ok(!matchFn(file), 'Should NOT match [' + file + '] but did'); 76 | } 77 | }); 78 | test.done(); 79 | }); 80 | }, 81 | "should match stuff with explicit includes and excludes": function (test) { 82 | fileMatcher.matcherFor({ root: root, includes: [ '**/general/**/*.js' ], excludes: [ '**/general.js' ] }, function (err, matchFn) { 83 | test.ok(!err); 84 | allFiles.forEach(function (file) { 85 | if (file.indexOf('/general/') < 0) { return; } 86 | var shouldMatch = file.indexOf('file.js') >= 0; 87 | if (shouldMatch) { 88 | test.ok(matchFn(file), 'Should match [' + file + '] but did not'); 89 | } else { 90 | test.ok(!matchFn(file), 'Should NOT match [' + file + '] but did'); 91 | } 92 | }); 93 | test.done(); 94 | }); 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /test/other/test-store.js: -------------------------------------------------------------------------------- 1 | /*jslint nomen: true */ 2 | var Store = require('../../lib/store'), 3 | path = require('path'), 4 | fs = require('fs'), 5 | util = require('util'), 6 | foo = path.resolve(__dirname, 'data', 'foo.js'), 7 | baz = path.resolve(__dirname, 'data', 'baz.js'), 8 | anon = path.resolve(__dirname, 'data', 'does_not_exist.js'), 9 | fooContents = fs.readFileSync(foo, 'utf8'), 10 | bazContents = fs.readFileSync(baz, 'utf8'); 11 | 12 | module.exports = { 13 | "fslookup store should work correctly": function (test) { 14 | var store = Store.create('fslookup'); 15 | 16 | store.set(foo, 'content'); 17 | test.ok(store.hasKey(foo)); 18 | test.ok(store.hasKey(baz)); 19 | test.ok(!store.hasKey(anon)); 20 | test.equal(0, store.keys().length); 21 | 22 | test.equals(fooContents, store.get(foo)); 23 | test.equals(bazContents, store.get(baz)); 24 | test.throws(function () { 25 | store.get(anon); 26 | }); 27 | test.throws(function () { 28 | store.set(anon, 'foo'); 29 | }, Error, /non-existent/); 30 | test.done(); 31 | }, 32 | 33 | "tmp store should work as expected": function (test) { 34 | var store = Store.create('tmp'); 35 | 36 | test.equal(0, store.keys().length); 37 | store.set(foo, 'content'); 38 | test.ok(store.hasKey(foo)); 39 | test.ok(!store.hasKey(baz)); 40 | test.equal(1, store.keys().length); 41 | store.set(baz, 'baz content'); 42 | test.equal(2, store.keys().length); 43 | 44 | test.equals('content', store.get(foo)); 45 | test.equals('baz content', store.get(baz)); 46 | test.throws(function () { 47 | store.get(anon); 48 | }); 49 | store.dispose(); 50 | test.ok(!store.hasKey(foo)); 51 | test.equal(0, store.keys().length); 52 | test.done(); 53 | }, 54 | 55 | "memory store should work as expected": function (test) { 56 | var store = Store.create('memory'); 57 | 58 | test.equal(0, store.keys().length); 59 | store.set(foo, 'content'); 60 | test.ok(store.hasKey(foo)); 61 | test.ok(!store.hasKey(baz)); 62 | test.equal(1, store.keys().length); 63 | store.set(baz, 'baz content'); 64 | test.equal(2, store.keys().length); 65 | 66 | test.equals('content', store.get(foo)); 67 | test.equals('baz content', store.get(baz)); 68 | test.throws(function () { 69 | store.get(anon); 70 | }); 71 | store.dispose(); 72 | test.ok(!store.hasKey(foo)); 73 | test.equal(0, store.keys().length); 74 | test.done(); 75 | }, 76 | 77 | "should be able to register a new store": function (test) { 78 | function NStore() { 79 | } 80 | NStore.prototype = { 81 | set: function (/* file, content */) { return 'x'; } 82 | }; 83 | NStore.TYPE = 'nstore'; 84 | 85 | Store.register(NStore); 86 | var store = Store.create('nstore'); 87 | test.ok(store); 88 | test.equals('x', store.set('foo', 'bar')); 89 | test.done(); 90 | }, 91 | 92 | "should not be able to register an invalid store": function (test) { 93 | function NStore() {} 94 | test.throws(function () { 95 | Store.register(NStore); 96 | }, 97 | /TYPE/); 98 | test.done(); 99 | }, 100 | 101 | "invalid store should not be created": function (test) { 102 | test.throws( 103 | function () { Store.create('foo'); }, 104 | /Invalid store \[foo\], allowed values are / 105 | ); 106 | test.done(); 107 | }, 108 | 109 | "should require overriding of all overrideables": function (test) { 110 | function NStore() {} 111 | NStore.TYPE = 'nstore'; 112 | util.inherits(NStore, Store); 113 | Store.register(NStore); 114 | 115 | var store = Store.create('nstore'), 116 | asserter = function (fn) { 117 | test.throws(function () { 118 | fn(); 119 | }, 120 | /must be overridden/); 121 | }; 122 | asserter(function () { store.set('foo', 'bar'); }); 123 | asserter(function () { store.get('foo'); }); 124 | asserter(function () { store.hasKey('foo'); }); 125 | asserter(function () { store.keys(); }); 126 | test.doesNotThrow(function () { store.dispose(); }); 127 | test.done(); 128 | } 129 | }; -------------------------------------------------------------------------------- /test/run-again.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var nodeunit = require('nodeunit'), 4 | mkdirp = require('mkdirp'), 5 | loader = require('./loader'), 6 | common = require('./common'); 7 | 8 | function runTests() { 9 | var outputDir = common.getBuildDir(); 10 | 11 | mkdirp.sync(outputDir); 12 | loader.runTests(process.argv[2], nodeunit.reporters['default'], undefined, function (err) { 13 | if (err) { throw err; } 14 | }); 15 | } 16 | 17 | runTests(); 18 | 19 | 20 | -------------------------------------------------------------------------------- /yuidoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "The Istanbul API", 3 | "description": "The Istanbul API: a code coverage library", 4 | "options": { 5 | "outdir": "./public/apidocs", 6 | "exclude": "vendor,node_modules,misc,test" 7 | } 8 | } 9 | 10 | --------------------------------------------------------------------------------