Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ and then:

`cross-env COVERAGE=true ember test`

When running with `parallel` set to true, the final reports can be merged by using `ember coverage-merge`. The final merged output will be stored in the `coverageFolder`.
Coverage also works when running tests in parallel, eg:

`COVERAGE=true ember exam --split=2 --parallel=true`

## Configuration

Expand All @@ -46,8 +48,6 @@ Configuration is optional. It should be put in a file at `config/coverage.js` (`

- `coverageFolder`: Defaults to `coverage`. A folder relative to the root of your project to store coverage results.

- `parallel`: Defaults to `false`. Should be set to true if parallel testing is being used, for example when using [ember-exam](https://github.com/trentmwillis/ember-exam) with the `--parallel` flag. This will generate the coverage reports in directories suffixed with `_<random_string>` to avoid overwriting other threads reports. These reports can be joined by using the `ember coverage-merge` command (potentially as part of the [posttest hook](https://docs.npmjs.com/misc/scripts) in your `package.json`).

#### Example
```js
module.exports = {
Expand Down
24 changes: 15 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,29 +85,35 @@ module.exports = {
return undefined;
},

includedCommands: function() {
return {
'coverage-merge': require('./lib/coverage-merge')
};
},

/**
* If coverage is enabled attach coverage middleware to the express server run by ember-cli
* @param {Object} startOptions - Express server start options
*/
serverMiddleware: function(startOptions) {
this.testemMiddleware(startOptions.app);
if (!this._isCoverageEnabled()) {
return;
}
attachMiddleware.serverMiddleware(startOptions.app, {
configPath: this.project.configPath(),
root: this.project.root,
fileLookup: this.fileLookup
});
},

testemMiddleware: function(app) {
if (!this._isCoverageEnabled()) {
return;
}
attachMiddleware(app, {
const config = {
configPath: this.project.configPath(),
root: this.project.root,
fileLookup: this.fileLookup
});
};
// if we're running `ember test --server` use the `serverMiddleware`.
if (process.argv.includes('--server') || process.argv.includes('-s')) {
return this.serverMiddleware({ app }, config);
}
attachMiddleware.testMiddleware(app, config);
},

// Custom Methods
Expand Down
85 changes: 49 additions & 36 deletions lib/attach-middleware.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
'use strict';

var bodyParser = require('body-parser');
var istanbul = require('istanbul-api');
var getConfig = require('./config');
var path = require('path');
var crypto = require('crypto');
const bodyParser = require('body-parser').json({ limit: '50mb' });
const istanbul = require('istanbul-api');
const getConfig = require('./config');
const path = require('path');

const WRITE_COVERAGE = '/write-coverage';

function logError(err, req, res, next) {
console.error(err.stack);
Expand All @@ -16,40 +17,52 @@ function fixFilePaths(coverageData, fileLookup) {
return coverageData;
}

module.exports = function(app, options) {
app.post('/write-coverage',
bodyParser.json({ limit: '50mb' }),
function(req, res) {
var config = getConfig(options.configPath);

if (config.parallel) {
config.coverageFolder = config.coverageFolder + '_' + crypto.randomBytes(4).toString('hex');
if (config.reporters.indexOf('json') === -1) {
config.reporters.push('json');
}
}

if (config.reporters.indexOf('json-summary') === -1) {
config.reporters.push('json-summary');
}

let reporter = istanbul.createReporter();
if (config.coverageFolder) {
reporter.dir = path.join(options.root, config.coverageFolder);
}
let map = istanbul.libCoverage.createCoverageMap();
let coverage = req.body;
function writeCoverage(coverage, fileLookup, map) {
Object.keys(fileLookup).forEach(filename => {
let fileCoverage = coverage[filename] || istanbul.libCoverage.createFileCoverage(filename).data;
map.addFileCoverage(fixFilePaths(fileCoverage, fileLookup));
});
}

Object.keys(options.fileLookup).forEach(filename => {
let fileCoverage = coverage[filename] || istanbul.libCoverage.createFileCoverage(filename).data;
map.addFileCoverage(fixFilePaths(fileCoverage, options.fileLookup));
});
function reportCoverage(map, root, configPath) {
let config = getConfig(configPath);
let reporter = istanbul.createReporter();
if (config.coverageFolder) {
reporter.dir = path.join(root, config.coverageFolder);
}
reporter.addAll(config.reporters);
reporter.write(map);
}

reporter.addAll(config.reporters);
reporter.write(map);
function coverageHandler(map, options, req, res) {
writeCoverage(req.body, options.fileLookup, map);
reportCoverage(map, options.root, options.configPath);
res.send(map.getCoverageSummary());
}

const results = map.getCoverageSummary();
res.send(results);
// Used when app is in dev mode (`ember serve`).
// Creates a new coverage map on every request.
const serverMiddleware = function(app, options) {
app.post(WRITE_COVERAGE,
bodyParser,
(req, res) => {
let map = istanbul.libCoverage.createCoverageMap();
coverageHandler(map, options, req, res);
},
logError);
};

// Used when app is in ci mode (`ember test`).
// Collects the coverage on each request and merges it into the coverage map.
const testMiddleware = function(app, options) {
let map = istanbul.libCoverage.createCoverageMap();
app.post(WRITE_COVERAGE,
bodyParser,
coverageHandler.bind(null, map, options),
logError);
};

module.exports = {
serverMiddleware,
testMiddleware,
};
60 changes: 0 additions & 60 deletions lib/coverage-merge.js

This file was deleted.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"fs-extra": "^5.0.0",
"istanbul-api": "^1.1.14",
"lodash.concat": "^4.5.0",
"node-dir": "^0.1.16",
"rsvp": "^4.8.1",
"walk-sync": "^0.3.2"
},
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/my-addon/config/coverage.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
reporters: ['lcov', 'html', 'text']
reporters: ['lcov', 'html', 'text', 'json-summary']
};
2 changes: 1 addition & 1 deletion test/fixtures/my-app-with-in-repo-addon/config/coverage.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
reporters: ['lcov', 'html', 'text']
reporters: ['lcov', 'html', 'text', 'json-summary']
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
reporters: ['lcov', 'html', 'text']
reporters: ['lcov', 'html', 'text', 'json-summary']
};
2 changes: 1 addition & 1 deletion test/fixtures/my-app/config/coverage.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
reporters: ['lcov', 'html', 'text']
reporters: ['lcov', 'html', 'text', 'json-summary']
};
11 changes: 2 additions & 9 deletions test/integration/app-coverage-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,31 +76,24 @@ describe('app coverage generation', function() {
});
});

