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.