1 | # babel-plugin-transform-adana
|
2 |
|
3 | Minimal, complete code-coverage tool for [babel] 6+.
|
4 |
|
5 | ![build status](http://img.shields.io/travis/izaakschroeder/babel-plugin-transform-adana/master.svg?style=flat)
|
6 | ![coverage](http://img.shields.io/coveralls/izaakschroeder/babel-plugin-transform-adana/master.svg?style=flat)
|
7 | ![license](http://img.shields.io/npm/l/babel-plugin-transform-adana.svg?style=flat)
|
8 | ![version](http://img.shields.io/npm/v/babel-plugin-transform-adana.svg?style=flat)
|
9 | ![downloads](http://img.shields.io/npm/dm/babel-plugin-transform-adana.svg?style=flat)
|
10 |
|
11 | Has all the features (and more) of [istanbul] including line, function and branch coverage, but works as a [babel] plugin instead of relying on `esparse` and `escodegen`. Works great with [west], [mocha], [jasmine] and probably more.
|
12 |
|
13 | Features:
|
14 |
|
15 | * First-class babel support,
|
16 | * Per-line/function/branch coverage,
|
17 | * Tagged instrumentation,
|
18 | * Smart branch detection.
|
19 |
|
20 | TODO:
|
21 | * User-defined tags,
|
22 | * More test cases,
|
23 | * Split out bins/reporters into other modules.
|
24 |
|
25 | ## FAQ
|
26 |
|
27 | * Why is a line marked not covered when it clearly is? - The `line` algorithm is conservative; if you have any part of a line with 0 hits, then that whole line is frozen at 0.
|
28 | * Why is `let i;`, `function foo() {}`, etc. not marked at all? – Some things are not executable code per se (i.e. declarations). They do nothing to effect program state and are therefore not instrumented.
|
29 |
|
30 | ## Usage
|
31 |
|
32 | Install `adana`:
|
33 |
|
34 | ```sh
|
35 | npm install --save-dev babel-plugin-transform-adana adana-cli
|
36 | ```
|
37 |
|
38 | Setup [babel] to use it:
|
39 |
|
40 | ```json
|
41 | {
|
42 | "env": {
|
43 | "test": {
|
44 | "plugins": [[
|
45 | "transform-adana", {
|
46 | "test": "src/**/*.js"
|
47 | }
|
48 | ]]
|
49 | }
|
50 | }
|
51 | }
|
52 | ```
|
53 |
|
54 | **IMPORTANT**: This plugin works best when it runs as the _first_ plugin in the babel transform list, since its purpose is to instrument your _original code_, not whatever other transformations happen to get made.
|
55 |
|
56 | Extract the coverage data from node:
|
57 |
|
58 | ```sh
|
59 | NODE_ENV="test" mocha \
|
60 | -r babel-plugin-transform-adana/dump \
|
61 | --compilers js:babel-core/register \
|
62 | test/*.spec.js
|
63 | ```
|
64 |
|
65 | ### Tags
|
66 |
|
67 | There is no `ignore` flag, but you can tag functions, branches or statements to which can be used to determine relevant coverage information. This allows you to categorize blocks of code and ask things like "Have I covered all the code that pertains to authentication in the file?". Existing `ignore` comments simply tag a function with the `ignore` tag.
|
68 |
|
69 | * Tags above a block (if/function/etc.) to apply to all code in that block.
|
70 | * Tags above or on a line apply to all statements in that line.
|
71 |
|
72 | ```javascript
|
73 |
|
74 |
|
75 | /* adana: +ie +firefox -chrome */
|
76 | function foo() {
|
77 | console.log('foo'); // adana: +test
|
78 | }
|
79 |
|
80 | /* adana: +chrome */
|
81 | if () {
|
82 |
|
83 | }
|
84 | ```
|
85 |
|
86 | ### API
|
87 |
|
88 | `adana` is simply a [babel] transformer that injects markers to determine if specific parts of the code have been run. To inject these markers simply add `transform-adana` as a plugin and use [babel] normally:
|
89 |
|
90 | ```javascript
|
91 | import { transform } from 'babel-core';
|
92 |
|
93 | const result = transform('some code', {
|
94 | plugins: [ 'transform-adana' ]
|
95 | });
|
96 |
|
97 | // Access result.code, result.map and result.metadata.coverage
|
98 | ```
|
99 |
|
100 | To collect information about code that has been instrumented, simply access the configured global variable, e.g. `__coverage__`.
|
101 |
|
102 | ```javascript
|
103 | import vm from 'vm';
|
104 | const sandbox = vm.createContext({});
|
105 | sandbox.global = sandbox;
|
106 | vm.runInContext(result.code, sandbox);
|
107 | console.log(sandbox.__coverage__);
|
108 | ```
|
109 |
|
110 | The `__coverage__` object has the following shape:
|
111 |
|
112 | ```javascript
|
113 | {
|
114 | // Array of counters; the index in this list maps to the same index in the
|
115 | // locations array.
|
116 | counters: [ 0, 0, ... ],
|
117 | // Detailed information about every location that's been instrumented.
|
118 | locations: [{
|
119 | loc: { start: { line: 0, column 0 }, end: { line: 0, column: 0 } },
|
120 | type: 'function|statement|branch',
|
121 | name: 'foo',
|
122 | group: 'bar',
|
123 | tags: [ 'tagA', 'tagB' ]
|
124 | }, {
|
125 | ...
|
126 | }, ...]
|
127 | }
|
128 | ```
|
129 |
|
130 | ```javascript
|
131 | /* global __coverage__ */
|
132 | import { writeFileSync } from 'fs';
|
133 |
|
134 | // Dump that data to disk after tests have finished.
|
135 | process.on('exit', () => {
|
136 | writeFileSync('coverage/coverage.json', JSON.stringify(__coverage__));
|
137 | });
|
138 |
|
139 | ```
|
140 |
|
141 | ### HMR Spec Coverage
|
142 |
|
143 | foo.spec.js -> foo.js: overwrite coverage info only for foo.js
|
144 |
|
145 | On HMR:
|
146 | - clear coverage counters for reloaded modules
|
147 | - run reloaded code
|
148 | - replace coverage counters for reloaded modules
|
149 |
|
150 | ### Other Notes
|
151 |
|
152 | ```js
|
153 | // TODO: Allow "merging" of same-hash coverage where the counters are
|
154 | // incremented. This could be for someone who has to run a program
|
155 | // several times for the same files to cover everything.
|
156 | ```
|
157 |
|
158 |
|
159 | [babel]: http://babeljs.io
|
160 | [istanbul]: https://github.com/gotwarlost/istanbul
|
161 | [mocha]: http://mochajs.org/
|
162 | [jasmine]: http://jasmine.github.io/
|
163 | [west]: https://www.github.com/izaakschroeder/west
|
164 | [lcov]: http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php
|