From 7425a5f5876cb77e34b5fadab5f8931897b03d37 Mon Sep 17 00:00:00 2001 From: Frank Ebersoll Date: Tue, 24 Nov 2015 20:04:15 +0100 Subject: [PATCH 1/6] Replaced karma/jasmine with mocha/chai for faster testing --- gulpfile.js | 12 ++- karma.conf.js | 30 ------- package.json | 10 +-- src/ExceptionlessClient-spec.ts | 32 ++++---- src/Utils-spec.ts | 47 +++++------ src/configuration/Configuration-spec.ts | 33 ++++---- src/configuration/SettingsManager-spec.ts | 6 +- src/plugins/EventPluginManager-spec.ts | 62 ++++++++------- .../default/DuplicateCheckerPlugin-spec.ts | 9 ++- src/plugins/default/ErrorPlugin-spec.ts | 21 ++--- src/queue/DefaultEventQueue-spec.ts | 23 +++--- src/storage/InMemoryStorage-spec.ts | 79 ++++++++++--------- .../DefaultSubmissionClient-spec.ts | 33 ++++---- src/tsconfig.test.json | 3 +- tsd.json | 12 ++- 15 files changed, 201 insertions(+), 211 deletions(-) delete mode 100644 karma.conf.js diff --git a/gulpfile.js b/gulpfile.js index 121637cc..bcf337a9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -116,10 +116,14 @@ gulp.task('exceptionless.test.umd', ['typescript.test'], function () { }); gulp.task('test', ['exceptionless.test.umd'], function(done) { - var Server = require('karma').Server; - new Server({ - configFile: __dirname + '/karma.conf.js' - }, done).start(); + var mocha = require('gulp-mocha'); + return gulp.src('dist/temp/exceptionless-spec.js', { read: false }) + .pipe(mocha({ + require: ['source-map-support/register'] + })) + .once('end', function () { + process.exit(); + }); }); gulp.task('format', function () { diff --git a/karma.conf.js b/karma.conf.js deleted file mode 100644 index ed82e42e..00000000 --- a/karma.conf.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -module.exports = function (config) { - config.set({ - basePath: 'src', - browserNoActivityTimeout: 100000, - browsers: ['Chrome'], - frameworks: ['jasmine'], - files: [ - '../node_modules/es5-shim/es5-shim.js', - '../node_modules/es6-shim/es6-shim.js', - '../dist/temp/exceptionless-spec.js' - ], - exclude: [], - reporters: ['progress'], - port: 9876, - 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, - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: true - }); -}; diff --git a/package.json b/package.json index cd79d1b3..4399a047 100644 --- a/package.json +++ b/package.json @@ -27,25 +27,21 @@ }, "devDependencies": { "bower": "1.6.5", + "chai": "^3.4.1", "del": "2.1.0", "es5-shim": "4.3.1", "es6-shim": "0.33.13", "gulp": "3.9.0", "gulp-concat": "2.6.0", "gulp-exec": "^2.1.2", + "gulp-mocha": "^2.2.0", "gulp-replace": "0.5.4", "gulp-sourcemaps": "1.6.0", "gulp-tslint": "3.6.0", "gulp-uglify": "1.5.1", "gulp-wrap-umd": "0.2.1", - "jasmine-core": "2.3.4", - "karma": "0.13.15", - "karma-chrome-launcher": "^0.2.1", - "karma-cli": "0.1.1", - "karma-jasmine": "0.3.6", - "karma-phantomjs-launcher": "0.2.1", - "phantomjs": "^1.9.18", "rimraf": "2.4.3", + "source-map-support": "^0.3.3", "tracekit": "0.3.1", "tsproject": "1.0.5", "typescript": "1.6.2", diff --git a/src/ExceptionlessClient-spec.ts b/src/ExceptionlessClient-spec.ts index f0b78a07..f661090c 100644 --- a/src/ExceptionlessClient-spec.ts +++ b/src/ExceptionlessClient-spec.ts @@ -1,55 +1,57 @@ import { ExceptionlessClient } from './ExceptionlessClient'; import { EventPluginContext } from './plugins/EventPluginContext'; +import { expect } from 'chai'; describe('ExceptionlessClient', () => { it('should use event reference ids', (done) => { let error = new Error('From Unit Test'); let client = new ExceptionlessClient('LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', 'http://localhost:50000'); - expect(client.config.lastReferenceIdManager.getLast()).toBe(null); + expect(client.config.lastReferenceIdManager.getLast()).to.be.null; client.submitException(error, (context: EventPluginContext) => { - expect(client.config.lastReferenceIdManager.getLast()).toBe(null); + expect(client.config.lastReferenceIdManager.getLast()).to.be.null; }); let numberOfPlugins = client.config.plugins.length; client.config.useReferenceIds(); - expect(client.config.plugins.length).toBe(numberOfPlugins + 1); + expect(client.config.plugins.length).to.equal(numberOfPlugins + 1); client.submitException(error, (context: EventPluginContext) => { if (!context.cancelled) { - expect(client.config.lastReferenceIdManager.getLast()).not.toBe(null); + expect(client.config.lastReferenceIdManager.getLast()).not.to.be.null; } else { - expect(client.config.lastReferenceIdManager.getLast()).toBe(null); + expect(client.config.lastReferenceIdManager.getLast()).to.be.null; } done(); + done = () => { }; }); - }, 5000); + }); it('should accept null source', () => { let client = new ExceptionlessClient('LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', 'http://localhost:50000'); let builder = client.createLog(null, 'Unit Test message', 'Trace'); - expect(builder.target.source).toBeUndefined(); - expect(builder.target.message).toBe('Unit Test message'); - expect(builder.target.data['@level']).toBe('Trace'); + expect(builder.target.source).to.be.undefined; + expect(builder.target.message).to.equal('Unit Test message'); + expect(builder.target.data['@level']).to.equal('Trace'); }); it('should accept source and message', () => { let client = new ExceptionlessClient('LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', 'http://localhost:50000'); let builder = client.createLog('ExceptionlessClient', 'Unit Test message'); - expect(builder.target.source).toBe('ExceptionlessClient'); - expect(builder.target.message).toBe('Unit Test message'); - expect(builder.target.data).toBeUndefined(); + expect(builder.target.source).to.equal('ExceptionlessClient'); + expect(builder.target.message).to.equal('Unit Test message'); + expect(builder.target.data).to.be.undefined; }); it('should accept source and message', () => { let client = new ExceptionlessClient('LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', 'http://localhost:50000'); let builder = client.createLog('Unit Test message'); - expect(builder.target.source).toBeUndefined(); - expect(builder.target.message).toBe('Unit Test message'); - expect(builder.target.data).toBeUndefined(); + expect(builder.target.source).to.be.undefined; + expect(builder.target.message).to.equal('Unit Test message'); + expect(builder.target.data).to.be.undefined; }); }); diff --git a/src/Utils-spec.ts b/src/Utils-spec.ts index cb49048d..378d2018 100644 --- a/src/Utils-spec.ts +++ b/src/Utils-spec.ts @@ -1,17 +1,18 @@ import { Utils } from './Utils'; +import { expect } from 'chai'; describe('Utils', () => { it('should add range', () => { let target: string[]; - expect(Utils.addRange(target)).toEqual([]); - expect(target).toBeUndefined(); + expect(Utils.addRange(target)).to.eql([]); + expect(target).to.be.undefined; - expect(Utils.addRange(target, '1', '2')).toEqual(['1', '2']); - expect(Utils.addRange(target, '1', '2')).toEqual(['1', '2']); + expect(Utils.addRange(target, '1', '2')).to.eql(['1', '2']); + expect(Utils.addRange(target, '1', '2')).to.eql(['1', '2']); target = ['3']; - expect(Utils.addRange(target, '1', '2')).toEqual(['3', '1', '2']); - expect(target).toEqual(['3', '1', '2']); + expect(Utils.addRange(target, '1', '2')).to.eql(['3', '1', '2']); + expect(target).to.eql(['3', '1', '2']); }); describe('stringify', () => { @@ -61,53 +62,53 @@ describe('Utils', () => { 'tags': [] }; - expect(Utils.stringify(error)).toBe(JSON.stringify(error)); - expect(Utils.stringify([error, error])).toBe(JSON.stringify([error, error])); + expect(Utils.stringify(error)).to.equal(JSON.stringify(error)); + expect(Utils.stringify([error, error])).to.equal(JSON.stringify([error, error])); }); it('circular reference', () => { let afoo: any = { a: 'foo' }; afoo.b = afoo; - expect(Utils.stringify(afoo)).toBe('{"a":"foo"}'); - expect(Utils.stringify([{ one: afoo, two: afoo }])).toBe('[{"one":{"a":"foo"}}]'); + expect(Utils.stringify(afoo)).to.equal('{"a":"foo"}'); + expect(Utils.stringify([{ one: afoo, two: afoo }])).to.equal('[{"one":{"a":"foo"}}]'); }); it('should behave like JSON.stringify', () => { - expect(Utils.stringify(user)).toBe(JSON.stringify(user)); + expect(Utils.stringify(user)).to.equal(JSON.stringify(user)); }); describe('with exclude pattern', () => { it('pAssword', () => { - expect(Utils.stringify(user, ['pAssword'])).toBe('{"id":1,"name":"Blake","passwordResetToken":"a reset token","myPasswordValue":"123456","myPassword":"123456","customValue":"Password","value":{}}'); + expect(Utils.stringify(user, ['pAssword'])).to.equal('{"id":1,"name":"Blake","passwordResetToken":"a reset token","myPasswordValue":"123456","myPassword":"123456","customValue":"Password","value":{}}'); }); it('*password', () => { - expect(Utils.stringify(user, ['*password'])).toBe('{"id":1,"name":"Blake","passwordResetToken":"a reset token","myPasswordValue":"123456","customValue":"Password","value":{}}'); + expect(Utils.stringify(user, ['*password'])).to.equal('{"id":1,"name":"Blake","passwordResetToken":"a reset token","myPasswordValue":"123456","customValue":"Password","value":{}}'); }); it('password*', () => { - expect(Utils.stringify(user, ['password*'])).toBe('{"id":1,"name":"Blake","myPasswordValue":"123456","myPassword":"123456","customValue":"Password","value":{}}'); + expect(Utils.stringify(user, ['password*'])).to.equal('{"id":1,"name":"Blake","myPasswordValue":"123456","myPassword":"123456","customValue":"Password","value":{}}'); }); it('*password*', () => { - expect(Utils.stringify(user, ['*password*'])).toBe('{"id":1,"name":"Blake","customValue":"Password","value":{}}'); + expect(Utils.stringify(user, ['*password*'])).to.equal('{"id":1,"name":"Blake","customValue":"Password","value":{}}'); }); it('*Address', () => { let event = { type: 'usage', source: 'about' }; - expect(Utils.stringify(event, ['*Address'])).toBe(JSON.stringify(event)); + expect(Utils.stringify(event, ['*Address'])).to.equal(JSON.stringify(event)); }); }); }); it('should parse version from url', () => { - expect(Utils.parseVersion('https://code.jquery.com/jquery-2.1.3.js')).toBe('2.1.3'); - expect(Utils.parseVersion('//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css')).toBe('3.3.4'); - expect(Utils.parseVersion('https://cdnjs.cloudflare.com/ajax/libs/1140/2.0/1140.css')).toBe('2.0'); - expect(Utils.parseVersion('https://cdnjs.cloudflare.com/ajax/libs/Base64/0.3.0/base64.min.js')).toBe('0.3.0'); - expect(Utils.parseVersion('https://cdnjs.cloudflare.com/ajax/libs/angular-google-maps/2.1.0-X.10/angular-google-maps.min.js')).toBe('2.1.0-X.10'); - expect(Utils.parseVersion('https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/2.1.8-M1/swagger-ui.min.js')).toBe('2.1.8-M1'); - expect(Utils.parseVersion('https://cdnjs.cloudflare.com/BLAH/BLAH.min.js')).toBe(null); + expect(Utils.parseVersion('https://code.jquery.com/jquery-2.1.3.js')).to.equal('2.1.3'); + expect(Utils.parseVersion('//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css')).to.equal('3.3.4'); + expect(Utils.parseVersion('https://cdnjs.cloudflare.com/ajax/libs/1140/2.0/1140.css')).to.equal('2.0'); + expect(Utils.parseVersion('https://cdnjs.cloudflare.com/ajax/libs/Base64/0.3.0/base64.min.js')).to.equal('0.3.0'); + expect(Utils.parseVersion('https://cdnjs.cloudflare.com/ajax/libs/angular-google-maps/2.1.0-X.10/angular-google-maps.min.js')).to.equal('2.1.0-X.10'); + expect(Utils.parseVersion('https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/2.1.8-M1/swagger-ui.min.js')).to.equal('2.1.8-M1'); + expect(Utils.parseVersion('https://cdnjs.cloudflare.com/BLAH/BLAH.min.js')).to.equal(null); }); }); diff --git a/src/configuration/Configuration-spec.ts b/src/configuration/Configuration-spec.ts index a9add8e1..adb16897 100644 --- a/src/configuration/Configuration-spec.ts +++ b/src/configuration/Configuration-spec.ts @@ -1,53 +1,54 @@ import { Configuration } from './Configuration'; import { EventPluginContext } from '../plugins/EventPluginContext'; +import { expect } from 'chai'; describe('Configuration', () => { it('should override configuration defaults', () => { let config = new Configuration(); - expect(config.apiKey).toBe(null); + expect(config.apiKey).to.equal(null); config.apiKey = 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw'; - expect(config.apiKey).toBe('LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw'); + expect(config.apiKey).to.equal('LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw'); Configuration.defaults.apiKey = 'test'; config = new Configuration(); - expect(config.apiKey).toBe('test'); + expect(config.apiKey).to.equal('test'); config = new Configuration({ apiKey: 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl: 'http://localhost:50000' }); - expect(config.apiKey).toBe('LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw'); + expect(config.apiKey).to.equal('LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw'); config = new Configuration({ apiKey: null }); - expect(config.apiKey).toBe('test'); + expect(config.apiKey).to.equal('test'); }); it('should not add duplicate plugin', () => { let config = new Configuration({ apiKey: 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl: 'http://localhost:50000' }); - expect(config.plugins).not.toBe(null); + expect(config.plugins).not.to.equal(null); while (config.plugins.length > 0) { config.removePlugin(config.plugins[0]); } config.addPlugin('test', 20, (context: EventPluginContext) => { }); config.addPlugin('test', 20, (context: EventPluginContext) => { }); - expect(config.plugins.length).toBe(1); + expect(config.plugins.length).to.equal(1); }); it('should generate plugin name and priority', () => { let config = new Configuration({ apiKey: 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl: 'http://localhost:50000' }); - expect(config.plugins).not.toBe(null); + expect(config.plugins).not.to.equal(null); while (config.plugins.length > 0) { config.removePlugin(config.plugins[0]); } config.addPlugin(null, null, (context: EventPluginContext) => { }); - expect(config.plugins.length).toBe(1); - expect(config.plugins[0].name).not.toBe(null); - expect(config.plugins[0].priority).toBe(0); + expect(config.plugins.length).to.equal(1); + expect(config.plugins[0].name).not.to.equal(null); + expect(config.plugins[0].priority).to.equal(0); }); it('should sort plugins by priority', () => { let config = new Configuration({ apiKey: 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl: 'http://localhost:50000' }); - expect(config.plugins).not.toBe(null); + expect(config.plugins).not.to.equal(null); while (config.plugins.length > 0) { config.removePlugin(config.plugins[0]); } @@ -55,9 +56,9 @@ describe('Configuration', () => { config.addPlugin('3', 3, (context: EventPluginContext) => { }); config.addPlugin('1', 1, (context: EventPluginContext) => { }); config.addPlugin('2', 2, (context: EventPluginContext) => { }); - expect(config.plugins.length).toBe(3); - expect(config.plugins[0].priority).toBe(1); - expect(config.plugins[1].priority).toBe(2); - expect(config.plugins[2].priority).toBe(3); + expect(config.plugins.length).to.equal(3); + expect(config.plugins[0].priority).to.equal(1); + expect(config.plugins[1].priority).to.equal(2); + expect(config.plugins[2].priority).to.equal(3); }); }); diff --git a/src/configuration/SettingsManager-spec.ts b/src/configuration/SettingsManager-spec.ts index 838c3cce..7f74bdff 100644 --- a/src/configuration/SettingsManager-spec.ts +++ b/src/configuration/SettingsManager-spec.ts @@ -1,14 +1,16 @@ import { Configuration } from './Configuration'; import { SettingsManager } from './SettingsManager'; +import { expect } from 'chai'; describe('SettingsManager', () => { it('should call changed handler', (done) => { SettingsManager.onChanged((configuration: Configuration) => { - expect(configuration.settings).toBeDefined(); + expect(configuration.settings).not.to.be.undefined; done(); + done = () => { }; }); let config = new Configuration('LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw'); SettingsManager.applySavedServerSettings(config); - }, 250); + }); }); diff --git a/src/plugins/EventPluginManager-spec.ts b/src/plugins/EventPluginManager-spec.ts index 8244b1e8..92abe373 100644 --- a/src/plugins/EventPluginManager-spec.ts +++ b/src/plugins/EventPluginManager-spec.ts @@ -2,15 +2,16 @@ import { ContextData } from './ContextData'; import { ExceptionlessClient } from '../ExceptionlessClient'; import { EventPluginManager } from './EventPluginManager'; import { EventPluginContext } from './EventPluginContext'; +import { expect } from 'chai'; describe('EventPluginManager', () => { it('should add items to the event.', (done) => { let client = new ExceptionlessClient({ apiKey: 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl: 'http://localhost:50000' }); let context = new EventPluginContext(client, {}, new ContextData()); - expect(context.event.source).toBeUndefined(); - expect(context.event.geo).toBeUndefined(); + expect(context.event.source).to.be.undefined; + expect(context.event.geo).to.be.undefined; - expect(client.config.plugins).not.toBe(null); + expect(client.config.plugins).not.to.equal(null); while (client.config.plugins.length > 0) { client.config.removePlugin(client.config.plugins[0]); } @@ -34,19 +35,19 @@ describe('EventPluginManager', () => { }); EventPluginManager.run(context, (ctx?: EventPluginContext) => { - expect(ctx.cancelled).toBeUndefined(); - expect(ctx.event.source).toBe('plugin 1'); - expect(ctx.event.geo).toBe('43.5775,88.4472'); + expect(ctx.cancelled).to.be.undefined; + expect(ctx.event.source).to.equal('plugin 1'); + expect(ctx.event.geo).to.equal('43.5775,88.4472'); done(); }); - }, 5000); + }); it('setting cancel should stop plugin execution.', (done) => { let client = new ExceptionlessClient({ apiKey: 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl: 'http://localhost:50000' }); let context = new EventPluginContext(client, {}, new ContextData()); - expect(client.config.plugins).not.toBe(null); + expect(client.config.plugins).not.to.equal(null); while (client.config.plugins.length > 0) { client.config.removePlugin(client.config.plugins[0]); } @@ -61,20 +62,21 @@ describe('EventPluginManager', () => { client.config.addPlugin('2', 2, () => { // Fail this test as this plugin should not be called. - expect(false).toBe(true); + expect(false).to.equal(true); }); EventPluginManager.run(context, (ctx?: EventPluginContext) => { - expect(ctx.cancelled).toBe(true); + expect(ctx.cancelled).to.equal(true); done(); + done = () => { }; }); - }, 5000); + }); it('throwing error should stop plugin execution.', (done) => { let client = new ExceptionlessClient({ apiKey: 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl: 'http://localhost:50000' }); let context = new EventPluginContext(client, {}, new ContextData()); - expect(client.config.plugins).not.toBe(null); + expect(client.config.plugins).not.to.equal(null); while (client.config.plugins.length > 0) { client.config.removePlugin(client.config.plugins[0]); } @@ -85,20 +87,20 @@ describe('EventPluginManager', () => { client.config.addPlugin('2', 2, () => { // Fail this test as this plugin should not be called. - expect(false).toBe(true); + expect(false).to.equal(true); }); EventPluginManager.run(context, (ctx?: EventPluginContext) => { - expect(ctx.cancelled).toBe(true); + expect(ctx.cancelled).to.equal(true); done(); }); - }, 5000); + }); it('throwing async error should stop plugin execution.', (done) => { let client = new ExceptionlessClient({ apiKey: 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl: 'http://localhost:50000' }); let context = new EventPluginContext(client, {}, new ContextData()); - expect(client.config.plugins).not.toBe(null); + expect(client.config.plugins).not.to.equal(null); while (client.config.plugins.length > 0) { client.config.removePlugin(client.config.plugins[0]); } @@ -120,20 +122,20 @@ describe('EventPluginManager', () => { client.config.addPlugin('2', 2, () => { // Fail this test as this plugin should not be called. - expect(false).toBe(true); + expect(false).to.equal(true); }); EventPluginManager.run(context, (ctx?: EventPluginContext) => { - expect(ctx.cancelled).toBe(true); + expect(ctx.cancelled).to.equal(true); done(); }); - }, 5000); + }); it('should cancel via timeout.', (done) => { let client = new ExceptionlessClient({ apiKey: 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl: 'http://localhost:50000' }); let context = new EventPluginContext(client, {}, new ContextData()); - expect(client.config.plugins).not.toBe(null); + expect(client.config.plugins).not.to.equal(null); while (client.config.plugins.length > 0) { client.config.removePlugin(client.config.plugins[0]); } @@ -144,20 +146,20 @@ describe('EventPluginManager', () => { client.config.addPlugin('2', 2, () => { // Fail this test as this plugin should not be called. - expect(false).toBe(true); + expect(false).to.equal(true); }); EventPluginManager.run(context, (ctx?: EventPluginContext) => { // Fail this test as this callback should not be called. - expect(false).toBe(true); + expect(false).to.equal(true); }); - }, 500); + }); it('should ensure config plugins are not wrapped.', () => { let client = new ExceptionlessClient({ apiKey: 'LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw', serverUrl: 'http://localhost:50000' }); let context = new EventPluginContext(client, {}, new ContextData()); - expect(client.config.plugins).not.toBe(null); + expect(client.config.plugins).not.to.equal(null); while (client.config.plugins.length > 0) { client.config.removePlugin(client.config.plugins[0]); } @@ -168,16 +170,16 @@ describe('EventPluginManager', () => { } }); - expect(client.config.plugins[0].name).toBe('1'); - expect(client.config.plugins.length).toBe(1); + expect(client.config.plugins[0].name).to.equal('1'); + expect(client.config.plugins.length).to.equal(1); EventPluginManager.run(context, (ctx?: EventPluginContext) => { - expect(client.config.plugins[0].name).toBe('1'); + expect(client.config.plugins[0].name).to.equal('1'); }); - expect(client.config.plugins.length).toBe(1); + expect(client.config.plugins.length).to.equal(1); EventPluginManager.run(context, (ctx?: EventPluginContext) => { - expect(client.config.plugins[0].name).toBe('1'); + expect(client.config.plugins[0].name).to.equal('1'); }); - expect(client.config.plugins.length).toBe(1); + expect(client.config.plugins.length).to.equal(1); }); }); diff --git a/src/plugins/default/DuplicateCheckerPlugin-spec.ts b/src/plugins/default/DuplicateCheckerPlugin-spec.ts index b9c354f9..1a2f3608 100644 --- a/src/plugins/default/DuplicateCheckerPlugin-spec.ts +++ b/src/plugins/default/DuplicateCheckerPlugin-spec.ts @@ -3,6 +3,7 @@ import { EventPluginContext } from '../EventPluginContext'; import { DuplicateCheckerPlugin } from './DuplicateCheckerPlugin'; import { ErrorPlugin } from './ErrorPlugin'; import { createFixture } from './EventPluginTestFixture'; +import { expect } from 'chai'; describe('DuplicateCheckerPlugin', () => { @@ -38,14 +39,14 @@ describe('DuplicateCheckerPlugin', () => { }]); run(exception); let contextOfSecondRun = run(exception); - expect(contextOfSecondRun.cancelled).toBeTruthy(); + expect(contextOfSecondRun.cancelled).to.be.true; }); it('shouldn\'t ignore error without stack', () => { let exception = createException(); run(exception); let contextOfSecondRun = run(exception); - expect(contextOfSecondRun.cancelled).toBeFalsy(); + expect(contextOfSecondRun.cancelled).not.to.be.true; }); it('shouldn\'t ignore different stack within window', () => { @@ -57,7 +58,7 @@ describe('DuplicateCheckerPlugin', () => { name: 'methodB' }]); let contextOfSecondRun = run(exception2); - expect(contextOfSecondRun.cancelled).toBeFalsy(); + expect(contextOfSecondRun.cancelled).not.to.be.true; }); it('shouldn\'t ignore duplicate after window', () => { @@ -68,7 +69,7 @@ describe('DuplicateCheckerPlugin', () => { now = 3000; let contextOfSecondRun = run(exception); - expect(contextOfSecondRun.cancelled).toBeFalsy(); + expect(contextOfSecondRun.cancelled).not.to.be.true; }); }); diff --git a/src/plugins/default/ErrorPlugin-spec.ts b/src/plugins/default/ErrorPlugin-spec.ts index 0aa61ac6..ee19d76d 100644 --- a/src/plugins/default/ErrorPlugin-spec.ts +++ b/src/plugins/default/ErrorPlugin-spec.ts @@ -5,6 +5,7 @@ import { IEvent } from '../../models/IEvent'; import { ErrorPlugin } from './ErrorPlugin'; import { CapturedExceptions } from './ErrorPlugin-spec-exceptions'; import { createFixture } from './EventPluginTestFixture'; +import { expect } from 'chai'; function BaseTestError() { this.name = 'NotImplementedError'; @@ -47,7 +48,7 @@ describe('ErrorPlugin', () => { contextData.setException(exception); target.run(context); let additionalData = getAdditionalData(event); - expect(additionalData).toBeNull(); + expect(additionalData).to.be.null; }); }); @@ -58,29 +59,29 @@ describe('ErrorPlugin', () => { }; processError(error); let additionalData = getAdditionalData(event); - expect(additionalData).not.toBeNull(); - expect(additionalData.someProperty).toBe('Test'); + expect(additionalData).not.to.be.null; + expect(additionalData.someProperty).to.equal('Test'); }); it('should support custom exception types', () => { processError(new BaseTestError()); let additionalData = getAdditionalData(event); - expect(additionalData).not.toBeNull(); - expect(additionalData.someProperty).toBe('Test'); + expect(additionalData).not.to.be.null; + expect(additionalData.someProperty).to.equal('Test'); }); it('should support inherited properties', () => { processError(new DerivedTestError()); let additionalData = getAdditionalData(event); - expect(additionalData).not.toBeNull(); - expect(additionalData.someProperty).toBe('Test'); - expect(additionalData.someOtherProperty).toBe('Test2'); + expect(additionalData).not.to.be.null; + expect(additionalData.someProperty).to.equal('Test'); + expect(additionalData.someOtherProperty).to.equal('Test2'); }); it('shouldn\'t set empty additional data', () => { processError({}); let additionalData = getAdditionalData(event); - expect(additionalData).toBeNull(); + expect(additionalData).to.be.null; }); it('should ignore functions', () => { @@ -91,7 +92,7 @@ describe('ErrorPlugin', () => { target.run(context); let additionalData = getAdditionalData(event); - expect(additionalData).toBeNull(); + expect(additionalData).to.be.null; }); }); }); diff --git a/src/queue/DefaultEventQueue-spec.ts b/src/queue/DefaultEventQueue-spec.ts index a271a937..6ec9d212 100644 --- a/src/queue/DefaultEventQueue-spec.ts +++ b/src/queue/DefaultEventQueue-spec.ts @@ -1,5 +1,6 @@ import { Configuration } from '../configuration/Configuration'; import { IEvent } from '../models/IEvent'; +import { expect } from 'chai'; describe('DefaultEventQueue', () => { function getConfiguration(): Configuration { @@ -8,7 +9,7 @@ describe('DefaultEventQueue', () => { serverUrl: 'http://localhost:50000' }); - expect(config.storage.getList().length).toBe(0); + expect(config.storage.getList().length).to.equal(0); return config; } @@ -16,20 +17,20 @@ describe('DefaultEventQueue', () => { let config: Configuration = getConfiguration(); let event: IEvent = { type: 'log', reference_id: '123454321' }; config.queue.enqueue(event); - expect(config.storage.getList().length).toBe(1); + expect(config.storage.getList().length).to.equal(1); }); it('should process queue', () => { let config: Configuration = getConfiguration(); let event: IEvent = { type: 'log', reference_id: '123454321' }; config.queue.enqueue(event); - expect(config.storage.getList().length).toBe(1); + expect(config.storage.getList().length).to.equal(1); config.queue.process(); if (!(config.queue)._suspendProcessingUntil) { - expect(config.storage.getList().length).toBe(0); + expect(config.storage.getList().length).to.equal(0); } else { - expect(config.storage.getList().length).toBe(1); + expect(config.storage.getList().length).to.equal(1); } }); @@ -39,7 +40,7 @@ describe('DefaultEventQueue', () => { let event: IEvent = { type: 'log', reference_id: '123454321' }; config.queue.enqueue(event); - expect(config.storage.getList().length).toBe(0); + expect(config.storage.getList().length).to.equal(0); }); it('should suspend processing', (done) => { @@ -48,16 +49,16 @@ describe('DefaultEventQueue', () => { let event: IEvent = { type: 'log', reference_id: '123454321' }; config.queue.enqueue(event); - expect(config.storage.getList().length).toBe(1); + expect(config.storage.getList().length).to.equal(1); setTimeout(() => { if (!(config.queue)._suspendProcessingUntil) { - expect(config.storage.getList().length).toBe(0); + expect(config.storage.getList().length).to.equal(0); } else { - expect(config.storage.getList().length).toBe(1); + expect(config.storage.getList().length).to.equal(1); } done(); - }, 10000); - }, 21000); + }); + }); }); diff --git a/src/storage/InMemoryStorage-spec.ts b/src/storage/InMemoryStorage-spec.ts index cd9eca5a..92e93d6a 100644 --- a/src/storage/InMemoryStorage-spec.ts +++ b/src/storage/InMemoryStorage-spec.ts @@ -1,6 +1,7 @@ import { IEvent } from '../models/IEvent'; import { InMemoryStorage } from './InMemoryStorage'; import { IStorageItem } from './IStorageItem'; +import { expect } from 'chai'; describe('InMemoryStorage', () => { it('should save events', () => { @@ -8,31 +9,31 @@ describe('InMemoryStorage', () => { let key = 'ex-q-'; let event1: IEvent = { type: 'log', reference_id: key + '123454321' }; let event2: IEvent = { type: 'log', reference_id: key + '098765432' }; - expect(storage.getList().length).toBe(0); + expect(storage.getList().length).to.equal(0); storage.save(event1.reference_id, event1); - expect(storage.getList().length).toBe(1); - expect(storage.getList(key).length).toBe(1); + expect(storage.getList().length).to.equal(1); + expect(storage.getList(key).length).to.equal(1); storage.save(event2.reference_id, event2); - expect(storage.getList().length).toBe(2); - expect(storage.getList(key).length).toBe(2); - expect(storage.getList(key + '1').length).toBe(1); + expect(storage.getList().length).to.equal(2); + expect(storage.getList(key).length).to.equal(2); + expect(storage.getList(key + '1').length).to.equal(1); }); it('should save once', () => { let storage = new InMemoryStorage(); storage.save('one', 1); - expect(storage.getList().length).toBe(1); + expect(storage.getList().length).to.equal(1); storage.save('one', 1); - expect(storage.getList().length).toBe(1); + expect(storage.getList().length).to.equal(1); }); it('should get by key', () => { let storage = new InMemoryStorage(); storage.save('ex-server-settings.json-version', 1); storage.save('ex-server-settings.json', { exist: true }); - expect(storage.getList().length).toBe(2); - expect(storage.get('ex-server-settings.json-version')).toBe(1); - expect(storage.get('ex-server-settings.json')).toEqual({ exist: true }); + expect(storage.getList().length).to.equal(2); + expect(storage.get('ex-server-settings.json-version')).to.equal(1); + expect(storage.get('ex-server-settings.json')).to.eql({ exist: true }); }); it('should get saved events', () => { @@ -44,7 +45,7 @@ describe('InMemoryStorage', () => { let event4: IEvent = { type: 'log', reference_id: key + '14' }; let event5: IEvent = { type: 'log', reference_id: key + '15' }; let event6: IEvent = { type: 'log', reference_id: key + '16' }; - expect(storage.getList().length).toBe(0); + expect(storage.getList().length).to.equal(0); storage.save(event1.reference_id, event1); storage.save(event2.reference_id, event2); @@ -52,31 +53,31 @@ describe('InMemoryStorage', () => { storage.save(event4.reference_id, event4); storage.save(event5.reference_id, event5); storage.save(event6.reference_id, event6); - expect(storage.getList().length).toBe(6); + expect(storage.getList().length).to.equal(6); let ev = storage.get(event1.reference_id); - expect(ev).toEqual(event1); - expect(ev).toEqual(storage.getList(event1.reference_id, 1)[0].value); - expect(storage.getList().length).toBe(6); + expect(ev).to.equal(event1); + expect(ev).to.equal(storage.getList(event1.reference_id, 1)[0].value); + expect(storage.getList().length).to.equal(6); storage.remove(event1.reference_id); - expect(storage.get(event1.reference_id)).toBe(null); - expect(storage.getList().length).toBe(5); + expect(storage.get(event1.reference_id)).to.equal(null); + expect(storage.getList().length).to.equal(5); ev = storage.getList(event2.reference_id, 1)[0].value; - expect(ev).toEqual(event2); + expect(ev).to.equal(event2); storage.remove(event2.reference_id); - expect(storage.getList().length).toBe(4); + expect(storage.getList().length).to.equal(4); let events = storage.getList(key, 2); - expect(events.length).toBe(2); - expect(events[0].value).not.toEqual(events[1].value); + expect(events.length).to.equal(2); + expect(events[0].value).not.to.equal(events[1].value); storage.remove(events[0].path); storage.remove(events[1].path); - expect(storage.getList().length).toBe(2); + expect(storage.getList().length).to.equal(2); events = storage.getList(key); - expect(events.length).toBe(2); - expect(events[0].value).not.toEqual(events[1].value); + expect(events.length).to.equal(2); + expect(events[0].value).not.to.equal(events[1].value); }); it('should clear all events', () => { @@ -88,7 +89,7 @@ describe('InMemoryStorage', () => { let event4: IEvent = { type: 'log', reference_id: key + '14' }; let event5: IEvent = { type: 'log', reference_id: key + '15' }; let event6: IEvent = { type: 'log', reference_id: key + '16' }; - expect(storage.getList().length).toBe(0); + expect(storage.getList().length).to.equal(0); storage.save(event1.reference_id, event1); storage.save(event2.reference_id, event2); @@ -96,17 +97,17 @@ describe('InMemoryStorage', () => { storage.save(event4.reference_id, event4); storage.save(event5.reference_id, event5); storage.save(event6.reference_id, event6); - expect(storage.getList().length).toBe(6); + expect(storage.getList().length).to.equal(6); storage.remove(event1.reference_id); - expect(storage.getList().length).toBe(5); + expect(storage.getList().length).to.equal(5); let events = storage.getList(); for (let index = 0; index < events.length; index++) { storage.remove(events[index].path); } - expect(storage.getList().length).toBe(0); + expect(storage.getList().length).to.equal(0); }); it('should get with limit', () => { @@ -115,9 +116,9 @@ describe('InMemoryStorage', () => { storage.save('ex-q-' + index, { type: 'log', reference_id: index.toString() }); } - expect(storage.getList().length).toBe(250); - expect(storage.getList(null).length).toBe(250); - expect(storage.getList(null, 1).length).toBe(1); + expect(storage.getList().length).to.equal(250); + expect(storage.getList(null).length).to.equal(250); + expect(storage.getList(null, 1).length).to.equal(1); }); it('should get the oldest events', () => { @@ -134,15 +135,15 @@ describe('InMemoryStorage', () => { reference_id: index.toString() }); - expect(storage.getList().length).toBe(index + 1); + expect(storage.getList().length).to.equal(index + 1); } let offset: number = 0; let events: IStorageItem[] = storage.getList('ex-q-', 2); while (events && events.length > 0) { - expect(2).toBe(events.length); + expect(2).to.equal(events.length); for (let ei = 0; ei < 2; ei++) { - expect(getDate(DATE, offset++)).toEqual(events[ei].value.date); + expect(getDate(DATE, offset++)).to.eql(events[ei].value.date); storage.remove(events[ei].path); } @@ -157,12 +158,12 @@ describe('InMemoryStorage', () => { } let events: IStorageItem[] = storage.getList(); - expect(events.length).toBe(5); - expect(events[0].path).toBe('ex-q-0'); + expect(events.length).to.equal(5); + expect(events[0].path).to.equal('ex-q-0'); storage.save('ex-q-6', { type: 'log', reference_id: '6' }); events = storage.getList(); - expect(events.length).toBe(5); - expect(events[0].path).toBe('ex-q-1'); + expect(events.length).to.equal(5); + expect(events[0].path).to.equal('ex-q-1'); }); }); diff --git a/src/submission/DefaultSubmissionClient-spec.ts b/src/submission/DefaultSubmissionClient-spec.ts index 78712232..47f82df6 100644 --- a/src/submission/DefaultSubmissionClient-spec.ts +++ b/src/submission/DefaultSubmissionClient-spec.ts @@ -6,6 +6,7 @@ import { ISubmissionAdapter } from './ISubmissionAdapter'; import { DefaultSubmissionClient } from './DefaultSubmissionClient'; import { SubmissionCallback } from './SubmissionCallback'; import { SubmissionRequest } from './SubmissionRequest'; +import { expect } from 'chai'; class TestAdapter implements ISubmissionAdapter { private request; @@ -44,7 +45,7 @@ class TestAdapter implements ISubmissionAdapter { public done() { if (!this.request) { - fail('sendRequest hasn\'t been called.'); + expect.fail('sendRequest hasn\'t been called.'); return; } @@ -70,8 +71,8 @@ describe('DefaultSubmissionClient', () => { }); adapter = new TestAdapter(r => { - expect(r.apiKey).toBe(apiKey); - expect(r.serverUrl).toBe(serverUrl); + expect(r.apiKey).to.equal(apiKey); + expect(r.serverUrl).to.equal(serverUrl); }); config.submissionAdapter = adapter; @@ -81,9 +82,9 @@ describe('DefaultSubmissionClient', () => { let events = [{ type: 'log', message: 'From js client', reference_id: '123454321' }]; adapter.withCheck(r => { - expect(r.data).toBe(JSON.stringify(events)); - expect(r.method).toBe('POST'); - expect(r.path).toBe('/api/v2/events'); + expect(r.data).to.equal(JSON.stringify(events)); + expect(r.method).to.equal('POST'); + expect(r.path).to.equal('/api/v2/events'); }); submissionClient.postEvents(events, config, () => done()); @@ -100,9 +101,9 @@ describe('DefaultSubmissionClient', () => { }]; adapter.withCheck(r => { - expect(r.data).toBe(JSON.stringify(events)); - expect(r.method).toBe('POST'); - expect(r.path).toBe('/api/v2/events'); + expect(r.data).to.equal(JSON.stringify(events)); + expect(r.method).to.equal('POST'); + expect(r.path).to.equal('/api/v2/events'); }); submissionClient.postEvents(events, config, () => done()); @@ -117,9 +118,9 @@ describe('DefaultSubmissionClient', () => { }; adapter.withCheck(r => { - expect(r.data).toBe(JSON.stringify(description)); - expect(r.method).toBe('POST'); - expect(r.path).toBe('/api/v2/events/by-ref/123454321/user-description'); + expect(r.data).to.equal(JSON.stringify(description)); + expect(r.method).to.equal('POST'); + expect(r.path).to.equal('/api/v2/events/by-ref/123454321/user-description'); }); submissionClient.postUserDescription('123454321', description, config, () => done()); @@ -132,10 +133,10 @@ describe('DefaultSubmissionClient', () => { adapter.withResponse(200, null, JSON.stringify({ version: 1 })); submissionClient.getSettings(config, response => { - expect(response.success).toBe(true); - expect(response.message).toBe(null); - expect(response.settings).not.toBe(null); - expect(response.settingsVersion).toBeGreaterThan(-1); + expect(response.success).to.be.true; + expect(response.message).to.be.null; + expect(response.settings).not.to.be.null; + expect(response.settingsVersion).to.be.greaterThan(-1); done(); }); diff --git a/src/tsconfig.test.json b/src/tsconfig.test.json index 1912d63a..341bc703 100644 --- a/src/tsconfig.test.json +++ b/src/tsconfig.test.json @@ -9,7 +9,8 @@ "bundles": { "exceptionless-spec": { "files": [ - "typings/jasmine/jasmine.d.ts", + "typings/mocha/mocha.d.ts", + "typings/chai/chai.d.ts", "typings/node/node.d.ts", "typings/stack-trace/stack-trace.d.ts", "typings/tracekit/tracekit.d.ts", diff --git a/tsd.json b/tsd.json index 7bcbeecf..ce6c23f2 100644 --- a/tsd.json +++ b/tsd.json @@ -5,9 +5,6 @@ "path": "src/typings", "bundle": "src/typings/tsd.d.ts", "installed": { - "jasmine/jasmine.d.ts": { - "commit": "68548c2daab053869f323d475419c693478bbb73" - }, "node/node.d.ts": { "commit": "68548c2daab053869f323d475419c693478bbb73" }, @@ -19,6 +16,15 @@ }, "stack-trace/stack-trace.d.ts": { "commit": "68548c2daab053869f323d475419c693478bbb73" + }, + "mocha/mocha.d.ts": { + "commit": "fa04c80f4a889613b96cb4f283848c61a9f64233" + }, + "assertion-error/assertion-error.d.ts": { + "commit": "fa04c80f4a889613b96cb4f283848c61a9f64233" + }, + "chai/chai.d.ts": { + "commit": "fa04c80f4a889613b96cb4f283848c61a9f64233" } } } From 78ad163f24a68434cb2340a7740a3ee6ddb079f1 Mon Sep 17 00:00:00 2001 From: Blake Niemyjski Date: Tue, 24 Nov 2015 13:16:22 -0600 Subject: [PATCH 2/6] Updated tracekit def --- src/typings/tracekit/tracekit.d.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/typings/tracekit/tracekit.d.ts b/src/typings/tracekit/tracekit.d.ts index 0e9403bd..1a80d7c3 100644 --- a/src/typings/tracekit/tracekit.d.ts +++ b/src/typings/tracekit/tracekit.d.ts @@ -2,7 +2,7 @@ declare module TraceKit { export interface StackFrame { url:string; func:string; - args:string|string[]; + args:string[]; line:number; column:number; context:string[]; @@ -68,6 +68,13 @@ declare module TraceKit { * @return {Object.} Stack trace information. */ ofCaller:(depth:string|number) => StackTrace; + + /** + * Retrieves source code from the source code cache. + * @param {string} url URL of source code. + * @return {Array.} Source contents. + */ + getSource:(url:string) => string[]; } export interface Report { From 04cd143a07fe5985813645a77b8c40452daaa627 Mon Sep 17 00:00:00 2001 From: Frank Ebersoll Date: Tue, 24 Nov 2015 20:35:34 +0100 Subject: [PATCH 3/6] Added .vscode --- .vscode/launch.json | 38 +++++++++++++++++++++++++++ .vscode/tasks.json | 64 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..ec1ff228 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,38 @@ +{ + "version": "0.1.0", + "configurations": [ + { + "name": "Launch", + "request": "launch", + "type": "node", + "program": "dist/exceptionless.node.js", + "runtimeArgs": [ + "--nolazy" + ], + "sourceMaps": true, + "outDir": "dist" + }, + { + "name": "Test", + "request": "launch", + "type": "node", + "program": "node_modules/.bin/_mocha", + "args": [ + "dist/temp/exceptionless-spec.js" + ], + "runtimeArgs": [ + "--nolazy" + ], + "sourceMaps": true, + "outDir": "dist" + }, + { + "name": "Attach", + "request": "attach", + "type": "node", + "port": 5858, + "sourceMaps": true, + "outDir": "dist" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..ca185b63 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,64 @@ +{ + "version": "0.1.0", + "command": "gulp", + "isShellCommand": true, + "args": [ + "--no-color" + ], + "tasks": [ + { + "taskName": "build", + "args": [], + "isBuildCommand": true, + "problemMatcher": { + // The problem is owned by the cpp language service. + "owner": "typescript", + // The file name for reported problems is relative to the opened folder. + "fileLocation": [ + "relative", + "${workspaceRoot}/src" + ], + // The actual pattern to match problems in the output. + "pattern": { + // The regular expression. Example to match: helloWorld.c:5:3: warning: implicit declaration of function ‘prinft’ [-Wimplicit-function-declaration] + "regexp": "\\[\\d\\d:\\d\\d:\\d\\d\\] \\[gulp-tslint\\] error \\([^)]+\\) ([^\\[]+)\\[(\\d+), (\\d+)\\]: (.+)$", + // The first match group matches the file name which is relative. + "file": 1, + // The second match group matches the line on which the problem occurred. + "line": 2, + // The third match group matches the column at which the problem occurred. + "column": 3, + // The fifth match group matches the message. + "message": 4 + } + } + }, + { + "taskName": "test", + "args": [], + "isBuildCommand": true, + "problemMatcher": { + // The problem is owned by the cpp language service. + "owner": "typescript", + // The file name for reported problems is relative to the opened folder. + "fileLocation": [ + "relative", + "${workspaceRoot}/src" + ], + // The actual pattern to match problems in the output. + "pattern": { + // The regular expression. Example to match: helloWorld.c:5:3: warning: implicit declaration of function ‘prinft’ [-Wimplicit-function-declaration] + "regexp": "\\[\\d\\d:\\d\\d:\\d\\d\\] \\[gulp-tslint\\] error \\([^)]+\\) ([^\\[]+)\\[(\\d+), (\\d+)\\]: (.+)$", + // The first match group matches the file name which is relative. + "file": 1, + // The second match group matches the line on which the problem occurred. + "line": 2, + // The third match group matches the column at which the problem occurred. + "column": 3, + // The fifth match group matches the message. + "message": 4 + } + } + } + ] +} \ No newline at end of file From ae1026af76bd5037c93a26d686c9c57caa62a1d6 Mon Sep 17 00:00:00 2001 From: Blake Niemyjski Date: Tue, 24 Nov 2015 13:55:33 -0600 Subject: [PATCH 4/6] Have test be the default launch task --- .vscode/launch.json | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index ec1ff228..0a5920cb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,17 +1,6 @@ { "version": "0.1.0", "configurations": [ - { - "name": "Launch", - "request": "launch", - "type": "node", - "program": "dist/exceptionless.node.js", - "runtimeArgs": [ - "--nolazy" - ], - "sourceMaps": true, - "outDir": "dist" - }, { "name": "Test", "request": "launch", From 0b4b331d75abdade43d4c0febed06296e25bcb70 Mon Sep 17 00:00:00 2001 From: Frank Ebersoll Date: Wed, 25 Nov 2015 00:42:53 +0100 Subject: [PATCH 5/6] Fix: Stringify didn't serialize inherited properties --- dist/exceptionless.js | 11 +++++++ dist/exceptionless.js.map | 2 +- dist/exceptionless.min.js | 4 +-- dist/exceptionless.min.js.map | 2 +- dist/exceptionless.node.js | 11 +++++++ dist/exceptionless.node.js.map | 2 +- src/Utils-spec.ts | 52 ++++++++++++++++++++++++++++++++-- src/Utils.ts | 15 ++++++++++ 8 files changed, 92 insertions(+), 7 deletions(-) diff --git a/dist/exceptionless.js b/dist/exceptionless.js index d34db277..26b1c536 100644 --- a/dist/exceptionless.js +++ b/dist/exceptionless.js @@ -1759,6 +1759,17 @@ var Utils = (function () { return value; }); } + if (({}).toString.call(data) === '[object Object]') { + var flattened = {}; + for (var prop in data) { + var value = data[prop]; + if (value === data) { + continue; + } + flattened[prop] = data[prop]; + } + return stringifyImpl(flattened, exclusions); + } if (({}).toString.call(data) === '[object Array]') { var result = []; for (var index = 0; index < data.length; index++) { diff --git a/dist/exceptionless.js.map b/dist/exceptionless.js.map index e5821b05..f71f230c 100644 --- a/dist/exceptionless.js.map +++ b/dist/exceptionless.js.map @@ -1 +1 @@ -{"version":3,"sources":["tracekit.js","/source/exceptionless.ts"],"names":["getDefaultsSettingsFromScriptTag","processUnhandledException"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC9oCA,8BAA8B,+BAA+B,CAAC,CAAA;AAE9D,mCAAmC,+BAA+B,CAAC,CAAA;AACnE,uCAAuC,mCAAmC,CAAC,CAAA;AAC3E,4CAA4C,wCAAwC,CAAC,CAAA;AACrF,yCAAyC,uCAAuC,CAAC,CAAA;AACjF,oCAAoC,uBAAuB,CAAC,CAAA;AAC5D,sBAAsB,SAAS,CAAC,CAAA;AAEhC;IACEA,EAAEA,CAACA,CAACA,CAACA,QAAQA,IAAIA,CAACA,QAAQA,CAACA,oBAAoBA,CAACA,CAACA,CAACA;QAChDA,MAAMA,CAACA,IAAIA,CAACA;IACdA,CAACA;IAEDA,IAAIA,OAAOA,GAAGA,QAAQA,CAACA,oBAAoBA,CAACA,QAAQA,CAACA,CAACA;IACtDA,GAAGA,CAACA,CAACA,GAAGA,CAACA,KAAKA,GAAGA,CAACA,EAAEA,KAAKA,GAAGA,OAAOA,CAACA,MAAMA,EAAEA,KAAKA,EAAEA,EAAEA,CAACA;QACpDA,EAAEA,CAACA,CAACA,OAAOA,CAACA,KAAKA,CAACA,CAACA,GAAGA,IAAIA,OAAOA,CAACA,KAAKA,CAACA,CAACA,GAAGA,CAACA,OAAOA,CAACA,gBAAgBA,CAACA,GAAGA,CAACA,CAACA,CAACA,CAACA,CAACA;YAC5EA,MAAMA,CAACA,aAAKA,CAACA,gBAAgBA,CAACA,OAAOA,CAACA,KAAKA,CAACA,CAACA,GAAGA,CAACA,KAAKA,CAACA,GAAGA,CAACA,CAACA,GAAGA,EAAEA,CAACA,CAACA;QACrEA,CAACA;IACHA,CAACA;IACDA,MAAMA,CAACA,IAAIA,CAACA;AACdA,CAACA;AAED,mCAAmC,UAA+B,EAAE,OAAa;IAC/EC,IAAIA,OAAOA,GAAGA,yCAAmBA,CAACA,OAAOA,CAACA,wBAAwBA,CAACA,IAAIA,KAAKA,CAACA,UAAUA,CAACA,OAAOA,IAAIA,CAACA,OAAOA,IAAIA,EAAEA,CAACA,CAACA,MAAMA,IAAIA,cAAcA,CAACA,EAAEA,SAASA,CAACA,CAACA;IACzJA,OAAOA,CAACA,iBAAiBA,CAACA,wBAAwBA,CAACA,GAAGA,UAAUA,CAACA;IACjEA,OAAOA,CAACA,MAAMA,EAAEA,CAACA;AACnBA,CAACA;AAmBD,IAAI,QAAQ,GAAG,6BAAa,CAAC,QAAQ,CAAC;AACtC,IAAI,QAAQ,GAAG,gCAAgC,EAAE,CAAC;AAClD,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC;AAC1C,CAAC;AAED,QAAQ,CAAC,WAAW,GAAG,IAAI,uCAAkB,EAAE,CAAC;AAChD,QAAQ,CAAC,eAAe,GAAG,IAAI,+CAAsB,EAAE,CAAC;AACxD,QAAQ,CAAC,oBAAoB,GAAG,IAAI,yDAA2B,EAAE,CAAC;AAClE,QAAQ,CAAC,iBAAiB,GAAG,IAAI,mDAAwB,EAAE,CAAC;AAE5D,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;AACrD,QAAQ,CAAC,6BAA6B,EAAE,CAAC;AAUnC,KAAM,CAAC,eAAe,GAAG,QAAQ,CAAC","file":"exceptionless.js","sourcesContent":["/*\n TraceKit - Cross browser stack traces - github.com/csnover/TraceKit\n MIT license\n*/\n\n(function(window, undefined) {\nif (!window) {\n return;\n}\n\nvar TraceKit = {};\nvar _oldTraceKit = window.TraceKit;\n\n// global reference to slice\nvar _slice = [].slice;\nvar UNKNOWN_FUNCTION = '?';\n\n\n/**\n * _has, a better form of hasOwnProperty\n * Example: _has(MainHostObject, property) === true/false\n *\n * @param {Object} object to check property\n * @param {string} key to check\n */\nfunction _has(object, key) {\n return Object.prototype.hasOwnProperty.call(object, key);\n}\n\nfunction _isUndefined(what) {\n return typeof what === 'undefined';\n}\n\n/**\n * TraceKit.noConflict: Export TraceKit out to another variable\n * Example: var TK = TraceKit.noConflict()\n */\nTraceKit.noConflict = function noConflict() {\n window.TraceKit = _oldTraceKit;\n return TraceKit;\n};\n\n/**\n * TraceKit.wrap: Wrap any function in a TraceKit reporter\n * Example: func = TraceKit.wrap(func);\n *\n * @param {Function} func Function to be wrapped\n * @return {Function} The wrapped func\n */\nTraceKit.wrap = function traceKitWrapper(func) {\n function wrapped() {\n try {\n return func.apply(this, arguments);\n } catch (e) {\n TraceKit.report(e);\n throw e;\n }\n }\n return wrapped;\n};\n\n/**\n * TraceKit.report: cross-browser processing of unhandled exceptions\n *\n * Syntax:\n * TraceKit.report.subscribe(function(stackInfo) { ... })\n * TraceKit.report.unsubscribe(function(stackInfo) { ... })\n * TraceKit.report(exception)\n * try { ...code... } catch(ex) { TraceKit.report(ex); }\n *\n * Supports:\n * - Firefox: full stack trace with line numbers, plus column number\n * on top frame; column number is not guaranteed\n * - Opera: full stack trace with line and column numbers\n * - Chrome: full stack trace with line and column numbers\n * - Safari: line and column number for the top frame only; some frames\n * may be missing, and column number is not guaranteed\n * - IE: line and column number for the top frame only; some frames\n * may be missing, and column number is not guaranteed\n *\n * In theory, TraceKit should work on all of the following versions:\n * - IE5.5+ (only 8.0 tested)\n * - Firefox 0.9+ (only 3.5+ tested)\n * - Opera 7+ (only 10.50 tested; versions 9 and earlier may require\n * Exceptions Have Stacktrace to be enabled in opera:config)\n * - Safari 3+ (only 4+ tested)\n * - Chrome 1+ (only 5+ tested)\n * - Konqueror 3.5+ (untested)\n *\n * Requires TraceKit.computeStackTrace.\n *\n * Tries to catch all unhandled exceptions and report them to the\n * subscribed handlers. Please note that TraceKit.report will rethrow the\n * exception. This is REQUIRED in order to get a useful stack trace in IE.\n * If the exception does not reach the top of the browser, you will only\n * get a stack trace from the point where TraceKit.report was called.\n *\n * Handlers receive a stackInfo object as described in the\n * TraceKit.computeStackTrace docs.\n */\nTraceKit.report = (function reportModuleWrapper() {\n var handlers = [],\n lastArgs = null,\n lastException = null,\n lastExceptionStack = null;\n\n /**\n * Add a crash handler.\n * @param {Function} handler\n */\n function subscribe(handler) {\n installGlobalHandler();\n handlers.push(handler);\n }\n\n /**\n * Remove a crash handler.\n * @param {Function} handler\n */\n function unsubscribe(handler) {\n for (var i = handlers.length - 1; i >= 0; --i) {\n if (handlers[i] === handler) {\n handlers.splice(i, 1);\n }\n }\n }\n\n /**\n * Dispatch stack information to all handlers.\n * @param {Object.} stack\n */\n function notifyHandlers(stack, isWindowError) {\n var exception = null;\n if (isWindowError && !TraceKit.collectWindowErrors) {\n return;\n }\n for (var i in handlers) {\n if (_has(handlers, i)) {\n try {\n handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));\n } catch (inner) {\n exception = inner;\n }\n }\n }\n\n if (exception) {\n throw exception;\n }\n }\n\n var _oldOnerrorHandler, _onErrorHandlerInstalled;\n\n /**\n * Ensures all global unhandled exceptions are recorded.\n * Supported by Gecko and IE.\n * @param {string} message Error message.\n * @param {string} url URL of script that generated the exception.\n * @param {(number|string)} lineNo The line number at which the error\n * occurred.\n * @param {?(number|string)} columnNo The column number at which the error\n * occurred.\n * @param {?Error} errorObj The actual Error object.\n */\n function traceKitWindowOnError(message, url, lineNo, columnNo, errorObj) {\n var stack = null;\n\n if (lastExceptionStack) {\n TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);\n \t processLastException();\n\t } else if (errorObj) {\n stack = TraceKit.computeStackTrace(errorObj);\n notifyHandlers(stack, true);\n } else {\n var location = {\n 'url': url,\n 'line': lineNo,\n 'column': columnNo\n };\n location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);\n location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);\n stack = {\n 'mode': 'onerror',\n 'message': message,\n 'stack': [location]\n };\n\n notifyHandlers(stack, true);\n }\n\n if (_oldOnerrorHandler) {\n return _oldOnerrorHandler.apply(this, arguments);\n }\n\n return false;\n }\n\n function installGlobalHandler () {\n if (_onErrorHandlerInstalled === true) {\n return;\n }\n _oldOnerrorHandler = window.onerror;\n window.onerror = traceKitWindowOnError;\n _onErrorHandlerInstalled = true;\n }\n\n function processLastException() {\n var _lastExceptionStack = lastExceptionStack,\n _lastArgs = lastArgs;\n lastArgs = null;\n lastExceptionStack = null;\n lastException = null;\n notifyHandlers.apply(null, [_lastExceptionStack, false].concat(_lastArgs));\n }\n /**\n * Reports an unhandled Error to TraceKit.\n * @param {Error} ex\n */\n function report(ex) {\n if (lastExceptionStack) {\n if (lastException === ex) {\n return; // already caught by an inner catch block, ignore\n } else {\n processLastException();\n }\n }\n\n var stack = TraceKit.computeStackTrace(ex);\n lastExceptionStack = stack;\n lastException = ex;\n lastArgs = _slice.call(arguments, 1);\n\n // If the stack trace is incomplete, wait for 2 seconds for\n // slow slow IE to see if onerror occurs or not before reporting\n // this exception; otherwise, we will end up with an incomplete\n // stack trace\n window.setTimeout(function () {\n if (lastException === ex) {\n processLastException();\n }\n }, (stack.incomplete ? 2000 : 0));\n\n throw ex; // re-throw to propagate to the top level (and cause window.onerror)\n }\n\n report.subscribe = subscribe;\n report.unsubscribe = unsubscribe;\n return report;\n}());\n\n/**\n * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript\n *\n * Syntax:\n * s = TraceKit.computeStackTrace.ofCaller([depth])\n * s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)\n * Returns:\n * s.name - exception name\n * s.message - exception message\n * s.stack[i].url - JavaScript or HTML file URL\n * s.stack[i].func - function name, or empty for anonymous functions (if guessing did not work)\n * s.stack[i].args - arguments passed to the function, if known\n * s.stack[i].line - line number, if known\n * s.stack[i].column - column number, if known\n * s.stack[i].context - an array of source code lines; the middle element corresponds to the correct line#\n * s.mode - 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace\n *\n * Supports:\n * - Firefox: full stack trace with line numbers and unreliable column\n * number on top frame\n * - Opera 10: full stack trace with line and column numbers\n * - Opera 9-: full stack trace with line numbers\n * - Chrome: full stack trace with line and column numbers\n * - Safari: line and column number for the topmost stacktrace element\n * only\n * - IE: no line numbers whatsoever\n *\n * Tries to guess names of anonymous functions by looking for assignments\n * in the source code. In IE and Safari, we have to guess source file names\n * by searching for function bodies inside all page scripts. This will not\n * work for scripts that are loaded cross-domain.\n * Here be dragons: some function names may be guessed incorrectly, and\n * duplicate functions may be mismatched.\n *\n * TraceKit.computeStackTrace should only be used for tracing purposes.\n * Logging of unhandled exceptions should be done with TraceKit.report,\n * which builds on top of TraceKit.computeStackTrace and provides better\n * IE support by utilizing the window.onerror event to retrieve information\n * about the top of the stack.\n *\n * Note: In IE and Safari, no stack trace is recorded on the Error object,\n * so computeStackTrace instead walks its *own* chain of callers.\n * This means that:\n * * in Safari, some methods may be missing from the stack trace;\n * * in IE, the topmost function in the stack trace will always be the\n * caller of computeStackTrace.\n *\n * This is okay for tracing (because you are likely to be calling\n * computeStackTrace from the function you want to be the topmost element\n * of the stack trace anyway), but not okay for logging unhandled\n * exceptions (because your catch block will likely be far away from the\n * inner function that actually caused the exception).\n *\n * Tracing example:\n * function trace(message) {\n * var stackInfo = TraceKit.computeStackTrace.ofCaller();\n * var data = message + \"\\n\";\n * for(var i in stackInfo.stack) {\n * var item = stackInfo.stack[i];\n * data += (item.func || '[anonymous]') + \"() in \" + item.url + \":\" + (item.line || '0') + \"\\n\";\n * }\n * if (window.console)\n * console.info(data);\n * else\n * alert(data);\n * }\n */\nTraceKit.computeStackTrace = (function computeStackTraceWrapper() {\n var debug = false,\n sourceCache = {};\n\n /**\n * Attempts to retrieve source code via XMLHttpRequest, which is used\n * to look up anonymous function names.\n * @param {string} url URL of source code.\n * @return {string} Source contents.\n */\n function loadSource(url) {\n if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on.\n return '';\n }\n try {\n var getXHR = function() {\n try {\n return new window.XMLHttpRequest();\n } catch (e) {\n // explicitly bubble up the exception if not found\n return new window.ActiveXObject('Microsoft.XMLHTTP');\n }\n };\n\n var request = getXHR();\n request.open('GET', url, false);\n request.send('');\n return request.responseText;\n } catch (e) {\n return '';\n }\n }\n\n /**\n * Retrieves source code from the source code cache.\n * @param {string} url URL of source code.\n * @return {Array.} Source contents.\n */\n function getSource(url) {\n if (typeof url !== 'string') {\n return [];\n }\n\n if (!_has(sourceCache, url)) {\n // URL needs to be able to fetched within the acceptable domain. Otherwise,\n // cross-domain errors will be triggered.\n var source = '';\n var domain = '';\n try { domain = document.domain; } catch (e) {}\n if (url.indexOf(domain) !== -1) {\n source = loadSource(url);\n }\n sourceCache[url] = source ? source.split('\\n') : [];\n }\n\n return sourceCache[url];\n }\n\n /**\n * Tries to use an externally loaded copy of source code to determine\n * the name of a function by looking at the name of the variable it was\n * assigned to, if any.\n * @param {string} url URL of source code.\n * @param {(string|number)} lineNo Line number in source code.\n * @return {string} The function name, if discoverable.\n */\n function guessFunctionName(url, lineNo) {\n var reFunctionArgNames = /function ([^(]*)\\(([^)]*)\\)/,\n reGuessFunction = /['\"]?([0-9A-Za-z$_]+)['\"]?\\s*[:=]\\s*(function|eval|new Function)/,\n line = '',\n maxLines = 10,\n source = getSource(url),\n m;\n\n if (!source.length) {\n return UNKNOWN_FUNCTION;\n }\n\n // Walk backwards from the first line in the function until we find the line which\n // matches the pattern above, which is the function definition\n for (var i = 0; i < maxLines; ++i) {\n line = source[lineNo - i] + line;\n\n if (!_isUndefined(line)) {\n if ((m = reGuessFunction.exec(line))) {\n return m[1];\n } else if ((m = reFunctionArgNames.exec(line))) {\n return m[1];\n }\n }\n }\n\n return UNKNOWN_FUNCTION;\n }\n\n /**\n * Retrieves the surrounding lines from where an exception occurred.\n * @param {string} url URL of source code.\n * @param {(string|number)} line Line number in source code to centre\n * around for context.\n * @return {?Array.} Lines of source code.\n */\n function gatherContext(url, line) {\n var source = getSource(url);\n\n if (!source.length) {\n return null;\n }\n\n var context = [],\n // linesBefore & linesAfter are inclusive with the offending line.\n // if linesOfContext is even, there will be one extra line\n // *before* the offending line.\n linesBefore = Math.floor(TraceKit.linesOfContext / 2),\n // Add one extra line if linesOfContext is odd\n linesAfter = linesBefore + (TraceKit.linesOfContext % 2),\n start = Math.max(0, line - linesBefore - 1),\n end = Math.min(source.length, line + linesAfter - 1);\n\n line -= 1; // convert to 0-based index\n\n for (var i = start; i < end; ++i) {\n if (!_isUndefined(source[i])) {\n context.push(source[i]);\n }\n }\n\n return context.length > 0 ? context : null;\n }\n\n /**\n * Escapes special characters, except for whitespace, in a string to be\n * used inside a regular expression as a string literal.\n * @param {string} text The string.\n * @return {string} The escaped string literal.\n */\n function escapeRegExp(text) {\n return text.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#]/g, '\\\\$&');\n }\n\n /**\n * Escapes special characters in a string to be used inside a regular\n * expression as a string literal. Also ensures that HTML entities will\n * be matched the same as their literal friends.\n * @param {string} body The string.\n * @return {string} The escaped string.\n */\n function escapeCodeAsRegExpForMatchingInsideHTML(body) {\n return escapeRegExp(body).replace('<', '(?:<|<)').replace('>', '(?:>|>)').replace('&', '(?:&|&)').replace('\"', '(?:\"|")').replace(/\\s+/g, '\\\\s+');\n }\n\n /**\n * Determines where a code fragment occurs in the source code.\n * @param {RegExp} re The function definition.\n * @param {Array.} urls A list of URLs to search.\n * @return {?Object.} An object containing\n * the url, line, and column number of the defined function.\n */\n function findSourceInUrls(re, urls) {\n var source, m;\n for (var i = 0, j = urls.length; i < j; ++i) {\n // console.log('searching', urls[i]);\n if ((source = getSource(urls[i])).length) {\n source = source.join('\\n');\n if ((m = re.exec(source))) {\n // console.log('Found function in ' + urls[i]);\n\n return {\n 'url': urls[i],\n 'line': source.substring(0, m.index).split('\\n').length,\n 'column': m.index - source.lastIndexOf('\\n', m.index) - 1\n };\n }\n }\n }\n\n // console.log('no match');\n\n return null;\n }\n\n /**\n * Determines at which column a code fragment occurs on a line of the\n * source code.\n * @param {string} fragment The code fragment.\n * @param {string} url The URL to search.\n * @param {(string|number)} line The line number to examine.\n * @return {?number} The column number.\n */\n function findSourceInLine(fragment, url, line) {\n var source = getSource(url),\n re = new RegExp('\\\\b' + escapeRegExp(fragment) + '\\\\b'),\n m;\n\n line -= 1;\n\n if (source && source.length > line && (m = re.exec(source[line]))) {\n return m.index;\n }\n\n return null;\n }\n\n /**\n * Determines where a function was defined within the source code.\n * @param {(Function|string)} func A function reference or serialized\n * function definition.\n * @return {?Object.} An object containing\n * the url, line, and column number of the defined function.\n */\n function findSourceByFunctionBody(func) {\n if (_isUndefined(document)) {\n return;\n }\n\n var urls = [window.location.href],\n scripts = document.getElementsByTagName('script'),\n body,\n code = '' + func,\n codeRE = /^function(?:\\s+([\\w$]+))?\\s*\\(([\\w\\s,]*)\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n eventRE = /^function on([\\w$]+)\\s*\\(event\\)\\s*\\{\\s*(\\S[\\s\\S]*\\S)\\s*\\}\\s*$/,\n re,\n parts,\n result;\n\n for (var i = 0; i < scripts.length; ++i) {\n var script = scripts[i];\n if (script.src) {\n urls.push(script.src);\n }\n }\n\n if (!(parts = codeRE.exec(code))) {\n re = new RegExp(escapeRegExp(code).replace(/\\s+/g, '\\\\s+'));\n }\n\n // not sure if this is really necessary, but I don’t have a test\n // corpus large enough to confirm that and it was in the original.\n else {\n var name = parts[1] ? '\\\\s+' + parts[1] : '',\n args = parts[2].split(',').join('\\\\s*,\\\\s*');\n\n body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\\s+/g, '\\\\s+');\n re = new RegExp('function' + name + '\\\\s*\\\\(\\\\s*' + args + '\\\\s*\\\\)\\\\s*{\\\\s*' + body + '\\\\s*}');\n }\n\n // look for a normal function definition\n if ((result = findSourceInUrls(re, urls))) {\n return result;\n }\n\n // look for an old-school event handler function\n if ((parts = eventRE.exec(code))) {\n var event = parts[1];\n body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]);\n\n // look for a function defined in HTML as an onXXX handler\n re = new RegExp('on' + event + '=[\\\\\\'\"]\\\\s*' + body + '\\\\s*[\\\\\\'\"]', 'i');\n\n if ((result = findSourceInUrls(re, urls[0]))) {\n return result;\n }\n\n // look for ???\n re = new RegExp(body);\n\n if ((result = findSourceInUrls(re, urls))) {\n return result;\n }\n }\n\n return null;\n }\n\n // Contents of Exception in various browsers.\n //\n // SAFARI:\n // ex.message = Can't find variable: qq\n // ex.line = 59\n // ex.sourceId = 580238192\n // ex.sourceURL = http://...\n // ex.expressionBeginOffset = 96\n // ex.expressionCaretOffset = 98\n // ex.expressionEndOffset = 98\n // ex.name = ReferenceError\n //\n // FIREFOX:\n // ex.message = qq is not defined\n // ex.fileName = http://...\n // ex.lineNumber = 59\n // ex.columnNumber = 69\n // ex.stack = ...stack trace... (see the example below)\n // ex.name = ReferenceError\n //\n // CHROME:\n // ex.message = qq is not defined\n // ex.name = ReferenceError\n // ex.type = not_defined\n // ex.arguments = ['aa']\n // ex.stack = ...stack trace...\n //\n // INTERNET EXPLORER:\n // ex.message = ...\n // ex.name = ReferenceError\n //\n // OPERA:\n // ex.message = ...message... (see the example below)\n // ex.name = ReferenceError\n // ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message)\n // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'\n\n /**\n * Computes stack trace information from the stack property.\n * Chrome and Gecko use this property.\n * @param {Error} ex\n * @return {?Object.} Stack trace information.\n */\n function computeStackTraceFromStackProp(ex) {\n if (!ex.stack) {\n return null;\n }\n\n var chrome = /^\\s*at (.*?) ?\\(((?:file|https?|blob|chrome-extension|native|eval).*?)(?::(\\d+))?(?::(\\d+))?\\)?\\s*$/i,\n gecko = /^\\s*(.*?)(?:\\((.*?)\\))?@?((?:file|https?|blob|chrome|\\[).*?)(?::(\\d+))?(?::(\\d+))?\\s*$/i,\n winjs = /^\\s*at (?:((?:\\[object object\\])?.+) )?\\(?((?:ms-appx|https?|blob):.*?):(\\d+)(?::(\\d+))?\\)?\\s*$/i,\n lines = ex.stack.split('\\n'),\n stack = [],\n parts,\n element,\n reference = /^(.*) is undefined$/.exec(ex.message);\n\n for (var i = 0, j = lines.length; i < j; ++i) {\n if ((parts = chrome.exec(lines[i]))) {\n var isNative = parts[2] && parts[2].indexOf('native') !== -1;\n element = {\n 'url': !isNative ? parts[2] : null,\n 'func': parts[1] || UNKNOWN_FUNCTION,\n 'args': isNative ? [parts[2]] : [],\n 'line': parts[3] ? +parts[3] : null,\n 'column': parts[4] ? +parts[4] : null\n };\n } else if ( parts = winjs.exec(lines[i]) ) {\n element = {\n 'url': parts[2],\n 'func': parts[1] || UNKNOWN_FUNCTION,\n 'args': [],\n 'line': +parts[3],\n 'column': parts[4] ? +parts[4] : null\n };\n } else if ((parts = gecko.exec(lines[i]))) {\n element = {\n 'url': parts[3],\n 'func': parts[1] || UNKNOWN_FUNCTION,\n 'args': parts[2] ? parts[2].split(',') : [],\n 'line': parts[4] ? +parts[4] : null,\n 'column': parts[5] ? +parts[5] : null\n };\n } else {\n continue;\n }\n\n if (!element.func && element.line) {\n element.func = guessFunctionName(element.url, element.line);\n }\n\n if (element.line) {\n element.context = gatherContext(element.url, element.line);\n }\n\n stack.push(element);\n }\n\n if (!stack.length) {\n return null;\n }\n\n if (stack[0] && stack[0].line && !stack[0].column && reference) {\n stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);\n } else if (!stack[0].column && !_isUndefined(ex.columnNumber)) {\n // FireFox uses this awesome columnNumber property for its top frame\n // Also note, Firefox's column number is 0-based and everything else expects 1-based,\n // so adding 1\n stack[0].column = ex.columnNumber + 1;\n }\n\n return {\n 'mode': 'stack',\n 'name': ex.name,\n 'message': ex.message,\n 'stack': stack\n };\n }\n\n /**\n * Computes stack trace information from the stacktrace property.\n * Opera 10+ uses this property.\n * @param {Error} ex\n * @return {?Object.} Stack trace information.\n */\n function computeStackTraceFromStacktraceProp(ex) {\n // Access and store the stacktrace property before doing ANYTHING\n // else to it because Opera is not very good at providing it\n // reliably in other circumstances.\n var stacktrace = ex.stacktrace;\n if (!stacktrace) {\n return;\n }\n\n var opera10Regex = / line (\\d+).*script (?:in )?(\\S+)(?:: in function (\\S+))?$/i,\n opera11Regex = / line (\\d+), column (\\d+)\\s*(?:in (?:]+)>|([^\\)]+))\\((.*)\\))? in (.*):\\s*$/i,\n lines = stacktrace.split('\\n'),\n stack = [],\n parts;\n\n for (var line = 0; line < lines.length; line += 2) {\n var element = null;\n if ((parts = opera10Regex.exec(lines[line]))) {\n element = {\n 'url': parts[2],\n 'line': +parts[1],\n 'column': null,\n 'func': parts[3],\n 'args':[]\n };\n } else if ((parts = opera11Regex.exec(lines[line]))) {\n element = {\n 'url': parts[6],\n 'line': +parts[1],\n 'column': +parts[2],\n 'func': parts[3] || parts[4],\n 'args': parts[5] ? parts[5].split(',') : []\n };\n }\n\n if (element) {\n if (!element.func && element.line) {\n element.func = guessFunctionName(element.url, element.line);\n }\n if (element.line) {\n try {\n element.context = gatherContext(element.url, element.line);\n } catch (exc) {}\n }\n\n if (!element.context) {\n element.context = [lines[line + 1]];\n }\n\n stack.push(element);\n }\n }\n\n if (!stack.length) {\n return null;\n }\n\n return {\n 'mode': 'stacktrace',\n 'name': ex.name,\n 'message': ex.message,\n 'stack': stack\n };\n }\n\n /**\n * NOT TESTED.\n * Computes stack trace information from an error message that includes\n * the stack trace.\n * Opera 9 and earlier use this method if the option to show stack\n * traces is turned on in opera:config.\n * @param {Error} ex\n * @return {?Object.} Stack information.\n */\n function computeStackTraceFromOperaMultiLineMessage(ex) {\n // TODO: Clean this function up\n // Opera includes a stack trace into the exception message. An example is:\n //\n // Statement on line 3: Undefined variable: undefinedFunc\n // Backtrace:\n // Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz\n // undefinedFunc(a);\n // Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy\n // zzz(x, y, z);\n // Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx\n // yyy(a, a, a);\n // Line 1 of function script\n // try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }\n // ...\n\n var lines = ex.message.split('\\n');\n if (lines.length < 4) {\n return null;\n }\n\n var lineRE1 = /^\\s*Line (\\d+) of linked script ((?:file|https?|blob)\\S+)(?:: in function (\\S+))?\\s*$/i,\n lineRE2 = /^\\s*Line (\\d+) of inline#(\\d+) script in ((?:file|https?|blob)\\S+)(?:: in function (\\S+))?\\s*$/i,\n lineRE3 = /^\\s*Line (\\d+) of function script\\s*$/i,\n stack = [],\n scripts = document.getElementsByTagName('script'),\n inlineScriptBlocks = [],\n parts;\n\n for (var s in scripts) {\n if (_has(scripts, s) && !scripts[s].src) {\n inlineScriptBlocks.push(scripts[s]);\n }\n }\n\n for (var line = 2; line < lines.length; line += 2) {\n var item = null;\n if ((parts = lineRE1.exec(lines[line]))) {\n item = {\n 'url': parts[2],\n 'func': parts[3],\n 'args': [],\n 'line': +parts[1],\n 'column': null\n };\n } else if ((parts = lineRE2.exec(lines[line]))) {\n item = {\n 'url': parts[3],\n 'func': parts[4],\n 'args': [],\n 'line': +parts[1],\n 'column': null // TODO: Check to see if inline#1 (+parts[2]) points to the script number or column number.\n };\n var relativeLine = (+parts[1]); // relative to the start of the