Encoding not recognized in jest.js - node.js

I have a problem testing a project using node-mysql2, react, sequelize and jest. This problem only occurs during testing.
Encoding not recognized: 'cesu8' (searched as: 'cesu8')
at Object.getCodec (project/node_modules/mysql2/node_modules/iconv-lite/lib/index.js:106:23)
at Object.getDecoder (project/node_modules/mysql2/node_modules/iconv-lite/lib/index.js:122:23)
at Object.<anonymous>.exports.decode (project/node_modules/mysql2/lib/parsers/string.js:9:23)
at Packet.Object.<anonymous>.Packet.readNullTerminatedString (project/node_modules/mysql2/lib/packets/packet.js:373:23)
at Function.Object.<anonymous>.Handshake.fromPacket (project/node_modules/mysql2/lib/packets/handshake.js:18:31)
at ClientHandshake.Object.<anonymous>.ClientHandshake.handshakeInit (project/node_modules/mysql2/lib/commands/client_handshake.js:98:38)
at ClientHandshake.Object.<anonymous>.Command.execute (project/node_modules/mysql2/lib/commands/command.js:40:20)
at Connection.Object.<anonymous>.Connection.handlePacket (project/node_modules/mysql2/lib/connection.js:515:28)
at PacketParser.onPacket (project/node_modules/mysql2/lib/connection.js:94:16)
at PacketParser.executeStart (project/node_modules/mysql2/lib/packet_parser.js:77:14)
at Socket.<anonymous> (project/node_modules/mysql2/lib/connection.js:102:29)

This is problem caused by mysql2 doing dynamic lazy require of encodings and Jest not being able to handle this. Have a look at few workarounds users suggested here:
add this snippet to setupTestFrameworkScriptFile
require('mysql2/node_modules/iconv-lite').encodingExists('foo');
or this somewhere early to your code:
import iconv from 'iconv-lite';
import encodings from 'iconv-lite/encodings';
iconv.encodings = encodings;

Really only need to add:
require('iconv-lite').encodingExists('foo')
to the top of whatever file you're testing such as factory.test.js
Not sure why this is unfortunately, but the above is better for copy / pasting than the chosen answer.

In my case, the encoding error appeared after a ReferenceError
The cause was a test calling a method that was making an async database call via mysql2 for which I had forgotten to await.
The solution for me was to simply add await in the right place.
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From models/alert.test.js.
at Object.getCodec (node_modules/mysql2/node_modules/iconv-lite/lib/index.js:63:27)
at Object.getDecoder (node_modules/mysql2/node_modules/iconv-lite/lib/index.js:125:23)
at Object.<anonymous>.exports.decode (node_modules/mysql2/lib/parsers/string.js:10:25)
at Packet.readNullTerminatedString (node_modules/mysql2/lib/packets/packet.js:412:25)
at Function.fromPacket (node_modules/mysql2/lib/packets/handshake.js:62:33) ^
...
Error: Encoding not recognized: 'cesu8' (searched as: 'cesu8')
at Object.getCodec (/Users/.../node_modules/mysql2/node_modules/iconv-lite/lib/index.js:104:23)

I met a similar issue with network node module. I made a workaround by mocking the network module and not using the iconv-lite. The following is my code.
jest.mock('network', () => ({
get_active_interface: cb => {
cb?.(undefined, { type: 'Wired' });
},
}));
As for this case, maybe you could try mock the module that made the call, some code like this.
jest.mock('mysql2',()=>({
someFuncThatCallsIcoveLite:(..._)=>{}
}))

require('../../node_modules/mysql2/node_modules/iconv-lite/lib').encodingExists('foo');
This worked for me on
Node 16.17.1
ts 4.8.4
jest 29.2.2

Related

How to Mock non-existent file import in nodejs Lambda

