global leak errors in mocha - node.js

I was trying to unit test the apple push notification library when I got a global leak error trying to open up an APN connection.
Is that a configuration error on my part or an error in node-apn or mocha?
I'm not sure I understand what checkGlobals is doing... is it just checking to see if any global variable are being set?
0) Feed "before all" hook:
Error: global leak detected: hasCert
at Runner.checkGlobals (/usr/lib/node_modules/mocha/lib/runner.js:96:21)
at Runner.<anonymous> (/usr/lib/node_modules/mocha/lib/runner.js:41:44)
at Runner.emit (events.js:64:17)
at /usr/lib/node_modules/mocha/lib/runner.js:159:12
at Hook.run (/usr/lib/node_modules/mocha/lib/runnable.js:114:5)
at next (/usr/lib/node_modules/mocha/lib/runner.js:157:10)
at Array.<anonymous> (/usr/lib/node_modules/mocha/lib/runner.js:165:5)
at EventEmitter._tickCallback (node.js:126:26)

Yes, Mocha features a global leak detection mechanism which alerts and fails if your code under test introduces global variables.
If hasCert is declared in a library and you have no control over its creation, you can tell Mocha to ignore it.
On the command line,
$ mocha --globals hasCert
To quote the documentation:
[This option] accepts a comma-delimited list of accepted global variable names. For example suppose your app deliberately exposes a global named app and YUI, you may want to add --globals app,YUI.
In a browser:
mocha.setup({globals: ['hasCert']});

You can also disable global leak detection by passing:
mocha --ignore-leaks
In a browser:
mocha.setup({ignoreLeaks: true});

I ran into this problem as well, you probably forgot a var statement somewhere like I did, which in JS means that a global variable will be created.
You may have to hunt it down yourself depending on how you structured your app, and hopefully it's not a 3rd-party bit of code that's causing this. :P
You should use JSLint or JSHint through your project, they should help uncover the source if it's anywhere in your code.

This can also happen if you forget new in a call to a constructor. In that case, this is the global object so any properties introduced in the constructor will be added to the global object.
That problem should not go undetected for long, but it's an interesting test failure.

I came across this answer when I trying to figure out how to squelch JSONP leaks such as:
Error: global leak detected: jQuery20305777117821853608_1388095882488
Squelch jQuery JSONP "leaks" via:
mocha.setup({
globals: ['jQuery*']
});

I was encountering this error for many functions as follows:
1) test "before all" hook:
Error: global leaks detected: __timers, _document, history, addEventListener, removeEventListener, dispatchEvent, raise, __stopAllTimers, Image, _virtualConsole, run, getGlobal, dispose, top, parent, self, frames, window, _frame, $, jQuery, Handlebars, Ember, Em, MetamorphENV, Cloud, jQuery1102048038746835663915, _listeners, _length, length, document, location, close, getComputedStyle, navigator, name, innerWidth, innerHeight, outerWidth, outerHeight, pageXOffset, pageYOffset, screenX, screenY, screenLeft, screenTop, scrollX, scrollY, scrollTop, scrollLeft, alert, blur, confirm, createPopup, focus, moveBy, moveTo, open, print, prompt, resizeBy, resizeTo, scroll, scrollBy, scrollTo, screen, mapper, mapDOMNodes, visitTree, markTreeReadonly, INDEX_SIZE_ERR, DOMSTRING_SIZE_ERR, HIERARCHY_REQUEST_ERR, WRONG_DOCUMENT_ERR, INVALID_CHARACTER_ERR, NO_DATA_ALLOWED_ERR, NO_MODIFICATION_ALLOWED_ERR, NOT_FOUND_ERR, NOT_SUPPORTED_ERR, INUSE_ATTRIBUTE_ERR, INVALID_STATE_ERR, SYNTAX_ERR, INVALID_MODIFICATION_ERR, NAMESPACE_ERR, INVALID_ACCESS_ERR, exceptionMessages, DOMException, NodeList, DOMImplementation, Node, NamedNodeMap, AttributeList, Element, DocumentFragment, Document, Attr, EventException, Event, UIEvent, MouseEvent, MutationEvent, EventTarget, languageProcessors, resourceLoader, HTMLCollection, HTMLOptionsCollection, HTMLDocument, HTMLElement, HTMLFormElement, HTMLLinkElement, HTMLMetaElement, HTMLHtmlElement, HTMLHeadElement, HTMLTitleElement, HTMLBaseElement, HTMLIsIndexElement, HTMLStyleElement, HTMLBodyElement, HTMLSelectElement, HTMLOptGroupElement, HTMLOptionElement, HTMLInputElement, HTMLTextAreaElement, HTMLButtonElement, HTMLLabelElement, HTMLFieldSetElement, HTMLLegendElement, HTMLUListElement, HTMLOListElement, HTMLDListElement, HTMLDirectoryElement, HTMLMenuElement, HTMLLIElement, HTMLCanvasElement, HTMLDivElement, HTMLParagraphElement, HTMLHeadingElement, HTMLQuoteElement, HTMLPreElement, HTMLBRElement, HTMLBaseFontElement, HTMLFontElement, HTMLHRElement, HTMLModElement, HTMLAnchorElement, HTMLImageElement, HTMLObjectElement, HTMLParamElement, HTMLAppletElement, HTMLMapElement, HTMLAreaElement, HTMLScriptElement, HTMLTableElement, HTMLTableCaptionElement, HTMLTableColElement, HTMLTableSectionElement, HTMLTableRowElement, HTMLTableCellElement, HTMLFrameSetElement, HTMLFrameElement, HTMLIFrameElement, StyleSheet, MediaList, CSSStyleSheet, CSSRule, CSSStyleRule, CSSMediaRule, CSSImportRule, CSSStyleDeclaration, StyleSheetList, VALIDATION_ERR, TYPE_MISMATCH_ERR, UserDataHandler, DOMError, DOMConfiguration, DOMStringList, XPathException, XPathExpression, XPathResult, XPathEvaluator, DocumentType, CharacterData, ProcessingInstruction, Comment, Text, NodeFilter, _parser, _parsingMode, _augmented
So I passed a wildcard in the setup function and it solved my issue.
mocha.setup({
globals: ['*']
});

