Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

provide isError and use it in place of instanceof Error #15872

Closed
wants to merge 1 commit into from

Conversation

graingert
Copy link
Contributor

@graingert graingert commented Mar 30, 2017

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)

feature/bug

What is the current behavior? (You can also link to an open issue here)

What is the new behavior (if this is a feature change)?

Angular should pass them on to the correct excetion handler, and there's an angular.isError function now.

Does this PR introduce a breaking change?

No

Please check if the PR fulfills these requirements

Other information:

re-implimented https://github.com/yefremov/iserror/blob/master/index.js to avoid license and make code faster.

Fixes #15868

@graingert
Copy link
Contributor Author

@gkalpak what would be a good way to construct a test that boots up an iframe? And where should I put it?

function testError(createError) {
    var iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    try {
        var error = createError(contentWindow);
        expect(error instanceof Error).to.be.false();
        expect(isError(error)).to.be.true();
    } finally {
        iframe.parentElement.removeChild(iframe);
    }
}

testError(function (win) {
    return new win.Error();
});

testError(function (win) {
    try {
        win.document.querySelectorAll('');
    catch(e) {
        return e;
    }
});

Copy link
Member

@gkalpak gkalpak left a comment

Choose a reason for hiding this comment

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

A couple of minor comments. Looks OK otherwise (except for the missing tests).
The tests can go in https://github.com/graingert/angular.js/blob/af80f4765e2a9e795f64103b6e15770cf6eda050/test/AngularSpec.js.

src/Angular.js Outdated
@@ -675,6 +676,28 @@ var isArray = Array.isArray;

/**
* @ngdoc function
* @name angular.isError
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we want to make this public.

src/Angular.js Outdated
* @returns {boolean} True if `value` is an `Error`.
*/
function isError(value) {
var tag = ({}).toString.call(value);
Copy link
Member

Choose a reason for hiding this comment

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

We already have a closure-global toString = Object.prototype.toString. You can simply toString.call(value).

@@ -17,6 +17,7 @@ describe('minErr', function() {
it('should return an Error factory', function() {
var myError = testError('test', 'Oops');
expect(myError instanceof Error).toBe(true);
expect(isError(myError)).toBe(true);
Copy link
Member

Choose a reason for hiding this comment

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

This seems irrelevant here. Please remove.

@graingert
Copy link
Contributor Author

graingert commented Mar 30, 2017 via email

@gkalpak
Copy link
Member

gkalpak commented Mar 30, 2017

What's wrong with publishing it?

We have to maintain it in a backwards compatible way. If it is not public we can change/improve/remove it without any concerns.

@graingert
Copy link
Contributor Author

graingert commented Mar 30, 2017 via email

@gkalpak
Copy link
Member

gkalpak commented Mar 30, 2017

How do you not publish it?

Remove the @ngdoc anotation (and others that are not relevant/necessary: @name, @module ng, @kind).
(It won't be available anyway, so we don't want to have it appear on the docs either.)

@@ -984,7 +984,7 @@ angular.mock.dump = function(object) {
} else if (angular.isObject(object)) {
if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
out = serializeScope(object);
} else if (object instanceof Error) {
} else if (angular.isError(object)) {
Copy link
Contributor Author

@graingert graingert Mar 30, 2017

Choose a reason for hiding this comment

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

a published isError is required here.

Copy link
Member

Choose a reason for hiding this comment

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

We could make it "private API" (e.g. angular.$$isError), but I don't think it is worth it. This function (angular.mock.dump) is very rarely used. It is also very rare to have new window contexts in unit tests and it is even more rare to have them throw errors. Plus, DOMExceptions get decently stringified by JSON.stringify (in contrast to plain Errors which become "{}"). All that considered, I think this one occurrence can remain object instanceof Error 😃

@graingert graingert force-pushed the add-is-error branch 5 times, most recently from fdaccde to fb8f226 Compare March 30, 2017 20:31
expect(isError(new Error())).toBe(true);
});

function testError(createError) {
Copy link
Member

Choose a reason for hiding this comment

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

Move this either at the top or at the bottom of the describe block (or even create a nested describe for "other-context" tests) and give it a more descriptive name (e.g. testErrorFromDifferent context.

Maybe having a separate describe block with beforeAll/afterAll blocks for creating/destorying the iframe is a good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

try { } finally { } is more fine grained in this usecase

Copy link
Member

Choose a reason for hiding this comment

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

What do you mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's specific to the actual call to the testErrorFromDifferentContext function. Like the lifetime of the iframe is shorter than a test, no point leaving it hanging around until afterAll/beforeAll is called.

Copy link
Member

Choose a reason for hiding this comment

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

I am fine either way, so feel free to leave things as they are 😃

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

@@ -984,7 +984,7 @@ angular.mock.dump = function(object) {
} else if (angular.isObject(object)) {
if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
out = serializeScope(object);
} else if (object instanceof Error) {
} else if (angular.isError(object)) {
Copy link
Member

Choose a reason for hiding this comment

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

We could make it "private API" (e.g. angular.$$isError), but I don't think it is worth it. This function (angular.mock.dump) is very rarely used. It is also very rare to have new window contexts in unit tests and it is even more rare to have them throw errors. Plus, DOMExceptions get decently stringified by JSON.stringify (in contrast to plain Errors which become "{}"). All that considered, I think this one occurrence can remain object instanceof Error 😃

@graingert graingert force-pushed the add-is-error branch 4 times, most recently from 85edd0f to a5710b1 Compare April 3, 2017 10:18
@graingert
Copy link
Contributor Author

graingert commented Apr 3, 2017

@gkalpak ok, I'm pretty sure I've resolved everything. Interestingly enough if seems as though Safari thinks DOMExceptions from frames are instances of Errors

@Narretz Narretz added this to the Backlog milestone Apr 5, 2017
Copy link
Member

@gkalpak gkalpak left a comment

Choose a reason for hiding this comment

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

LGTM


it('should not assume objects are errors', function() {
expect(isError({ message: 'A fake error', stack: 'no stack here'}))
.toBe(false);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Unnecessarily wrapped line.

@gkalpak
Copy link
Member

gkalpak commented Apr 27, 2017

(The commit message needs some love (as it doesn't follow our guidelines).)

@gkalpak
Copy link
Member

gkalpak commented Apr 27, 2017

I am going to merge this as is, but feel free to add DOMError in another PR 😁

@graingert
Copy link
Contributor Author

I'd like to add DOMError too

scratch that, DOMError only has a 'name' property and never has a stack, so it shouldn't be added imhop.

@gkalpak
Copy link
Member

gkalpak commented Apr 27, 2017

Actually, before I merge it can you take care of the following:

  1. Fix provide isError and use it in place of instanceof Error #15872 (comment).
  2. Fix the commit message to follow the commit message guidelines.
  3. Add a comment above the function pointing to the original project.

@graingert
Copy link
Contributor Author

Fix the commit message to follow the commit message guidelines.

what message should I use?

@gkalpak
Copy link
Member

gkalpak commented Apr 27, 2017

Something that follows the guidelines. E.g. something like:

fix(*): correctly detect Error instances from different contexts

Errors thrown from different contexts (such as an iframe or webworker) are not detected as Error
instances and handled accordingly.
This commit fixes it by introducing an isError() helper, that is able to correctly detect such
instances.

Fixes #15868

12345678901234567890123456789012345678901234567890123456789012345678901234567890
Errors thrown from different contexts (such as an iframe or webworker) are not detected as Error
instances and handled accordingly.
This commit fixes it by introducing an isError() helper, that is able to correctly detect such
instances.

Fixes angular#15868
@graingert
Copy link
Contributor Author

@gkalpak any updates on this?

@gkalpak gkalpak closed this in 837acd1 May 22, 2017
gkalpak pushed a commit that referenced this pull request May 22, 2017
Previously, errors thrown from different contexts (such as an iframe or
webworker) were not detected as `Error` instances and handled accordingly.
This commit fixes it by introducing an `isError()` helper, that is able to
correctly detect such instances.

Fixes #15868

Closes #15872
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

provide isError and use it in place of instanceof Error
5 participants