Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix instanceof checks #5995

Closed
wants to merge 7 commits into from
Closed

Conversation

SimenB
Copy link
Member

@SimenB SimenB commented Apr 15, 2018

Summary

This is a rebase of @suchipi's https://github.com/suchipi/jest/tree/instanceof_overrides with added tests.

I added the two small reproductions from #2549 for Error, but I'd love to get small examples of other globals breaking (Object, Array, Buffer, Symbol comes to mind). Can anyone provide those?

Fixes #2549.

Test plan

Tests added (but I want more of them).

@@ -33,5 +34,7 @@ export default function(globalObject: Global, globals: ConfigGlobals) {
globalObject.setImmediate = global.setImmediate;
globalObject.clearImmediate = global.clearImmediate;

addInstanceOfAlias(globalObject.Error, Error);
Copy link
Member Author

Choose a reason for hiding this comment

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

We should probably have some sort of blacklist and just loop all globals, rather than patching them in as people report bugs.

Copy link

Choose a reason for hiding this comment

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

Yes, please.

});

test('Array', () => {
expect([]).toBeInstanceOf(Array);
Copy link
Member Author

Choose a reason for hiding this comment

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

this assertion passes without change in jest...

Copy link
Collaborator

Choose a reason for hiding this comment

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

Does it make sense to test it in jsdom environment too?

Copy link
Member Author

Choose a reason for hiding this comment

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

Too bad it's not supported as a flag, but yeah, I can run the tests in both envs

@SimenB
Copy link
Member Author

SimenB commented Apr 15, 2018

This seems to have broken node assert support... I'll see if I can take a look at it later this coming week. Help appreciated!

*/
'use strict';

export default function addInstanceOfAlias(target: Object, alias: Object) {
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 know if alias is a good name. I only know alias in terms of giving things a different name but this is setting a different constructor. How about addInstanceOfObject or addInstanceOfConstructor or addInstanceParent or similar?

@cpojer
Copy link
Member

cpojer commented Apr 15, 2018

This is really super cool. Didn't know this feature in JS. TIL.

@suchipi
Copy link
Contributor

suchipi commented Apr 15, 2018

@SimenB thanks for picking this up, sorry I never got around to finishing it

@SimenB
Copy link
Member Author

SimenB commented Apr 15, 2018

@suchipi Thanks for pointing Symbol.hasInstance!

Btw, do you have any ideas about why this instanceof fails with the changes in this PR: https://github.com/facebook/jest/blob/65d3d8d37e471ae9a02150933168470d9966c021/packages/jest-jasmine2/src/jasmine/Spec.js#L135?

@cpojer
Copy link
Member

cpojer commented Apr 15, 2018

Can we use duck-typing in that assertion?

@SimenB
Copy link
Member Author

SimenB commented Apr 15, 2018

I had a hope that instanceof would just work:tm: with this change. But as long as it works in user's test code while we have to fake it in Jest internals, that should be fine

@suchipi
Copy link
Contributor

suchipi commented Apr 15, 2018

I'm not sure why that's failing; before merging, it'd be good to verify the same issue doesn't occur in user code when using AssertionError

@SimenB
Copy link
Member Author

SimenB commented May 21, 2018

Tried a rebase. Seems like the issue here is that all someError instanceof AnyError evaluate to true with this diff

@Bradcomp
Copy link

Is there any progress on this? I just ran into the same issue and it's really annoying.

@edevil
Copy link

edevil commented Oct 8, 2018

Is there anything that I can do to help move this along? I just spent 2 hours trying to track down why parsing the "Set-Cookie" header was not working only on tests.

@SimenB
Copy link
Member Author

SimenB commented Oct 8, 2018

You can play with it and see if you can get the tests to pass. I can give it a quick rebase first, though 🙂

All help welcome! This is probably the biggest pain point I have with Jest, and a huge gotcha

@SimenB SimenB force-pushed the instanceof_overrides branch 2 times, most recently from e4db645 to 8c16548 Compare October 8, 2018 14:46
@@ -148,12 +146,12 @@ Spec.prototype.onException = function onException(error) {
return;
}

if (error instanceof ExpectationFailed) {
return;
if (error && error.code === 'ERR_ASSERTION') {
Copy link
Member Author

@SimenB SimenB Oct 8, 2018

Choose a reason for hiding this comment

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

not happy with this. The conditional itself is fine (probably better than instanceof), but this shouldn't be a necessary change

@edevil
Copy link

edevil commented Oct 8, 2018

@SimenB FWIW, I tried Jest with this PR and it did not solve the bug.

I'm using superagent, and the multiple set-cookie headers on a response are still returned concatenated instead of as separate elements in an array, as it does outside Jest.

I linked jest-cli and jest-util, is this not enough to exercise the new code?

@SimenB
Copy link
Member Author

SimenB commented Oct 8, 2018

Can you share your reproduction? I can add it as a test case 🙂

@edevil
Copy link

edevil commented Oct 8, 2018

@SimenB This is one from the other ticket:

    let url = require('url');
    let query = url.parse('https://brpx.com/?f=1&f=2&f=3', true).query;
    console.log(query.f instanceof Array);

This is fixed by addInstanceOfAlias(globalObject.Array, Array);

However, this also does not fix my problem. I'm trying to find a small repro script but I've just noticed that in node 10 the problem does not happen, only in 8, so I'm less motivated now. :)

@edevil
Copy link

edevil commented Oct 8, 2018

I've noticed that in Node 10 several instanceof Array were changed to Array.isArray (https://github.com/nodejs/node/blob/master/lib/_http_outgoing.js#L398). This may explain why it now works. But why can't this Array alias work in Node 8? Is it because it's internal Node code?

@edevil
Copy link

edevil commented Oct 9, 2018

I just confirmed that the problem is on https://github.com/nodejs/node/blob/v8.x/lib/_http_outgoing.js#L313.

if (value instanceof Array) {

This check is not fixed even when aliasing Array with this PR.

@SimenB
Copy link
Member Author

SimenB commented Jan 26, 2019

@edevil that's fixed in newer versions of Node, thankfully 🙂 (as you mention)

nodejs/node#20250

If we can get this working for node 10, I'm perfectly fine with that (although it would be great if we could find a solution that works for all supported versions of node)

@mcheshkov
Copy link

mcheshkov commented Apr 13, 2019

I was trying to extract this to local jest environment, and found a bug.

[Symbol.hasInstance] implementation in this PR does not validate which constructor was instanceof originally called for. So, for obj instanceof Subclass it will try to call Subclass[Symbol.hasInstance], and, if it's not defined it will fall through to Class[Symbol.hasInstance], which resolves to true, which is may be correct result for Class, but incorrect for Subclass

Try running this:

class Class {
  static [Symbol.hasInstance](){ console.log('Class hasInstance'); return true; }
}
class Subclass extends Class {}
const obj = new Class(); // or even {} for this example
console.log(obj instanceof Subclass);

I've fixed this locally using this === target in hasInstance implementation. this resolves to actual constructor in RHS of instanceof, and target is one we are patching.

@rodush
Copy link

rodush commented Feb 22, 2020

Guys, this seems to be abandoned, but the fix is really needed to be able to (integration-) test rest apps, with e.g. restify, and the supertest.
Currently, the status code of the original error (one of restify-errors, e.g.: 503) passed to the next() callback, is swallowed and lost.
It results in supertest receives the incorrect status code (500 instead of 503 or any other custom code).

@julienw
Copy link

julienw commented Jun 2, 2020

FYI I found an instance of (pun not intended) this problem in koajs/koa#1466.

@kayahr
Copy link

kayahr commented Oct 1, 2021

You were asking for more tests. I collected a bunch here: SingleContextNodeEnvironment.test.ts

Most of these tests fail with standard Jest but all work when using jest-environment-node-single-context

Some of the tests may look very constructed but they simulate problems happening for real when working with types returned by Node APIs. I just had to be creative for these tests to get the various datatypes directly from Node.

@SimenB
Copy link
Member Author

SimenB commented Oct 1, 2021

Hah wow, almost 3.5 years old this PR 🙈

Thanks a bunch @kayahr! I'll rebase this and add those. I have zero recollection of the state of this PR, but let's see where it's at.

I need a screenshot to remember the times we supported node 6, tho! 😀

image

@SimenB
Copy link
Member Author

SimenB commented Oct 1, 2021

@mcheshkov I don't follow your example. Running your code in plain Node logs out true - and the same works in Jest without any changes at all (and in this PR)

@github-actions
Copy link

github-actions bot commented Oct 1, 2022

This PR is stale because it has been open 1 year with no activity. Remove stale label or comment or this will be closed in 30 days.

@github-actions github-actions bot added the Stale label Oct 1, 2022
@github-actions
Copy link

This PR was closed because it has been stalled for 30 days with no activity. Please open a new PR if the issue is still relevant, linking to this one.

@github-actions github-actions bot closed this Oct 31, 2022
@github-actions
Copy link

github-actions bot commented Dec 1, 2022

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 1, 2022
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.

Jest globals differ from Node globals