Reputation: 2572
I want to test a function having no body in Typescript project. I created a utility JS function that works perfectly fine. Given below:
function isEmpty(f) {
// Get content between first { and last }
const m = f.toString().match(/\{([\s\S]*)\}/m);
// Strip comments
const l = m && m[1].replace(/^\s*\/\/.*$/mg, '').trim();
if (l.length === 0) {
return true;
} else {
return false;
}
};
function doSeatMouseClick(_event, _seat) {
//do nothing, read only
}
var bool = isEmpty(doSeatMouseClick);
console.log(bool)
In the Spec file, when I access this isEmpty function. Tests are getting failed and on console logging that function I see some extra code which I guess webpack preprocessors is adding.
describe('doSeatMouseClick()', () => {
it('should be empty', () => {
console.log(selectionTool.doSeatMouseClick.toString());
const bool = isEmpty(selectionTool.doSeatMouseClick);
expect(bool).toBeTruthy();
});
});
Screenshot of failed test:
Necessary Configuration added below:
Webpack Config:
const path = require('path');
const webpack = require('webpack');
const htmlWebpackPlugin = require('html-webpack-plugin');
const commonConfig = require('./base.js');
const merge = require('webpack-merge');
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const PACKAGE = require('../package.json');
const appVersion = PACKAGE.version;
const rootpath = path.resolve(__dirname, '..');
console.log("Creating for development");
var ENV = process.env.NODE_ENV;
module.exports = merge(commonConfig, {
mode: 'development',
devtool: 'eval-cheap-module-source-map', //eval-cheap-source-map
output: {
library: "Library",
libraryTarget: 'umd',
libraryExport: "default",
umdNamedDefine: true,
path: path.resolve(rootpath, 'dist'),
// publicPath: '/',
filename: '[name].js',
pathinfo: false
},
module: {
rules: [
{
test: /\.(jpe?g|png|gif|svg)$/,
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'assets/[name].[hash:8].[ext]'
}
},
{
test: /\.worker\.ts$/,
loader: 'worker-loader',
options: {
inline: true,
fallback: false
}
},
{
// Include ts, tsx, js, and jsx files.
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
loader: require.resolve('babel-loader'),
query: { compact: false }
},
{
test: /(\.scss|\.css)$/,
use: [{
loader: MiniCssExtractPlugin.loader
}, {
loader: 'css-loader',
options: {
sourceMap: true,
modules: {
localIdentName: '[name]-[hash:base64:5]'
}
}
},
{
loader: 'postcss-loader' //PostCSS plugins go here
}, {
loader: 'sass-loader',
options: {
sourceMap: true
}
}]
}
]
},
plugins: [
new LodashModuleReplacementPlugin,
new htmlWebpackPlugin({ //create html file to serve the bundle
template: path.join(rootpath, '', 'index.html'), //webpack build html file for us in dist folder(will take this index.html file and inject main.js file)
inject: false
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(ENV),
app_version: JSON.stringify(appVersion)
}),
new MiniCssExtractPlugin({
filename: '[name].css'
})
// new BundleAnalyzerPlugin()
],
optimization: {
//some code
},
devServer: {
port: 3000,
open: true,
inline: true,
stats: 'errors-only'
}
});
Karma Config:
const webpackConfig = require('./webpack/test');
const testPattern = 'src/**/*.spec.ts';
// const testPattern = 'src/models/tools/*.spec.ts';
module.exports = function (config) {
config.set({
basePath: '',
// frameworks to use
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
{ pattern: testPattern, watched: false }
],
// list of files / patterns to exclude
exclude: [
'src/**/*.d.ts',
'src/**/*.scss'
],
// preprocess matching files before serving them to the browser
// Source maps can be found on clicking DEBUG btn
// Preprocessor will convert Typescript to Javascript
preprocessors: {
[testPattern]: ['webpack', 'sourcemap']
},
webpack: webpackConfig,
webpackMiddleware: {
stats: 'errors-only'
},
webpackServer: {
noInfo: true
},
coverageIstanbulReporter: {
fixWebpackSourcePaths: true,
reports: ['html', 'text-summary'],
// enforce percentage thresholds
// anything under these percentages will cause karma to fail with an exit code of 1 if not running in watch mode
// thresholds: {
// emitWarning: true, // set to `true` to not fail the test command when thresholds are not met
// // thresholds for all files
// global: {
// statements: 100,
// lines: 100,
// branches: 100,
// functions: 100
// },
// // thresholds per file
// each: {
// statements: 100,
// lines: 100,
// branches: 100,
// functions: 100
// // overrides: {
// // 'baz/component/**/*.js': {
// // statements: 98
// // }
// // }
// }
// },
// Omit files with no statements, no functions and no branches covered from the report
skipFilesWithNoCoverage: true,
verbose: true // output config used by istanbul for debugging
},
// Enable or disable failure on running empty test-suites. If disabled the program will return exit-code 0 and display a warning.
failOnEmptyTestSuite: false,
// test results reporter to use
reporters: ['spec', 'coverage-istanbul'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
browsers: ['ChromeHeadless'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
});
};
I don't know how to tackle this or Am I doing anything wrong here. Any suggestions would be helpful.
Upvotes: 3
Views: 748
Reputation: 961
It looks like the cause of the function not being empty is your coverage tracker, thus the "cov" part of cov_10clr7afd6.f[9]++
. This is one of the problems with inspecting any code that has gone through compilation steps, it might not be what you would reasonably expect.
Here's some options:
function isEmpty(f) {
// Get content between first { and last }
const m = f.toString().match(/\{([\s\S]*)\}/m);
// Strip comments
const l = m && m[1]
.replace(/^\s*\/\/.*$/mg, '')
.replace(/cov_\w*\.f\[[0-9]+\]\+\+;?/g, '')
.trim();
if (l.length === 0) {
return true;
} else {
return false;
}
};
function doSeatMouseClick(_event, _seat) {
//do nothing, read only
cov_10clr7afd6.f[9]++;
cov_10clr7afd6.f[500]++
}
var bool = isEmpty(doSeatMouseClick);
console.log(bool)
This solution is as well since any change in your codecov tool could break this.
There's a lint rule https://eslint.org/docs/rules/no-empty-function which does this, but intentionally ignores functions with comments. You could either take this behavior, or you could write your own based on it by simplifying the reportIfEmpty
function in its source.
// https://github.com/eslint/eslint/blob/master/lib/rules/no-empty-function.js
function reportIfEmpty(node) {
const kind = getKind(node);
const name = astUtils.getFunctionNameWithKind(node);
if (allowed.indexOf(kind) === -1 &&
node.body.type === "BlockStatement" &&
node.body.body.length === 0
) {
context.report({
node,
loc: node.body.loc,
messageId: "unexpected",
data: { name }
});
}
}
You can add this new rule to your eslint config using following Create custom ESLint rules in 2 minutes.
This should be a more robust solution, but may highlight other errors that you don't want to deal with. You can use an eslint override to help target the rule to the files you want.
// .eslintrc
{
"overrides": [
{
"files": ["some/path/*.ts"],
"rules": {
"my-project/no-empty-functions": "error"
}
}
]
}
Upvotes: 3