What does this mean in a jasmine test: Uncaught TypeError: Object prototype may only be an Object or null: undefined?

Viewed 1517

I'm working to get some Typescript classes tested with Jasmine. I've created some classes that compose with each other via module declarations:

module xxx.DeviceData {
    export class ConsoleListener extends DataListener {
        constructor() {
            super("ConsoleListener");
        }

        addData(data: string) {
            console.log(this.title + ": " + data);
        }

        clear() {
            console.clear();
        }
    }
}

I'm composing the Jasmine tests together the same way:

module xxx.DeviceData {
 describe('ConsoleListener test', function () {
     var consoleListener,
         consoleListenerObj;

     beforeEach(() => {
         consoleListener = jasmine.createSpy("consoleListener");

         consoleListenerObj = jasmine.createSpyObj('consoleListenerObj', ['onSuccess', 'onFailure', 'addData', 'clear']);
         consoleListenerObj.onSuccess();
         consoleListenerObj.onFailure();
         consoleListenerObj.addData();
         consoleListenerObj.clear();
     });

     it('test#1 columnDefinition defined', function () {
         let consoleListener = new ConsoleListener();

         expect(consoleListener).not.toBeNull();
     });

     it('test#2 call onSuccess', function () {
         expect(consoleListenerObj.onSuccess).toHaveBeenCalled();
     });

     it('test#3 call onFailure', function () {
         expect(consoleListenerObj.onFailure).toHaveBeenCalled();
     });

     it('test#4 call addData', function () {
         expect(consoleListenerObj.addData('123'));
     });

     it('test#5 call clear', function () {
         expect(consoleListenerObj.clear());
     });
 });
}

This all transpiles perfectly. When I try to execute the test, I receive this error

Uncaught TypeError: Object prototype may only be an Object or null: undefined at Scripts/DeviceData/ConsoleListener.js:5:27

Something is going wrong on line 5 of the transpiled JS, right? Here is that:

var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var xxx;
(function (xxx) {
    var DeviceData;
    (function (DeviceData) {
        var ConsoleListener = (function (_super) {
            __extends(ConsoleListener, _super);
            function ConsoleListener() {
                return _super.call(this, "ConsoleListener") || this;
            }
            ConsoleListener.prototype.addData = function (data) {
                console.log(this.title + ": " + data);
            };
            ConsoleListener.prototype.clear = function () {
                console.clear();
            };
            return ConsoleListener;
        }(DeviceData.DataListener));
        DeviceData.ConsoleListener = ConsoleListener;
    })(DeviceData = xxx.DeviceData || (xxx.DeviceData = {}));
})(xxx|| (xxx= {}));
//# sourceMappingURL=ConsoleListener.js.map

And sure enough, line 5 seems to be talking about objects and prototypes.

I've tried different ways to get the modules to talk to each other, but this module approach is the only one I can get to work consistently. Is there something missing from the karma / jasmine context that needs to be passed here?

Here is my karma.config:

    module.exports = function (config) {
        config.set({

            frameworks: ["jasmine","karma-typescript"],

            preprocessors: {
                "Scripts/**/*.ts": ["karma-typescript"]
            },

            files: [
                'Scripts/DeviceData/*.ts',
                'Scripts/UnitTests/*.spec.ts' 
            ],

            exclude: [
                'Scripts/**/NodeJSDataSocket.ts'
            ],
            reporters: ["progress", "karma-typescript"],

            //reporters: ["dots", "karma-typescript"],

            browsers: ["Chrome"],
            karmaTypescriptConfig: {
                compilerOptions: {
                    "module": "commonjs",
                    "sourceMap": true,
                    "target": "es5"
"moduleResolution": "classic",
"noImplicitAny": false
                },
                tsconfig: "./tsconfig.json",
            },
        });
    };
2 Answers

The error looks like it is coming from the extends function TypeScript injects when you inherit from a super class.

Looking at the code, I'd say your DataListener is not available to you when you use it:

extends DataListener 

It is probably not missing entirely, otherwise the compiler would warn you - so it is either not included (i.e. loaded) when Jasmine runs, or you've got things loading out of order.

Get them in order and hopefully... joy!

        files: [
            'Scripts/DeviceData/DataListener.ts',
            'Scripts/DeviceData/ConsoleListener.ts',
            'Scripts/UnitTests/*.spec.ts' 
        ],

The __extends() function is generated by the TypeScript compiler to handle class inheritance. b means base class and d is for derived class. So, the issue is that the base class DataListener is missing. Check the way your compiling and bundling the scripts. One namespace (cf. module keyword) can be defined across several files but their gathering/bundling must be handled by hand or with the compiler option --outFile.

Related