MochaJS 'Window Is Undefined' - javascript

In MochaJS, I keep receiving this error when importing my JS file for testing:
ReferenceError: window is not defined
My js file is written with the following pattern, which I believe is best practice for defining window level variables:
if (typeof window.myVar === 'undefined') {
window.myVar = ...
}
According to:
What is the correct way to check if a global variable exists?
At any rate, it seems reasonable that one should be able to make a reference to 'window' at any point within a js file without breaking a unit test.
I do NOT want to simulate going to a URL (as zombieJS tutorials seems to assume), nor can I get zombieJS or phantomJS to create a mock of the window object.
Inserting
var window = {};
Into my unit test has no effect.
Other posts that seem to deal with this, such as Defining Window for Testing in Mocha make absolutely no sense to me.
How do people deal with this?

I figured it out!
Basically it involves passing whatever the root object is as "this" to the function that is being unit tested.
I wrote a more comprehensive blog post on it.

Related

Best practices for creating a "debug mode" variable for my app?

I was about to comment out blocks of code that just printed/console.logged debugging info, and I thought, why don't I create a global scope "debug" variable, and instead of commenting this code out, put an if (DEBUG == 1) {} around it?
The reason I ask is because I'm working with javascript at the moment, and my code is spread across a few .js files. If I create a DEBUG variable in app.js, I'll need to export it from app.js and require it in other files; is this consistent with best practices? Is there a better way to do what I'm thinking of?
There are many ways to do this. Most logging libraries will have levels that allow you to only output or see messages whose levels are above some minimum. Alternatively, if you're just using console.log or console.debug and content to keep those in lieu of more robust log streams, you can change the behavior of these by using your own logging library; for example, if you have a debug.js file that exports your debug() function, import/require it once in each other file and just call debug() instead of console.debug() (or you can actually reassign console.debug = debug but that will have potential side effects in any dependencies or dependent code).
In debug.js, your function can simply check an environment variable (in node.js or similar) or global variable (in the browser) or even a hard-coded flag, and immediately return (doing nothing) if you're in production or not in the mood to print debug messages.
Take a look at bunyan's log levels as an example of how a popular logging library handles this: https://www.npmjs.com/package/bunyan#levels
If you are programming the browser and you want a quick and dirty global variable, you can do window.myVar = 'whatever'.

Integrating Instana error tracking into an Angular 2 application

