Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/raven.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,18 +461,18 @@ Raven.prototype = {
captureException: function(ex, options) {
options = objectMerge({trimHeadFrames: 0}, options ? options : {});

if (isPlainObject(ex)) {
// If it is plain Object, serialize it manually and extract options
// This will allow us to group events based on top-level keys
// which is much better than creating new group when any key/value change
options = this._getCaptureExceptionOptionsFromPlainObject(options, ex);
ex = new Error(options.message);
} else if (isErrorEvent(ex) && ex.error) {
if (isErrorEvent(ex) && ex.error) {
// If it is an ErrorEvent with `error` property, extract it to get actual Error
ex = ex.error;
} else if (isError(ex)) {
// we have a real Error object
ex = ex;
} else if (isPlainObject(ex)) {
// If it is plain Object, serialize it manually and extract options
// This will allow us to group events based on top-level keys
// which is much better than creating new group when any key/value change
options = this._getCaptureExceptionOptionsFromPlainObject(options, ex);
ex = new Error(options.message);
} else {
// If none of previous checks were valid, then it means that
// it's not a plain Object
Expand Down
40 changes: 40 additions & 0 deletions test/raven.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3066,8 +3066,10 @@ describe('Raven (public API)', function() {
var error = new ErrorEvent('pickleRick', {error: new Error('pickleRick')});
this.sinon.stub(Raven, 'isSetup').returns(true);
this.sinon.stub(Raven, '_handleStackInfo');
this.sinon.spy(Raven, '_getCaptureExceptionOptionsFromPlainObject');
Raven.captureException(error, {foo: 'bar'});
assert.isTrue(Raven._handleStackInfo.calledOnce);
assert.isFalse(Raven._getCaptureExceptionOptionsFromPlainObject.called);
});

it('should send ErrorEvents without Errors as messages', function() {
Expand All @@ -3079,6 +3081,44 @@ describe('Raven (public API)', function() {
});
}

it("should treat Schrodinger's Error in the same way as regular Error", function() {
// Schrodinger's Error is an object that is and is not an Error at the same time
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you characterize the properties of these errors a little bit more explicitly (like, "it has the prototype of an error but also passes isPlanObject because X", or maybe link the the issue? i'm not sure i would understand based only on this test

// Like... error, but not really.
// But error.
//
// To be more exact, it's an object literal or an instance of constructor function
// that has it's prototype set to the Error object itself.
// When using `isPlanObject`, which makes a call to `Object.prototype.toString`,
// it returns `[object Object]`, because any instance created with `new X`
// where X is a custom constructor like `function X () {}`, it's return value
// is an object literal.
// However, because it has it's prototype set to an Error object,
// when using `instanceof Error` check, it returns `true`, because calls
// like this, are always going up the prototype chain and will verify
// all possible constructors. For example:
//
// class Foo extends Bar {}
// class Bar extends Error {}
//
// var foo = new Foo();
//
// and now `foo` is instance of every "extension" ever created in the chain
//
// foo instanceof Foo; // true
// foo instanceof Bar; // true (because Foo extends Bar)
// foo instanceof Error; // true (because Foo extends Bar that extends Error)

function SchrodingersError() {}
SchrodingersError.prototype = new Error("Schrödinger's cat was here");
var error = new SchrodingersError();
this.sinon.stub(Raven, 'isSetup').returns(true);
this.sinon.stub(Raven, '_handleStackInfo');
this.sinon.spy(Raven, '_getCaptureExceptionOptionsFromPlainObject');
Raven.captureException(error, {foo: 'bar'});
assert.isTrue(Raven._handleStackInfo.calledOnce);
assert.isFalse(Raven._getCaptureExceptionOptionsFromPlainObject.called);
});

it('should call handleStackInfo', function() {
var error = new Error('pickleRick');
this.sinon.stub(Raven, 'isSetup').returns(true);
Expand Down