I added "mocha.globals(['browserSync']);" below to fix my problem. The rest of the code is from https://mochajs.org/ - section : RUNNING MOCHA IN THE BROWSER
<script>mocha.setup('bdd')</script>
<script src="basic-spec.js"></script>
<script>
mocha.checkLeaks();
mocha.globals(['jQuery']);
mocha.globals(['___browserSync___']); //<<== This line was added
mocha.run();
</script>

Define Your stub variables before you use it.
var hasCert;
var hasCert = sinon.stub(instance, method);

Just for the record that ignoreLeaks option has been deprecated since Mocha 7.0.0;
We should use mocha.setup({ checkLeaks: false }) instead.

Related

Use a top-level await, if supported by the current runtime

Top-level await support was added to Node.js in 14.3.0 via --experimental-top-level-await and later to --harmony-top-level-await.
The Problem
I need to use a top level await in my ESM script file, if it is supported by the current Node.js runtime. And further, I need to set a boolean flag to indicate that the promise was successfully awaited at the top level.
An example of what I mean:
let topLevelAwaitEnabled;
try {
await Promise.resolve(); // replaced with an actual promise
topLevelAwaitEnabled = true;
} catch (ignored) {
topLevelAwaitEnabled = false;
}
console.log(topLevelAwaitEnabled);
// carry on with the rest of the application, regardless of success or failure
// at some point, topLevelAwaitEnabled is checked to conditionally execute some code
If top level await support is enabled, this succeeds fine. However, if it is not supported, this will result in the following error during parsing and cannot be caught at runtime with a try/catch:
$ node test.js...\test.js:3
await Promise.resolve(); // replaced with an actual promise
^^^^^
SyntaxError: await is only valid in async function
So the question is: How can I use a top level await if it is supported, without creating incompatibility issues with Node.js runtimes that do not support top level await (either no CLI flag was specified or simply no runtime support)?
If the answer is "it is not possible", I would like an explanation as to why this is impossible.
In the case I am actually committing an XY problem, the underlying issue is I need a top-level dynamic import.
Note: I am well aware that top level await is not recommended for a variety of reasons, however it is crucial for a specific functionality of my application and does not impose any issue with my use case. Alternatives will likely not suffice.
Attempts
I have tried the following methods, to no avail:
eval: I have tried replacing the await line with an eval("await Promise.resolve()"), in the hope the code was evaluated in the current context. Unfortunately, even if top level await is supported, this will result in the same error, as it does not seem to inherit the current context.
vm.compileFunction: Same issue was eval(), top level await is not supported.
vm.SourceTextModule: Evaluation is asynchronous and would need to be awaited at the top level to check if it is supported... which is a catch 22.
conditional execution of the await based on process.version and process.execArgv: The error during parsing - it never actually executes the code, so conditional execution is ruled out.
As far as I know this is not possible because the parser will simply error out. The compiler will not understand the await directive and will not complete its cycle. This is probably similar to using a word that's simply not a recognized keyword.
The closest you can get is using an anonymous function.
Seems like you might be able to check the version of node being used at runtime like so process.version and then you can use also use process.argv to check for any flags passed in when starting the process.
Using the above methods, you can first check the version of node being used, and then if relevant, you can check for the needed flags.
Node docs on checking for CLI args.
(async()=>{await promise})()

React FacebookAuth Error: FB.login() called before FB.init()

