karma: [object ErrorEvent] thrown in angular app, Debug window.

Expected behaviour

Passing tests in the command line should match passing tests in the karma debugger.

Actual behaviour

In debug browser, a test that passes according to the command line fails in the debug browser with error message: [object ErrorEvent] thrown.

Environment Details

  • Karma version (output of karma --version):
  • Relevant part of your karma.config.js file

There error itself is being thrown in zone.js/dist/zone.js: 195 , with zone.js version = 0.8.12.

From package.json

"devDependencies": {
        "@ionic/app-scripts": "2.1.4",
        "@types/jasmine": "^2.6.0",
        "@types/node": "^8.0.19",
        "angular2-template-loader": "^0.6.2",
        "html-loader": "^0.5.1",
        "istanbul-instrumenter-loader": "^3.0.0",
        "jasmine": "^2.8.0",
        "jasmine-spec-reporter": "^4.2.1",
        "karma": "^1.7.1",
        "karma-chrome-launcher": "^2.2.0",
        "karma-coverage-istanbul-reporter": "^1.3.0",
        "karma-jasmine": "^1.1.0",
        "karma-jasmine-html-reporter": "^0.2.2",
        "karma-sourcemap-loader": "^0.3.7",
        "karma-webpack": "^2.0.5",
        "null-loader": "^0.1.1",
        "protractor": "^5.2.0",
        "ts-loader": "^3.0.3",
        "ts-node": "^3.3.0",
        "typescript": "~2.4.0"
    },

karma.conf.js

var webpackConfig = require('./webpack.test.js');

module.exports = function(config) {
  var _config = {
    basePath: '../',

    frameworks: ['jasmine'],

    files: [
      {
        pattern: './test-config/karma-test-shim.js',
        watched: true
      },
      {
        pattern: './src/assets/**/*',
        watched: false,
        included: false,
        served: true,
        nocache: false
      }
    ],

    proxies: {
      '/assets/': '/base/src/assets/'
    },

    preprocessors: {
      './test-config/karma-test-shim.js': ['webpack', 'sourcemap']
    },

    webpack: webpackConfig,

    webpackMiddleware: {
      stats: 'errors-only'
    },

    webpackServer: {
      noInfo: true
    },

    browserConsoleLogOptions: {
      level: 'log',
      format: '%b %T: %m',
      terminal: true
    },

    coverageIstanbulReporter: {
      reports: [ 'html', 'lcovonly' ],
      fixWebpackSourcePaths: true
    },

    reporters: config.coverage ? ['kjhtml', 'dots', 'coverage-istanbul'] : ['kjhtml', 'dots'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  };

  config.set(_config);
};

karma-test-shim.js


require('core-js/es6');
require('core-js/es7/reflect');

require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy');
require('zone.js/dist/sync-test');
require('zone.js/dist/jasmine-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');

var appContext = require.context('../src', true, /\.spec\.ts/);

appContext.keys().forEach(appContext);

var testing = require('@angular/core/testing');
var browser = require('@angular/platform-browser-dynamic/testing');

testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting());

The test itself:

I removed the @Injectable tag in the hopes that it would isolate angular from the issue. The test “LocationService Lat/Lon to tile conversion should match for the two lat2TileY functions” (the last test) is the one that passes in the command line but fails in the debugger.

Also, I can get the test to pass if I comment out the two tests preceding the last test, but not if I comment out the second-to-last test or the third-to-last test only. Kind of puzzled as to how that’s happening.

import { Coordinates, Geolocation } from '@ionic-native/geolocation';
import { LocationNoAngular } from './location-no-angular';
import { LocationService } from './location.service';
import * as LocationTestData from './location-spec-data';

