jeffrifwald / babel-istanbul

Yet another JS code coverage tool that computes statement, line, function and branch coverage with module loader hooks to transparently add coverage when running tests. Supports all JS coverage use cases including unit tests, server side functional tests and browser tests. Built for scale.
Other
144 stars 23 forks source link

Default exports are listed as having an uncovered `else` branch #63

Closed Vinnl closed 8 years ago

Vinnl commented 8 years ago

(I first described this problem at StackOverflow.)

My code coverage report lists an uncovered else branch when I just run export default <module_name>:

tmlbz

Coincidentally, I was running version 6.7.4 of Babel locally and didn't run into this, but versions 6.7.5 and above to trigger this. It might be a problem with me or Babel, but this felt like the logical first place to try to me after trying to find a problem at my side. If it's not, do let me know :)

(My code is located at GitLab, if it helps.)

jeffrifwald commented 8 years ago

I did some testing and it looks like babel 6.7.5 and 6.7.4 create the exact same code for the module above. Something interesting I did note from your GitLab repo is that you are using the rewire plugin. My hunch is that rewire is adding a branch at that statement. Maybe experiment with that and see if that is the real cause of the issue.

jeffrifwald commented 8 years ago

Take a look at the output difference in no rewire vs rewire:

'use strict';

Object.defineProperty(exports, "__esModule", {
    value: true
});

var _selectIsConfiguring = require('../../../selectors/selectIsConfiguring');

var _selectIsConfiguring2 = _interopRequireDefault(_selectIsConfiguring);

var _selectCurrentMessage = require('../../../selectors/selectCurrentMessage');

var _selectCurrentMessage2 = _interopRequireDefault(_selectCurrentMessage);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function intent(drivers) {
    return {
        isConfiguring$: (0, _selectIsConfiguring2.default)(drivers.DOM, drivers.History, drivers.Keypress),
        currentMessage$: (0, _selectCurrentMessage2.default)(drivers.DOM, drivers.History)
    };
}

exports.default = intent;
'use strict';

Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.__RewireAPI__ = exports.__ResetDependency__ = exports.__set__ = exports.__Rewire__ = exports.__GetDependency__ = exports.__get__ = undefined;

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };

var _selectIsConfiguring = require('../../../selectors/selectIsConfiguring');

var _selectIsConfiguring2 = _interopRequireDefault(_selectIsConfiguring);

var _selectCurrentMessage = require('../../../selectors/selectCurrentMessage');

var _selectCurrentMessage2 = _interopRequireDefault(_selectCurrentMessage);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function intent(drivers) {
    return {
        isConfiguring$: _get__('selectIsConfiguring')(drivers.DOM, drivers.History, drivers.Keypress),
        currentMessage$: _get__('selectCurrentMessage')(drivers.DOM, drivers.History)
    };
}

exports.default = _get__('intent');
var _RewiredData__ = {};
var _RewireAPI__ = {};

(function () {
    function addPropertyToAPIObject(name, value) {
        Object.defineProperty(_RewireAPI__, name, {
            value: value,
            enumerable: false,
            configurable: true
        });
    }

    addPropertyToAPIObject('__get__', _get__);
    addPropertyToAPIObject('__GetDependency__', _get__);
    addPropertyToAPIObject('__Rewire__', _set__);
    addPropertyToAPIObject('__set__', _set__);
    addPropertyToAPIObject('__reset__', _reset__);
    addPropertyToAPIObject('__ResetDependency__', _reset__);
    addPropertyToAPIObject('__with__', _with__);
})();

function _get__(variableName) {
    return _RewiredData__ === undefined || _RewiredData__[variableName] === undefined ? _get_original__(variableName) : _RewiredData__[variableName];
}

function _get_original__(variableName) {
    switch (variableName) {
        case 'selectIsConfiguring':
            return _selectIsConfiguring2.default;

        case 'selectCurrentMessage':
            return _selectCurrentMessage2.default;

        case 'intent':
            return intent;
    }

    return undefined;
}