I'm using the admin-on-rest npm package starter project and trying to plug in a simple SSO Facebook login button using the FacebookAuth npm package. Every time I try to click the "Login" button, I get the following error:
FB.login() called before FB.init()
I'm using an .env file with the following variable: REACT_APP_FACEBOOK_APP_ID and setting it to the right value. I even did console.log() within my app and can see it output.
I checked and I'm only loading the FB SDK once, not multiple times (this was a common issue reported on other threads).
Ok, it turned out to be something pretty dumb, but something to point out nonetheless!
In my .env file, I had accidentally placed a semicolon (;) at the end of the declaration, like this:
REACT_APP_FACEBOOK_APP_ID = XXXXXXXXXXXX;
Apparently .env files do NOT like semi-colons. This was just very difficult to figure out from the error above.
So if any of you want to pull your hair out because of this issue, and you're using similar tech, check to make sure you're syntactically kosher EVERYWHERE variables are being declared.
the funny thing was i forgot to replace your-app-id with my app id:
<script>
FB.init({
appId: 'your-app-id',
autoLogAppEvents: true,
xfbml: true,
version: 'v8.0'
});
</script>

EventEmitter memory leak detected with node-rest-client

During the execution of my mocha testsuit, the following warning is reported by Node.js:
(node) warning: possible EventEmitter memory leak detected. 11 error listeners a
dded. Use emitter.setMaxListeners() to increase limit.
Trace
at Object.addListener (events.js:179:15)
at new exports.Client (C:\xyz\node_modules\node-rest-client\lib\node-rest-
client.js:320:17)
at Context.<anonymous> (C:\xyz\test\backend\rest\resources.js:40:10)
...
I strongly suspect this has to do with node-rest-client module, that I use. The last showed line, indicated in the warning, is actually:
rest = new Client();
If I execute only a single testcase which throws this warning, the warning does not shows up. It happens only when I execute the whole testsuite, with around 15 new Client() lines.
I have not found a way to somehow close the rest client, so I now tried simply with:
delete rest
It did not help to remove the warning.
Any clues?
This isn't necessarily a problem, more a warning of the system to make sure that you know what you're doing.
Because of the internals of node-rest-client you can't change it specifically for that module (see also this issue), but to get rid of the warning during your tests, put this somewhere at the top of your code:
require('events').EventEmitter.defaultMaxListeners = Infinity;
More information here.

How to detect if a mocha test is running in node.js?

I want to make sure that in case the code is running in test mode, that it does not (accidentally) access the wrong database. What is the best way to detect if the code is currently running in test mode?
As already mentioned in comment it is bad practice to build your code aware of tests. I even can't find mentioned topic on SO and even outside.
However, I can think of ways to detect the fact of being launched in test.
For me mocha doesn't add itself to global scope, but adds global.it.
So your check may be
var isInTest = typeof global.it === 'function';
I would suggest to be sure you don't false-detect to add check for global.sinon and global.chai which you most likely used in your node.js tests.
Inspecting process.argv is a good approach in my experience.
For instance if I console.log(process.argv) during a test I get the following:
[
'node',
'/usr/local/bin/gulp',
'test',
'--file',
'getSSAI.test.unit.js',
'--bail',
'--watch'
]
From which you can see that gulp is being used. Using yargs makes interpretting this a whole lot easier.
I strongly agree with Kirill and in general that code shouldn't be aware of the fact that it's being tested (in your case perhaps you could pass in your db binding / connection via a constructor?), for things like logging I can see why you might want to detect this.
Easiest option is to just use the detect-mocha [NPM package.
var detectMocha = require('detect-mocha');
if(detectMocha()) {
// doSomethingFancy
}
If you don't want to do that, the relevant code is just
function isMochaRunning(context) {
return ['afterEach','after','beforeEach','before','describe','it'].every(function(functionName){
return context[functionName] instanceof Function;
})
Where context is the current window or global.
I agreed with #Joshua on his answer, he says Inspecting process.argv is a good approach in my experience.
So, I've written a simple detecting mocha code.
const _MOCHA_PATH = new RegExp('(\\\\|/)node_modules\\1mocha\\1bin\\1_mocha$');
var isMochaRunning = process.argv.findIndex(arg => _MOCHA_PATH.test(arg)) > -1;
In a small project with no logging infrastructure, I use
if (process.env.npm_lifecycle_event !== 'test')
console.error(e);
to avoid logging expected errors during testing, as they would interfere with test output.

How do I fix the Implicit textOnly message that appears when starting an Express server?

When starting Express server with node app.js I see the following message displayed:
Implicit textOnly for script and style is deprecated. Use script. or style. instead.
What does this message mean and how can I fix it?
This issue and this pull request discuss the message in detail. Basically you will soon have to tell Jade explicitly that a script is text only. The current method of:
script
var a = 2;
is deprecated. It will be replaced with:
script.
var a = 2;
So to fix this message you need to explicitly show that the block is text by adding a dot after script or style
As stated here this is being removed to make Jade easier to learn and to permit the use of Jade code blocks in the block.
As I mentioned above, this applies to implicit style as well.

Resources