describe('LocationService', () => {
  let locationService: LocationNoAngular;
  const epsilon = .004;
  beforeAll(() => {
    locationService = new LocationNoAngular(new Geolocation());
  });

  describe('getDistanceFunction', () => {
    it('should return zero for identical locations', () => {
      const distance: number = locationService.getDistance(
        LocationTestData.zeroDistanceTestPoint,
        LocationTestData.zeroDistanceTestPoint
      );

      expect(Math.abs(distance)).toBeLessThan(epsilon);
    });

    it('should return 179.9 for distance between known coordinates', () => {
      const distance: number = locationService.getDistance(
        LocationTestData.knownDistanceTestPoint1,
        LocationTestData.knownDistanceTestPoint2
      );

      expect(Math.abs(distance - LocationTestData.knownDistance)).toBeLessThan(epsilon);
    });
  });

  describe('Lat/Lon to tile conversion', () => {
    it('should map longitude to x tiles 0, 1, 2, 3 on lower bound', () => {
      expect(locationService.lon2TileX(-180, 2)).toEqual(0);
      expect(locationService.lon2TileX(-90, 2)).toEqual(1);
      expect(locationService.lon2TileX(0, 2)).toEqual(2);
      expect(locationService.lon2TileX(90, 2)).toEqual(3);
    });

    it('should map longitude to x tiles 0, 1, 2, 3 on upper boundbound', () => {
      expect(locationService.lon2TileX(-91, 2)).toEqual(0);
      expect(locationService.lon2TileX(-1, 2)).toEqual(1);
      expect(locationService.lon2TileX(89, 2)).toEqual(2);
      expect(locationService.lon2TileX(179, 2)).toEqual(3);
    });

    it('should match the alternative lat/lon to tile function', () => {
      expect(locationService.lon2TileX(-91, 2)).toEqual(locationService.lon2TileXVerify(-91, 2));
      expect(locationService.lon2TileX(-1, 2)).toEqual(locationService.lon2TileXVerify(-1, 2));
      expect(locationService.lon2TileX(89, 2)).toEqual(locationService.lon2TileXVerify(89, 2));
      expect(locationService.lon2TileX(179, 2)).toEqual(locationService.lon2TileXVerify(179, 2));
    });

    it('should map lat2TileY between 0, 1, 2, 3', () => {
      expect(locationService.lat2TileY(-85, 2)).toEqual(3);
      expect(locationService.lat2TileY(-66, 2)).toEqual(2);
      expect(locationService.lat2TileY(1, 2)).toEqual(1);
      expect(locationService.lat2TileY(67, 2)).toEqual(0);
      expect(true).toBe(true);
    });

    it('should map lat2TileY between 0, 1, 2, 3', () => {
      expect(locationService.lat2TileY(-85, 2)).toEqual(3);
      expect(locationService.lat2TileY(-66, 2)).toEqual(2);
      expect(locationService.lat2TileY(1, 2)).toEqual(1);
      expect(locationService.lat2TileY(67, 2)).toEqual(0);
    });

    it('should match for the two lat2TileY functions', function() {
      expect(locationService.lat2TileY(-85, 2)).toEqual(locationService.lat2TileYVerify(-85, 2));
      expect(locationService.lat2TileY(-60, 2)).toEqual(locationService.lat2TileYVerify(-60, 2));
      expect(locationService.lat2TileY(1, 2)).toEqual(locationService.lat2TileYVerify(1, 2));
      expect(locationService.lat2TileY(67, 2)).toEqual(locationService.lat2TileYVerify(67, 2));
    });


  });

})

The LocationNoAngular class (abbreviated)

export class LocationNoAngular {

  private locationSubscription: Subscription;
  private currentLocation: Geoposition
  public locationData: Subject<Geoposition>;
  public readonly metersPerDegreeLat: number = 111111;

  constructor(private geolocation: Geolocation) {
    this.locationData = new Subject<any>();

  }

...skipping some methods...

  lon2TileXVerify(lon, zoom) {
    return (Math.floor((lon + 180) / 360 * Math.pow(2, zoom)));
  }

  lat2TileYVerify(lat, zoom) {
    return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom)));
  }

  // https://msdn.microsoft.com/en-us/library/bb259689.aspx
  lon2TileX(lon, zoom) {
    return Math.floor(((lon + 180) / 360) * Math.pow(2, zoom));
  }

  lat2TileY(lat, zoom) {
    const sinLatitude: number = Math.sin(this.degreesToRadians(lat));
    return Math.floor((.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI)) * Math.pow(2, zoom));
  }

Steps to reproduce the behaviour

Hopefully the above information is enough, but happy to setup more detail if necessary. It’s an ionic3 project.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 49 (6 by maintainers)

Most upvoted comments

In case anyone still has this issue, I found the easiest way to see the real error : simply open your browser running the tests, and open the console. The error will be there.

