Sinon and eslint - javascript

I am writing unit tests for my angular app with Karma, Jasmine, and Sinon and I run eslint over my code base.
I define global variables that will be used in the beforeEach inject to create a sinon.stub. ESLint keeps complaining that my global variables are defined but never used. For example:
'getListStub' is defined but never used no-unused-vars
but in my code it looks like this
var listService, getListStub;
beforeEach(inject(function(_listService_) {
listService = _listService_;
getListStub = sinon.stub(listService, 'getList').returns(q.when(listResponse));
}
What is the best way to stop ESLint from producing errors?
Is setting /*eslint no-unused-vars: 0*/ at the top of those testing files best?

If you aren't using getListStub anywhere, why are you assigning it to a variable?
The properties of JS closure and memory management (specifically, holding referenced objects) will allow you to use _listService_ directly and you shouldn't need to cache getListStub.
If that does work correctly with sinon, you should be able to rewrite your function as:
beforeEach(inject(function(_listService_) {
sinon.stub(_listService_, 'getList').returns(q.when(listResponse));
}

Related

Variable is not defined for module library

I've been refactoring my App.js file and taking some functions out into a file called utilities.js. Each function has an export and my App.js has the requisite import. However, I am getting some 'myVariable' is not defined errors for the utilities.js file. I figure this is because I am declaring the variable outside of the App() function in my App.js file, and so if you look at the utilities.js file in isolation, the variables haven't been declared. This pieceTable variable is also used in other functions in App.js.
How do you deal with this situation?
Example:
utilities.js file
export const findPieces = piece => {
pieceTable = [];
// blah blah
}
App.js file
import {findPieces} from './utilities'
let pieceTable=[];
function App() {
const findDraught = findPieces('King')
}
I get a "Failed to compile" error for this type of scenario, saying 'pieceTable' is not defined in utilities.js
If you actually want a global
When you say let pieceTable=[]; you are explicitly making pieceTable a variable with scope local to the module, i.e. not a global.
If you had just referred to pieceTable you would be assigning a property to the top-level scope object which is window (in a browser JavaScript context). You can refer to it explicitly with: window.pieceTable.
(FYI, in node.js it would be global.pieceTable. Read why.)
Globals are passé and I don't recommend using one in your case.
If you don't want a global, but you still want modular design
Choose an owning module for pieceTable and have that module export it. Then have every other module that needs to refer to pieceTable import it. This is making use of the module mechanism rather than the global mechanism.
If you want pure design
Design your functions so that they are passed in the value of pieceTable (or an object that has pieceTable as a property) as an argument to the function rather than relying on getting a singleton or global. This will make your functions easier to test as well and eliminate confusing global side effects.
My functions are not pure. I need to pass in the pieceTable as an argument when calling it, and it avoids all these issues. Don't have my utilities.js file rely on global variables in another file. Pass in arguments to these utility functions instead.
Well it's not defined in utilities.js because there's not a let or const.
If you don't want to redefine it in the utilities function, then you need to pass in the pieceTable when you call findPieces.

Testing JavaScript without exports using the Jest testing framework

I am in the process of moving an old JS codebase to modern JS. First step though is to add some tests to the current codebase. The current code base is essentially a bunch of individual JS files each wrapped in an IIFE.
This in itself is a problem for tests because, unless something is exposed to the global object, you cannot reach into the IIFE. Some of the code I am refactoring to be simple JS object with properties, which is attached to a namespace(namespace below is just a placeholder name) on the global object, for example:
var namespace = window.namespace || {};
var paymentsHandlerUtils = {
getNewValue: function(selectedAmount) {
'use strict';
return selectedAmount < 1 || isNaN(selectedAmount)
? ''
: '$' + selectedAmount;
},
getSelectedAmount: function(value) {
'use strict';
return value % 1 === 0 ? parseInt(value) : parseFloat(value).toFixed(2);
}
};
namespace.paymentsHandlerUtils = paymentsHandlerUtils;
My question is, how would you go about testing this with Jest? I have tried requiring the above as follows:
const paymentsHandlerUtils = require('../js/components/payments/payments-handler-utils.js');
This runs, but the paymentsHandlerUtils object is just an empty {}. Not surprising, as nothing is being returned by simply executing the JS. However, window.namespace is also undefined. Seems like the code is not being executed in the context of jsDOM, so the global(s) is not created.
Is there a way to get this to work, or is this simply not a use case for Jest? Thanks in advance.
I don’t think there is a way to access module globals when imported as it goes against the principle of encapsulated modules in the first place.
An alternative which would require the least refactoring would be to add the following code to all your modules:
if (typeof exports === "object") {
module.exports = paymentsHandlerUtils;
}
namespace.paymentsHandlerUtils = paymentsHandlerUtils;
It is inspired by the old UMD (Universal Module Definition). The condition detects if you are running in a commonjs environment and exports your variable. You will then be able to require it in your tests:
const paymentsHandlerUtils = require('../js/components/payments/payments-handler-utils.js');
Otherwise you need to use another test suite that works in the browser, because Jest doesn’t.
Good luck to your migrations!
In fact this is now possible with rewire. Here's what you have to do:
install rewire (npm i rewire)
in your jest file,
const rewire = require("'rewire');
const paymentsHandlerUtilsRewire = rewire('../js/components/payments/payments-handler-utils.js');
const namespace = paymentsHandlerUtilsRewire.__get__('namespace');
do what you were planning to do with namespace and namespace.paymentsHandlerUtils
Read more in the rewire repo; there's also babel-plugin-rewire for ES6+ (see, for instance, here).

How to determine if JEST is running the code or not?

I am creating a JS test on my react-native project. I'm specifically using firebase for react native, in which I would like to replace firebase instance with a mockfirebase instance if JS is running the code of my class.
For example I have class setup like below.
import firebase from 'react-native-firebase';
class Database() {
/// use the firebase instance
}
I'd like to have a check if jest is the running environment then I'd replace the import line with appropriate mock class.
jest sets an environment variable called JEST_WORKER_ID so you check if this is set:
function areWeTestingWithJest() {
return process.env.JEST_WORKER_ID !== undefined;
}
I also see that if NODE_ENV is not set the jest CLI sets it to the value 'test'. This might be another way to check.
I usually have NODE_ENV=development set globally on my shell. This works for me:
typeof jest !== 'undefined'
(note that global.jest and 'jest' in global don't work, as this doesn't seem to be a global variable, just a value made available on all modules much like node's require or __filename)
you could add parameter to global for example global.isJest and check on the front end if it is defined
For me best way is checking two things - 0 and undefined:
[0, undefined].includes(process.env.JEST_WORKER_ID)
so it's based on https://stackoverflow.com/a/52231746/3012785

Mock doesn't work, while reassignment does

I have the following Jest/Enzyme test:
const mockCheckMyFunctionality = jest.fn();
jest.mock('../modules/MyFunctionality', () => ({
checkMyFunctionality: mockCheckMyFunctionality
}));
const wrapper = shallow(
<App initialProps={mockInitialProps} />
);
expect(mockCheckMyFunctionality).toHaveBeenCalledTimes(1);
This will fail with TypeError: Cannot read property 'onNextTick' of undefined. The error message itself is not relevant, but it just shows that the real MyFunctionality.checkMyFunctionality is called instead of mockCheckMyFunctionality.
However, if I replace:
jest.mock('../modules/MyFunctionality', () => ({
checkMyFunctionality: mockCheckMyFunctionality
}));
With:
MyFunctionality.checkMyFunctionality = mockCheckMyFunctionality;
The test will pass, showing that mockCheckMyFunctionality is actually called. However, this is hacky and fails EsLint checking.
The method I am testing is just this:
setupMyFunctionality() {
checkMyFunctionality(this.props.something);
}
How can I modify the mocking such that it is visible inside App?
Reassignment seems to work but mocking doesn't.
Maybe you forgot to create a manual mock for the MyFuncionality module.
As in the documentation for mocking a module:
Manual mocks are defined by writing a module in a __mocks__/
subdirectory immediately adjacent to the module. For example, to mock a module called user in the models directory, create a file called user.js and put it in the models/__mocks__ directory.
...
When a manual mock exists for a given module, Jest's module system will use that module when explicitly calling jest.mock('moduleName').
If that's the case, you can create a folder named __mocks__ adjacent to the MyFunctionality file, then create a MyFunctionality file within that folder, containing the mock implementation as in the code snippet, and explicitly call the mock by calling jest.mock('../modules/MyFunctionality') before the tests.
Another solution, which I started using in my projects, is passing the dependencies as props to the components. To implement that you could change the App component and pass the MyFunctionality as props, thus in the above test you would need just to pass the mock implemented as props, avoiding the necessity of creating __mocks__/ folder to accomplish the test.

Use function from the main.js in imported module

I'm trying to include IOUtil.js and ChannelReplacement.js in my add-on, using the Cu.import(...) function. These two both use xpcom_generateQI, which I'm trying to obtain from the XPCOM jsm, but the two scripts cant access it.
const {Cc, Ci, Cu, Cr} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const xpcom_generateQI = XPCOMUtils.generateQI;
Cu.import(self.data.url("IOUtil.js"));
Cu.import(self.data.url("ChannelReplacement.js"));
gives me xpcom_generateQI is not defined.
How do I access a function which is defined in main.js?
Issues
Don't use Cu.import for local SDK modules. Don't write JS code modules for SDK add-ons, the SDK uses CommonJS-style modules together with the require() facility which also comes with proper cleanup for free, which cannot be said for JS code modules and Cu.import (you'd need to properly Cu.unload everything and likely kill some references yourself).
That https-everywhere stuff are neither JS code modules nor SDK modules, but uses the subscript loader. Either convert it to SDK code modules, or use the subscript loader yourself.
It is OK to import built-in JS Code modules in different scopes/modules. There is not actually a need to make available xpcom_generateQI from main (although it can be done; well, get to that).
To be future proof, you should bind your xpcom_generateQI shortcut properly, as in XPCOMUtils.generateQI.bind(XPCOMUtils). Otherwise, if the implementation changes and requires a proper this, your stuff will break.
To export something from any CommonJS module, you need to put it into the exports module. See the first link.
To import something, use require() (first link again).
Be aware of circular references, where Module A imports Module B imports Module A. Right now this kinda works (but only kinda, because some stuff might not be available from Module A when Module B imports it like this, as Module A is not fully loaded). Better avoid it.
Example 1 (circular)
So here is a example with circular require (main imports modules imports main)
main.js
function someFunction() {
console.log("some function called");
}
exports.someFunction = someFunction;
var mod = require("./module");
mod.anotherFunction();
module.js
const { someFunction } = require("./main");
exports.anotherFunction = function() {
someFunction();
}
Now, because of circular references this is a fragile construct. If works right now, but when modules get more complex or the SDK changes, it might break... Better put someFunction into a third module.
Example 2 (avoiding circular imports)
main.js
var mod = require("./module");
mod.anotherFunction();
// Or call someFunction directly
var { someFunction } = require("./utils");
someFunction();
module.js
const { someFunction } = require("./utils");
exports.anotherFunction = function() {
someFunction();
}
utils.js
function someFunction() {
console.log("some function called");
}
exports.someFunction = someFunction;
There are no circles anymore. If you wanted to reuse xpcom_generateQI, you'd put it as a property of exports in utils.js (in this example) and later require it with require("./utils").
https-everywhere
The https-everywhere stuff needs to be either converted or loaded using the subscript loader. I would recommend against the subscript loader, because in all likelihood the verbatim https-everywhere code does not clean up after itself. I'd actually also recommend against just converting it by throwing some stuff in (exports.xzy = ...). This code is not meant to be run in the SDK. Better create your own implementation and just borrow ideas from https-everywhere where needed.

Categories