-
Notifications
You must be signed in to change notification settings - Fork 30
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
Don't assume HTMLElement is in the global scope #91
Conversation
Instead uses `globalObject.HTMLElement`, to avoid breaking in a node environment where `HTMLElement` is not defined.
Thanks for the PR @jetpacmonkey. Could we also get some tests for this? Or some information on how you have come to produce the error? |
@jetpacmonkey Could you provide a bit more detail about the problem you're running into, and the surrounding circumstances? The lines you changed are currently only run when |
Hmm, you're right. I'm using jsdom in my test environment, and only put a few values from the |
Do you need these in the global scope, given as you've already said this is an antipattern? |
True, doing less of an antipattern is still an antipattern :) I'll see if I can remember why I did that, and remove whatever's depending on it. Thanks for the quick responses! |
@jetpacmonkey did you get anywhere with that? I think unless there's a super solid usecase for this, I'd rather close the PR. Thoughts? |
I have the exact use case of @jetpacmonkey, I'm using jsdom, and whenever using sinon.match I get this error, see #98 |
See also: enzymejs/enzyme#888 |
I think enzyme might be the real problem here, since it expects to execute in a browser-like environment, and I haven't come across a great tool for sandboxing browser-like environments. Someone should write one of those... Anyways, it appears that I don't have a great way to get around that antipattern. However, what's really confusing me right now is the fact that I'm not intentionally putting |
This appears to be relevant: https://github.com/airbnb/enzyme/blob/master/docs/guides/jsdom.md#describewithdom-api-and-clearing-the-document-after-every-test Antipattern or no, a shared global DOM appears to be required for testing React components. |
@jetpacmonkey I'm also not putting location into the global.. Resuming:
My configuration before loading the react tests:
EDIT:
Given I've added |
@keithamus according to this comment from enzyme member he think this should be an issue of type-detect. What do you think? As temp fix until this will be solved I can only see an extra line in my config before running the tests:
|
So, the initial reason for not merging this PR as it was presented was that Plus, I'd argue that relying on implicit globals any more than is absolutely necessary is an antipattern itself. @keithamus thoughts? |
at work so cant get into detail, but essentially the pr needs to be updated
so that the tests involving HTMLElement are skipped if its undefined.
checking if it's defined should occur at the top with all the other checks.
…On Apr 17, 2017 9:41 AM, "Jeremy Tice" ***@***.***> wrote:
So, the initial reason for not merging this PR as it was presented was
that obj instanceof undefined "doesn't make sense". However, it *does*
avoid things unexpectedly breaking, and it doesn't cause any false
positives. The only case where it has any effect at all is arguably an
antipattern, but it's also a pattern that's explicitly recommended by at
least one popular testing library.
Plus, I'd argue that relying on implicit globals any more than is
absolutely necessary is an antipattern itself. @keithamus
<https://github.com/keithamus> thoughts?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#91 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AQdhvd7NXKZ3PidrD78XpQBDPx5gcbKfks5rw2wQgaJpZM4MlwBg>
.
|
Alright, I can do that. |
I had two real concerns initially with the PR; one being I wasn't totally satisfied with why we should make the change, as it seems we have valid enough code. I'm satisfied now, however, that there is clear precedent for us to be more defensive. My larger concern is that I'd like some tests to prevent regressions in future. However this is a simple enough change, which will be difficult to test, and has hung around for nearly a month so I'm willing to forgo the need for tests. I would however agree with @meeber - |
You know, I just re-read what @LeonardoGentile wrote. Checking if it's in the |
And I have no problem writing a couple of tests for this. I just wanted to make sure that this was a solution that you guys were ok with before I spent the time. |
Upon further reflection, I think we should do two things here:
|
has CSP issues, otherwise 👍 |
@meeber I pushed up a check that should do what you described. I suppose for the new tests, I'll just add a jsdom devDependency and re-run the node tests using that? |
The funny thing is, with my original change, it actually wouldn't have been |
Oh, and apparently the linter hated that commit. Looks like it was disabled for that line anyways, before, so I guess I'll do that again. |
index.js
Outdated
// See http://stackoverflow.com/a/6930376 | ||
var globalObject; | ||
try { | ||
globalObject = Function('return this')() || (42, eval)('this'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's my understanding that all that's needed here is var globalObject = Function('return this')();
. Not the ||
nor the try/catch
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On top of what @meeber has mentioned, I would prefer new Function
. new
with constructors is the new black in ES6. Also, this won't work in Chrome OS extensions for example. More on CSP: jashkenas/underscore#906.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was, for the most part, copied from that StackOverflow post. The try/catch
is needed because of CSP. window
is a safe fallback because CSP doesn't exist in non-browser environments, afaik. The ||
I wasn't quite as clear on. Something to do with use strict
if I was reading that SO post correctly?
@shvaikalesh will it "not work" even with the try/catch
? I thought I was already accounting for CSP... Also, I agree about using new
.
index.js
Outdated
'document', | ||
'navigator', | ||
'HTMLElement', | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing I'm concerned about here is that lines like this if (obj === (globalObject.navigator || {}).mimeTypes) {
indicate that sometimes globalObject.navigator
isn't expected to be defined, even in a valid browser environment. However, this isn't my area of expertise. @keithamus @shvaikalesh thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've never encountered a browser (not SSR nor React Native) environment without navigator
defined.
@jetpacmonkey Thanks for working on this! Regarding tests: not sure how I feel about pulling in |
index.js
Outdated
var globalObject = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : self; // eslint-disable-line | ||
var isDom = 'location' in globalObject && 'document' in globalObject; | ||
|
||
/* eslint-disable */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to disable specific rule(s), not the whole eslint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I just did it this way because it was originally // eslint-disable-line
Okay I've modified the PR to fix up all the comments. I'm happy with this but I'll dismiss my PR as I've contributed. @shvaikalesh @meeber could you please check through this and if we're all happy with this we can squash and merge as a |
I think we should avoid |
Okay @shvaikalesh how about this? Best of both worlds? |
@shvaikalesh @keithamus Can you talk me through how the global object detection now works? |
index.js
Outdated
'HTMLElement' in window | ||
) { | ||
isDom = true; | ||
globalObject = window; // eslint-disable-line no-undef |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove this line: if we have window
object, it is equal to self
current code: var globalObject = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : self; PR: var globalObject = typeof self === 'object' ? self : global; Kinda the same thing, except that the latter option relies on |
Okay, updated. We should be good now right? Whoever merges don't forget to squash+merge as |
var globalObject = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : self; // eslint-disable-line | ||
var isDom = 'location' in globalObject && 'document' in globalObject; | ||
|
||
/* eslint-disable no-undef */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, forgot to re-enable this one. Can't right now - but will get to it later.
The build failed because of ESLint errors
It would be easy to fix this with a few well placed comments: // eslint-disable-next-line no-undef id-blacklist
var globalObject = typeof self === 'object' ? self : global;
/*
* All of these attributes must be available on the global object for the current environment
* to be considered a DOM environment (browser)
*/
/* eslint-disable no-undef */
var isDom = typeof window === 'object' &&
'document' in window &&
'navigator' in window &&
'HTMLElement' in window;
/* eslint-enable no-undef */ Who can fix the branch, so we can get this merged? |
@meeber @shvaikalesh shall we get this reviewed and (squash) merged? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Selecting "Approve" so if my question is off-base, we can get this merged without any further delay:
Is there any plausible scenario in which isDom
gets set to true
(because window
is an object and has the document
, navigator
, and HTMLElement
properties), but globalObject
gets set to something other than window
on line 11? If so, then the code won't behave as expected.
I guess I'm mostly worried about these "browser-like" Node environments thwarting the detection logic again, which is what led to this issue originally
Perhaps this is a bug, like the one is this PR, which we'll cross when someone experiences it. While technically possible, I doubt we'll ever see it - if someone is trying to emulate the DOM it's probably a mistake to be missing |
Thank you 💯 It would be great if someone could publish a new release to npm |
It looks like sinonjs/sinon#1377 is waiting on this being released, is it possible to get it published please? 🙏 |
Ah perfect, thanks @vieiralucas! |
@gpoole @chaijs/type-detect v4.0.4 just got released |
Instead uses
globalObject.HTMLElement
, to avoid breaking in a node environment whereHTMLElement
is not defined.Closes #98