I am facing issue in testing where My logger is in lambda layer thus non-existing for nodeJs import in lambda.js during mocha-chai testing. I tried mock-fs but getting errors Can not find module /opt/logger.js or maybe I am trying wrong way and not sure if it is useful in this way. Please check below code for reference. Any help or suggestion is most welcome.
lambda.js -
const logger = require('/opt/logger.js') // coming from lambda_layer.js
lambda.test.js -
mock({
"/opt/logger.js": console.log('hello')
});
Was able to accomplish this with mockery. Thanks for allowing me to put this here for everyone. Will update this with better code soon.
mockery.registerMock({'/test/': { destroyAllHumans: actionStub })
mockery.enabled();

React native crypto stream module is undefined

I'm giving a try with [react-native-crypto][1] in order to learn how to convert nodejs to be used in React Native project in the future. Unfortunately, I couldn't get it running successfully. I've faced an issue with stream is undefined. ERROR TypeError: undefined is not an object (evaluating '_$$_REQUIRE(_dependencyMap[0], "stream").Transform.call').
If you have ever faced a similar problem, I'm so grateful for your help.
Also, I attach the screenshot of the issue as the following
For anyone still trying to solve this issue, I have figured out a solution that worked for me. So within node_modules/cipher-base/index.js, the top of the file should have a line which defines the variable Transform as var Transform = require('stream').Transform. For some reason, it does not like the module stream and as such it needs to be changed to readable-stream. Therefore the variable Transform should now read var Transform = require('readable-stream').Transform.
From what I have gathered, the stream module it is trying to refer to isnt actually a module that can be used. The reason why it gets referenced however seems to be because the tsconfig.json file in the root directory specifies "stream": ["./node_modules/readable-stream"] as a path, almost as if to make stream refer to the readable-stream module, which in theory it should refer to when it is called. But in this case it doesnt happen so we need to explicitly define that we are refering to the readable-stream module.
Hope this helps anyone else out there and prevents others scratching their heads for hours on end like it did for me!
I have figured it out by editing in metro.config.js as the following:
resolver: {
extraNodeModules: {
stream: require.resolve('stream-browserify'),
}
},

How to disable Jest `console.log` tags

I have some NodeJS logging done via console.log() internally (its actually loglevel) and as I see it, Jest tags console.log output with console.log ../path/to/string/with/console.log/call:line# for whatever reason:
I haven't found any related options in the docs. How can I disable it?
IMPORTANT:
I had the curiosity to take a look to the answer mentioned in the first answer, wich it says:
Looking at the source code for Jest, it doesn't seem like there is a
neat way to turn those messages off.
And I noticed an update marked on the answer and resolves the problem.
Solution:
Just add this code to your test:
beforeEach(() => {
global.console = require('console');
});
Create a global configuration test file e.g. src/test/config.js, add this line to that file:
jest.spyOn(console, "log").mockImplementation();
add this to jest config:
setupFilesAfterEnv: ['./src/test/config.js']
you can also use that file for global cleanup, before/after each etc
Thanks #Anders Carstensen, I've looked at the answer you mentioned and it says:
Looking at the source code for Jest, it doesn't seem like there is a neat way to turn those messages off.
Not an option for me to write my own console, so I'll just stick with Mocha/Sinon for now.
tslalamam answer code not worked for me, but this one works
1: Create a file with this code (e.g. config.js)
import console from "console"
global.console = console
2: Add this line to your jest.config.js
setupFilesAfterEnv: ["./config.js"]
Before:
After:
Enjoi!

How requiring a module on entry point makes available on other modules on NodeJS?

This is probably a duplicated question, but I couldn't find anything.
Since I'm learning NodeJS, I think that I'm not using the right words to search, so it's hard to find an answer.
Here is the situation:
I'm currently following an online course about NodeJS and coding an API.
In the current step we are using Winston library to log errors. The instructor, have configured on Index,js, which is the entry point of the app, like this:
File: index.js
const winston = require('winston');
const errorHandler = require(./middleware/error.js);
//(...) some other imports
app.use(errorHandler);
winston.add(winston.transports.File,{filename:'logFile.log'});
And in other module we've created in the course to handle errors, he requires winston and simply call to log the error. Something like this:
File: error.js
const winston = require('winston');
function errorHandler(err,req,res,next){
winston.error(err.message,err);
res.status(500).send("something failed");
}
module.exports = errorHandler;
After doing a test, the error is correctly written to the file, and my question is: How it works? How a setting made on the 'required version' of winston at index.js is visible from the other required version at error.js?
From index.js we are importing error.js too, so i can imagine somehow this two modules are sharing this winston object, but again, I don't understand how or where is it shared.
Again, please excuseme if I'm not using the right terms to refer anything here, I'll accept any advice.
Thanks.
When a module is loaded in node.js, it is cached by the require() sub-system. So, when you then require() it again, that means you'll get the exact same module as the previous one.
So ... if you initialized the module after you first loaded it and the module stores some state that represents that intialization, then subsequent use of that module will be using the same (already initialized) module.
And in other module we've created in the course to handle errors, he requires winston and simply call to log the error.
It gets the same instance of the winston module that was already initialized/configured previously.
After doing a test, the error is correctly written to the file, and my question is: How it works? How a setting made on the 'required version' of winston at index.js is visible from the other required version at error.js?
Module caching as describe above. There's only one winston module that all are sharing so if it's initialized/configured in one place, all will use that configuration.

global leak errors in mocha

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.

Resources