I'm trying to integrate Instana into an application. More specifically I'm trying to send errors from my Angular app to Instana. I have my code 'working' so this is kind of a question about best practice.
Instana's Backend Correlation documentation defines functions in 'window' from what I understand. I've set something similar to this in my index.html.
<script>
(function(i,s,o,g,r,a,m){i['InstanaEumObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//eum.instana.io/eum.min.js','ineum');
ineum('apiKey', 'someKey');
ineum('traceId', 'backendTraceId'); //replace 'backendTraceId with the appropriate expression for retrieval
</script>
The issue I have is when I try to follow Instana's guide for Angular 2+ Integration regarding error tracking, where they call one of the methods that I can access from window. The guide just straight up calls the function ineum(...) by itself. When I try to do this, my project wouldn't compile.
class CustomErrorHandler implements ErrorHandler {
handleError(error) {
ineum('reportError', error);
// Continue to log caught errors to the console
console.error(error);
}
}
My current fix is: (<any>window).ineum('reportError', errorContext); But I was looking at another stack overflow question where they accessed window differently in their javascript.
Is typecasting window to 'any' a bad practice?
Is it better to try window['ineum'](...) or is that just a syntax preference?
Is it better to try to define functions in another file, say an Instana service of sorts, and then use that service in the index.html script and CustomErrorHandler or keep it in the window? (Though this one may be a little tricker for me to figure out)
Sorry for my confusion as this may not be an actual issue because my code is 'working' but I just want clarification on this. I'm not sure if I just didn't follow Instana's guides correctly but this was the best of what I could find. I tried reaching out via their contact page but I haven't received a response quite yet.
Instana has a demo application that shows to do this.
To summarize the parts that you would need:
Ensure that you have a local directory configured within your TypeScript config that allows definition of custom typings:
// common file name: tsconfig.app.json
{
// other configuration…
"typeRoots": [
// other configuration…
"./custom-typings"
]
}
Declare the global ineum function in a file within this directory:
// common file name globals.d.ts
declare function ineum(s: string, ...parameters: any[]): any;
The combination of these two steps will make TypeScript aware of the function. Now you can use ineum just like any other global.

Can't set values on `process.env` in client-side Javascript

I have a system (it happens to be Gatsby, but I don't believe that's relevant to this question) which is using webpack DefinePlugin to attach some EnvironmentVariables to the global variable: process.env
I can read this just fine.
Unfortunatley, due to weirdnesses in the app startup proces, I need have chosen to do some brief overwritting of those EnvironmentVariables after the site loads. (Not interested in discussing whether that's the best option, in the context of this question. I know there are other options; I want to know whether this is possible)
But it doesn't work :(
If I try to do it explicitly:
process.env.myVar = 'foo';
Then I get ReferenceError: invalid assignment left-hand side.
If I do it by indexer (which appears to be what dotenv does) then it doesn't error, but also doesn't work:
console.log(process.env.myVar);
process.env['myVar'] = 'foo';
console.log(process.env.myVar);
will log undefined twice.
What am I doing wrong, and how do I fix this?
The premise behind this attempted solution was flawed.
I was under the impression that webpack "made process.env.* available as an object in the browser".
It doesn't!
What it actually does is to transpile you code down into literals wherever you reference process.env. So what looks like fetch(process.env.MY_URL_VAR); isn't in fact referencing a variable, it's actually being transpiled down into fetch("http://theActualValue.com") at compile time.
That means that it's conceptually impossible to modify the values on the "process.env object", because there is not in fact an actual object, in the transpiled javascript.
This explains why the direct assignment gives a ref error (you tried to execute "someString" = "someOtherString";) but the indexer doesn't. (I assume that process.env gets compiled into some different literal, which technically supports an indexed setter)
The only solutions available would be to modify the webpack build process (not an option, though I will shortly raise a PR to make it possible :) ), use a different process for getting the Env.Vars into the frontEnd (sub-optimal for various other reasons) or to hack around with various bits of environment control that Gatsby provides to make it all kinda-sorta work (distasteful for yet other reasons).

TypeScript doesn't have window.eval()

In JavaScript I can do:
window.eval(userInput)
in a client-side .js file without any issue.
But in TypeScript, window.eval() does not exist. I get the error:
property eval does not exist on type window
How can I get around this issue?
The reason I want to use eval() is to execute some user created code. The eval call must be done on a global scope because the user code relies on some other code that I have already previously loaded with <script> tags.
There are a couple of ways to do this.
Use type assertion (Type unsafe, but quick and easy):
(window as any).eval("1 + 1");
Or you can modify the window declaration as described in this issue (Type safe)

Timing issues with overriding js functions across files

In my rails project, I've written a javascript function to override the default behaviour of an object in another defined in another file. The line that did this looked something like:
window.someObject.methodToOverride = function...
Initially this would get me a cannot set property 'methodToOverride' of undefined error, which I'm assuming was related to the a timing issue (with someObject not being setup yet). I was able to resolve the issue by chucking it in a jQuery $(document).ready function, but while that works, it seems a bit hacky to me.
Is there a better way to do this?
This sounds like you're looking for require.js. What it does is let you set up your code modularly, defining which module depends on which. In your case, once it's set up, by putting a wrapper such as
require(["someObject"], function (someObject) {
someObject.methodToOverride = function...
}
will make it so when you got to this function, require.js would dynamically load your someObject file, and when it was fully loaded, pass someObject as a parameter into the function you provided. It can work with much more complicated examples, with any level and any number of dependencies, loading them no more than once on an as-needed basis.
You can find a lot more information on require.js all over SO, e.g.:
simple example for using require.js
Require.js nested requires

Categories

Resources