What fixed it for me was taking this: beforeEach(() => { fixture = TestBed.createComponent(ConversationComponent); component = fixture.componentInstance; fixture.detectChanges(); });

and removingfixture.detectChanges();

Still not sure why this fixed it bu I had [object ErrorEvent] thrown on just about all my specs until I removed it

This works for me

Just comment this line

// fixture.detectChanges();

Running tests with ng test --source-map=false let me see what the actual error message was, instead of the cryptic [object ErrorEvent] thrown. So I could narrow it down to exactly where the problem was.

@trichetriche => That is fine if you are running the tests locally, but my problem is that the tests were working 100% correctly whenever I was using the browser on my local machine (no errors either in the browser or in the browser’s console), but they were failing on our CI/CD environment when it was running the Chrome Headless browser via Puppeteer.

If we look at the definition given in the docs for dixture.detectChanges(). I don’t think the solution given by @JonahElbaz is viable. You must tell the TestBed to perform data binding by calling fixture.detectChanges(). Only then does the <h1> have the expected title.

https://angular.io/guide/testing

any fix for this issue?

I have found the cause! It has nothing to do with karma, but the issue is caused by the source-map-support package which is installed by @angular/angular-cli.

The Angular source maps refer to paths like ng:///DynamicTestModule/YourComponent.ngfactory.js which triggers CORS errors.

Should be fixed by https://github.com/evanw/node-source-map-support/pull/222 and/or by the Angular CLI.

For me, it helped to wrap the test-function with async(...).
Then I get the correct error instead of [object ErrorEvent] thrown

import { async } from '@angular/core/testing';

it(`does anything`, async(() => {
    ...
}));

We have had this issue for more than a year now. We have to switch between --source-map=true and --source-map=false all the time. Can this issue be reopened?

I found the solution but I don’t remember link to it.

The problem was caused by using “this.router.navigate([‘/login’]);” in ngOnInit method in completely other component. In your tests you need to specify:

                RouterTestingModule.withRoutes([
                    { path: 'login', component: LoginComponent }
                ]),

and put this formula into “imports”, and also “LoginComponent” into “declarations”.

For anyone else who might be having this issue, I have a feeling it is likely related to: https://stackoverflow.com/questions/45722256/how-do-i-debug-a-object-errorevent-thrown-error-in-my-karma-jasmine-tests. I haven’t been able to identify the exact place in my code that’s triggering the error, but when porting most of the code over to an angular-cli-generated project and when using the default karma.conf.js, the problem goes away.

The only trace I have when executing tests is this:

image

I had the same issue and found the cause is this.router.navigate used in ngOnInit.

Solution: Add below code in spec.ts

const mockRoute = { navigate: jasmine.createSpy(‘navigate’) };

Likewise mock others nested function call.

I may be a bit late on this topic, but I agree with @leotm : the routing is in cause of those issues.

Like him, I commented this line this.route.snapshot.params.versionId and my error magically appeared (with source mapping disabled).

I thought I should post a message, if it could help …

TL;DR: It may be related to testing routing.

I’m getting [object ErrorEvent] thrown too. I traced it to one line of code. Here’s my code snippet.

this.username = this.userService.getUser(this.route.snapshot.paramMap.get('id'))[0];

The problem lies with the test environment attempting to evaluate the this.route.snapshot.paramMap.get('id') part.

If I replace it with 0, [object ErrorEvent] thrown goes away.

My userService has a user like so: public users = [ ["admin", "First name", "Surname", etc... ] ].

So 0 just gets this user, at index 0.

Otherwise when normally running my app, this.route.snapshot.paramMap.get('id') is evaluated when the user selects a user to edit from my table of users.

So in my HTML, *ngFor="let user of users; index as i" loops to display all the users and routerLink="/edit/{{i}}" so you can click on edit buttons for each user, which when clicked go to e.g. http://localhost:4200/edit/0 to edit the aforementioned admin user’s details.

I changed the title to help others find your hint.

My guess is that you have a race or an async test that isn’t set up quite right. I would just assume that the debug case needs to be fixed and ignore that the command line passes.

This is probably not a karma-runner bug but reopen if you find more info suggesting it is. Esp. if you find that there is a better way to report that error.