Skip to content

Commit 246bc1a

Browse files
committed
Move coverage to independent module
1 parent e70ef03 commit 246bc1a

File tree

1 file changed

+17
-159
lines changed

1 file changed

+17
-159
lines changed

test/integration/index.coverage.ts

Lines changed: 17 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,15 @@
55

66
'use strict';
77

8-
declare var global: any;
9-
108
/* tslint:disable no-require-imports */
119

1210
import * as fs from 'fs';
1311
import * as glob from 'glob';
1412
import * as paths from 'path';
13+
import Mocha = require('mocha');
14+
import { ITestRunnerOptions, CoverageRunner } from '../coverage';
1515

16-
const istanbul = require('istanbul');
17-
const Mocha = require('mocha');
18-
const remapIstanbul = require('remap-istanbul');
16+
declare var global: any;
1917

2018
// Linux: prevent a weird NPE when mocha on Linux requires the window size from the TTY
2119
// Since we are not running in a tty environment, we just implement the method statically
@@ -25,10 +23,11 @@ if (!tty.getWindowSize) {
2523
return [80, 75];
2624
};
2725
}
28-
const config = {
26+
27+
const config: Mocha.MochaOptions = {
2928
reporter: 'mocha-jenkins-reporter',
3029
ui: 'tdd',
31-
color: true,
30+
useColors: true,
3231
timeout: 15000
3332
};
3433

@@ -43,12 +42,6 @@ function configure(mochaOpts: any): void {
4342
}
4443
exports.configure = configure;
4544

46-
function _mkDirIfExists(dir: string): void {
47-
if (!fs.existsSync(dir)) {
48-
fs.mkdirSync(dir);
49-
}
50-
}
51-
5245
function _readCoverOptions(testsRoot: string): ITestRunnerOptions | undefined {
5346
const coverConfigPath = paths.join(testsRoot, '..', '..', '..', 'coverconfig.json');
5447
console.log("coverage config location", coverConfigPath);
@@ -59,18 +52,16 @@ function _readCoverOptions(testsRoot: string): ITestRunnerOptions | undefined {
5952
return undefined;
6053
}
6154

62-
function run(testsRoot1: string, clb: any): any {
63-
return new Promise( (c, e) => { // Glob test files
64-
// Read configuration for the coverage file
65-
const testsRoot = paths.resolve(__dirname);
66-
const coverOptions = _readCoverOptions(testsRoot);
67-
let coverageRunner;
68-
if (coverOptions && coverOptions.enabled) {
69-
console.log('coverage enabled!');
70-
// Setup coverage pre-test, including post-test hook to report
71-
coverageRunner = new CoverageRunner(coverOptions, testsRoot);
72-
coverageRunner.setupCoverage();
73-
}
55+
function run(): any {
56+
return new Promise( (c, e) => {
57+
const testsRoot = paths.resolve(__dirname);
58+
const coverOptions = _readCoverOptions(testsRoot);
59+
let coverageRunner;
60+
if (coverOptions && coverOptions.enabled) {
61+
console.log('coverage enabled!');
62+
coverageRunner = new CoverageRunner(coverOptions, testsRoot);
63+
coverageRunner.setupCoverage();
64+
}
7465
glob('**/**.test.js', { cwd: testsRoot }, (error, files): any => {
7566
if (error) {
7667
return e(error);
@@ -95,137 +86,4 @@ function run(testsRoot1: string, clb: any): any {
9586
});
9687
});
9788
}
98-
exports.run = run;
99-
100-
interface ITestRunnerOptions {
101-
enabled?: boolean;
102-
relativeCoverageDir: string;
103-
relativeSourcePath: string;
104-
ignorePatterns: string[];
105-
includePid?: boolean;
106-
reports?: string[];
107-
verbose?: boolean;
108-
}
109-
110-
class CoverageRunner {
111-
112-
private coverageVar: string = `$$cov_${new Date().getTime()}$$`;
113-
private transformer: any = undefined;
114-
private matchFn: any = undefined;
115-
private instrumenter: any = undefined;
116-
117-
constructor(private options: ITestRunnerOptions, private testsRoot: string) {
118-
if (!options.relativeSourcePath) {
119-
return;
120-
}
121-
}
122-
123-
public setupCoverage(): void {
124-
// Set up Code Coverage, hooking require so that instrumented code is returned
125-
const self = this;
126-
self.instrumenter = new istanbul.Instrumenter({ coverageVariable: self.coverageVar });
127-
const sourceRoot = paths.join(self.testsRoot, self.options.relativeSourcePath);
128-
129-
// Glob source files
130-
const srcFiles = glob.sync('**/**.js', {
131-
cwd: sourceRoot,
132-
ignore: self.options.ignorePatterns
133-
});
134-
135-
// Create a match function - taken from the run-with-cover.js in istanbul.
136-
const decache = require('decache');
137-
const fileMap: any = {};
138-
srcFiles.forEach((file) => {
139-
const fullPath = paths.join(sourceRoot, file);
140-
fileMap[fullPath] = true;
141-
142-
// On Windows, extension is loaded pre-test hooks and this mean we lose
143-
// our chance to hook the Require call. In order to instrument the code
144-
// we have to decache the JS file so on next load it gets instrumented.
145-
// This doesn't impact tests, but is a concern if we had some integration
146-
// tests that relied on VSCode accessing our module since there could be
147-
// some shared global state that we lose.
148-
decache(fullPath);
149-
});
150-
151-
self.matchFn = (file: string): boolean => fileMap[file];
152-
self.matchFn.files = Object.keys(fileMap);
153-
154-
// Hook up to the Require function so that when this is called, if any of our source files
155-
// are required, the instrumented version is pulled in instead. These instrumented versions
156-
// write to a global coverage variable with hit counts whenever they are accessed
157-
self.transformer = self.instrumenter.instrumentSync.bind(self.instrumenter);
158-
const hookOpts = { verbose: true, extensions: ['.js'] };
159-
istanbul.hook.hookRequire(self.matchFn, self.transformer, hookOpts);
160-
161-
// initialize the global variable to stop mocha from complaining about leaks
162-
global[self.coverageVar] = {};
163-
}
164-
165-
/**
166-
* Writes a coverage report.
167-
* Note that as this is called in the process exit callback, all calls must be synchronous.
168-
*
169-
* @returns {void}
170-
*
171-
* @memberOf CoverageRunner
172-
*/
173-
public reportCoverage(): void {
174-
const self = this;
175-
istanbul.hook.unhookRequire();
176-
let cov: any;
177-
if (typeof global[self.coverageVar] === 'undefined' || Object.keys(global[self.coverageVar]).length === 0) {
178-
console.error('No coverage information was collected, exit without writing coverage information');
179-
return;
180-
} else {
181-
cov = global[self.coverageVar];
182-
}
183-
184-
// TODO consider putting this under a conditional flag
185-
// Files that are not touched by code ran by the test runner is manually instrumented, to
186-
// illustrate the missing coverage.
187-
self.matchFn.files.forEach((file: any) => {
188-
if (cov[file]) {
189-
return;
190-
}
191-
self.transformer(fs.readFileSync(file, 'utf-8'), file);
192-
193-
// When instrumenting the code, istanbul will give each FunctionDeclaration a value of 1 in coverState.s,
194-
// presumably to compensate for function hoisting. We need to reset this, as the function was not hoisted,
195-
// as it was never loaded.
196-
Object.keys(self.instrumenter.coverState.s).forEach((key) => {
197-
self.instrumenter.coverState.s[key] = 0;
198-
});
199-
200-
cov[file] = self.instrumenter.coverState;
201-
});
202-
203-
// TODO Allow config of reporting directory with
204-
const reportingDir = paths.join(self.testsRoot, self.options.relativeCoverageDir);
205-
const includePid = self.options.includePid;
206-
const pidExt = includePid ? ('-' + process.pid) : '';
207-
const coverageFile = paths.resolve(reportingDir, `coverage${pidExt}.json`);
208-
209-
// yes, do this again since some test runners could clean the dir initially created
210-
_mkDirIfExists(reportingDir);
211-
212-
fs.writeFileSync(coverageFile, JSON.stringify(cov), 'utf8');
213-
214-
const remappedCollector = remapIstanbul.remap(cov, {
215-
warn: (warning: any) => {
216-
// We expect some warnings as any JS file without a typescript mapping will cause this.
217-
// By default, we'll skip printing these to the console as it clutters it up
218-
if (self.options.verbose) {
219-
console.warn(warning);
220-
}
221-
}
222-
});
223-
224-
const reporter = new istanbul.Reporter(undefined, reportingDir);
225-
const reportTypes = (self.options.reports instanceof Array) ? self.options.reports : ['lcov'];
226-
reporter.addAll(reportTypes);
227-
reporter.write(remappedCollector, true, () => {
228-
console.log(`reports written to ${reportingDir}`);
229-
});
230-
}
231-
}
89+
exports.run = run;

0 commit comments

Comments
 (0)