function _assign__(variableName, value) {
    if (_RewiredData__ === undefined || _RewiredData__[variableName] === undefined) {
        return _set_original__(variableName, value);
    } else {
        return _RewiredData__[variableName] = value;
    }
}

function _set_original__(variableName, _value) {
    switch (variableName) {}

    return undefined;
}

function _update_operation__(operation, variableName, prefix) {
    var oldValue = _get__(variableName);

    var newValue = operation === '++' ? oldValue + 1 : oldValue - 1;

    _assign__(variableName, newValue);

    return prefix ? newValue : oldValue;
}

function _set__(variableName, value) {
    return _RewiredData__[variableName] = value;
}

function _reset__(variableName) {
    delete _RewiredData__[variableName];
}

function _with__(object) {
    var rewiredVariableNames = Object.keys(object);
    var previousValues = {};

    function reset() {
        rewiredVariableNames.forEach(function (variableName) {
            _RewiredData__[variableName] = previousValues[variableName];
        });
    }

    return function (callback) {
        rewiredVariableNames.forEach(function (variableName) {
            previousValues[variableName] = _RewiredData__[variableName];
            _RewiredData__[variableName] = object[variableName];
        });
        var result = callback();

        if (!!result && typeof result.then == 'function') {
            result.then(reset).catch(reset);
        } else {
            reset();
        }

        return result;
    };
}

var _typeOfOriginalExport = typeof intent === 'undefined' ? 'undefined' : _typeof(intent);

function addNonEnumerableProperty(name, value) {
    Object.defineProperty(intent, name, {
        value: value,
        enumerable: false,
        configurable: true
    });
}

if ((_typeOfOriginalExport === 'object' || _typeOfOriginalExport === 'function') && Object.isExtensible(intent)) {
    addNonEnumerableProperty('__get__', _get__);
    addNonEnumerableProperty('__GetDependency__', _get__);
    addNonEnumerableProperty('__Rewire__', _set__);
    addNonEnumerableProperty('__set__', _set__);
    addNonEnumerableProperty('__reset__', _reset__);
    addNonEnumerableProperty('__ResetDependency__', _reset__);
    addNonEnumerableProperty('__with__', _with__);
    addNonEnumerableProperty('__RewireAPI__', _RewireAPI__);
}

exports.__get__ = _get__;
exports.__GetDependency__ = _get__;
exports.__Rewire__ = _set__;
exports.__set__ = _set__;
exports.__ResetDependency__ = _reset__;
exports.__RewireAPI__ = _RewireAPI__;
jeffrifwald commented 8 years ago

babel-istanbul is usually pretty good about auto-ignoring single lines of code generated by babel, but I'm actually not sure about how this specific case works. There is a pretty complex relationship between that plugin and babel itself.

This looks like a clue: https://www.npmjs.com/package/babel-plugin-rewire#combining-with-other-pluginstools , but I am unsure as to how well this tool actually works well with source maps.

jeffrifwald commented 8 years ago

Another clue below, looks like the rewire plugin changed something a little while ago:

https://github.com/speedskater/babel-plugin-rewire/issues/95

Vinnl commented 8 years ago

Huh, I guess you're right - it only works with 6.7.4 locally :S

It seems very likely that the issue then lies in the interaction with rewire - that's what I get for setting up a toolchain with so many moving parts. Swapping out babel-istanbul with isparta (which appears to be suggested by the rewire plugin) did result in a correct code coverage report, so I guess that's good enough for me. However, I'm not sure if you're interested in seeing this work with babel-istanbul as well? I'm not familiar enough with the tools to be able to help out with the code, but since you already spent so much time diving into this, if there's anything I can do to help test, let me know.

Otherwise, just leave this closed :) In any case, thank you so much for taking the time to help and actually pinpoint the likely issue.