it('uses parallel configuration and merges coverage when merge-coverage command is issued', function() {
it('merges coverage when tests are run in parallel', function() {
expect(dir(`${app.path}/coverage`)).to.not.exist;
fs.copySync('tests/dummy/config/coverage-parallel.js', `${app.path}/config/coverage.js`);
process.env.COVERAGE = true;
return app.run('ember', 'exam', '--split=2', '--parallel=true').then(function() {
expect(dir(`${app.path}/coverage`)).to.not.exist;
return app.run('ember', 'coverage-merge');
}).then(function() {
expect(file(`${app.path}/coverage/lcov-report/index.html`)).to.not.be.empty;
expect(file(`${app.path}/coverage/index.html`)).to.not.be.empty;
var summary = fs.readJSONSync(`${app.path}/coverage/coverage-summary.json`);
expect(summary.total.lines.pct).to.equal(83.33);
});
});

it('uses nested coverageFolder and parallel configuration and run merge-coverage', function() {
it('uses nested coverageFolder', function() {
var coverageFolder = `${app.path}/coverage/abc/easy-as/123`;

expect(dir(coverageFolder)).to.not.exist;
fs.copySync('tests/dummy/config/coverage-nested-folder.js', `${app.path}/config/coverage.js`);
process.env.COVERAGE = true;
return app.run('ember', 'exam', '--split=2', '--parallel=true').then(function() {
expect(dir(coverageFolder)).to.not.exist;
return app.run('ember', 'coverage-merge');
}).then(function() {
expect(file(`${coverageFolder}/lcov-report/index.html`)).to.not.be.empty;
expect(file(`${coverageFolder}/index.html`)).to.not.be.empty;
var summary = fs.readJSONSync(`${coverageFolder}/coverage-summary.json`);
Expand Down
34 changes: 29 additions & 5 deletions test/unit/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,39 @@ describe('index.js', function() {
});

describe('serverMiddleware', function() {
var app;

beforeEach(function() {
sandbox.stub(Index, 'testemMiddleware');
Index.serverMiddleware({
app: 'foo-bar'
app = {
post: sinon.spy()
};

sandbox.stub(Index, 'project').value({
root: '/path/to/foo-bar',
configPath: sinon.stub().returns('tests/dummy/config/environment.js')
});
});

it('calls testemMiddleware with correct arguments', function() {
expect(Index.testemMiddleware.lastCall.args).to.eql(['foo-bar']);
describe('when coverage is enabled', function() {
beforeEach(function() {
sandbox.stub(Index, '_isCoverageEnabled').returns(true);
Index.serverMiddleware({ app });
});

it('adds POST endpoint to app', function() {
expect(app.post.callCount).to.equal(1);
});
});

describe('when coverage is not enabled', function() {
beforeEach(function() {
sandbox.stub(Index, '_isCoverageEnabled').returns(false);
Index.serverMiddleware({ app });
});

it('does not add POST endpoint to app', function() {
expect(app.post.callCount).to.equal(0);
});
});
});

Expand Down
3 changes: 2 additions & 1 deletion tests/dummy/config/coverage-excludes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
module.exports = {
excludes: [
'**/utils/my-uncovered-util.js'
]
],
reporters: ['lcov', 'html', 'text', 'json-summary']
};
2 changes: 1 addition & 1 deletion tests/dummy/config/coverage-nested-folder.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@

module.exports = {
coverageFolder: 'coverage/abc/easy-as/123',
parallel: true
reporters: ['lcov', 'html', 'text', 'json-summary']
};
5 changes: 0 additions & 5 deletions tests/dummy/config/coverage-parallel.js

This file was deleted.