Object.assign required modules? - node.js

I'm trying to combine Object.assign with require for module import / export. Example use case:
// foo/bar.js
module.exports = () => console.log('foo')
// foo/biz.js
module.exports = Object.assign({}, require('./bar')
With the above, the object exported from biz.js is always empty. Yet to my eye it looks like it should contain the function exported from bar. Could someone ELI5 why it does not?

module.exports = () => console.log('foo')
You are exporting a function! Not an object with a key. You can try these ways:
Way 1:
// foo/bar.js
module.exports = () => console.log('foo')
// foo/biz.js
module.exports = Object.assign({}, {bar: require('./bar'})
Way 2:
// foo/bar.js
module.exports = {
bar: () => console.log('foo')
}
// foo/biz.js
module.exports = Object.assign({}, require('./bar')

Related

how to refer to a function when unit testing with NodeJS / Mocha

I'm making my first test with Mocha.
Dummy test is passing, but when I want to refer to my actual function that is in another file, it won't find it:
ReferenceError: main is not defined
I have a single index.js with :
async function main() {
function comparer(otherArray) {
return function (current) {
return otherArray.filter(function (other) {
return other.prm === current.prm && other.conso_prod === current.conso_prod
}).length === 0;
}
}
}
module.exports = main();
and in my test.js file, I do:
const {expect} = require('chai');
describe('Sum numbers', () => {
it('Compare 2 existing array', () => {
const meters = []
const perimeter = []
const onlyInMeters = meters.filter(main.comparer(perimeter));
expect(onlyInMeters).to.equal([]);
});
});
But when I refer to main.comparer, it can't find it:
ReferenceError: main is not defined
What am I forgetting? Sorry, I'm a NodeJS Noob!
It seems like you did not import the index.js file in test.js file. You are returning noting from main function as well.
Also, why are you exporting it like module.exports = main(); Instead you can do this:
// index.js
module.exports = {
comparer: (otherArray) => { ... }
}
// test.js
cosnt main = require('PATH_OF_index.js');
main.comparer();

node.js adding new export field without braking existing requires

Consider following simple scenario, file config.js:
const config = {
a: '123'
}
module.exports = config;
and it's usage:
const cfg = require('./conifg');
console.log(cfg.a);
Now I'm in need to add additional export member to config.js:
const config = {
a: '123'
}
function someFunction() {
console.log('blah');
}
module.exports = {
config,
someFunction
};
This modification brakes down so-far working code, because cfg.a in
const cfg = require('./conifg');
console.log(cfg.a);
points now to undefined.
Is there any way to extend module.exports while remaining it's "default" exported member to not brake things down?
you may export all property of config separately
module.exports = {
...config,
someFunction
};
or if you don't want use spread, you can access by
const cfg = require('./conifg');
console.log(cfg.config.a);
You can do it like this
const config = {
a: '123',
someFunction: someFunction
}
function someFunction() {
console.log('blah');
}
module.exports = config;

Testing ScrollIntoView in Jest

With the function which uses scrollIntoView
export const scrollDown = () => {
document.querySelector('.bottom').scrollIntoView({
behavior: 'smooth'
});
}
I have a test here that goes like this
describe('scrollDown', () => {
let scrollIntoViewMock = jest.fn();
window.HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
scrollDown()
expect(scrollIntoViewMock).toBeCalled();
})
But the test is failing with the TypeError: Cannot set property 'scrollIntoView' of undefined
The test was from another SO answer for scrollIntoView testing question. Any help would be appreciated.
You need to add an HTMLElement with class bottom to the document:
const scrollDown = () => {
document.querySelector('.bottom').scrollIntoView({
behavior: 'smooth'
});
}
test('scrollDown', () => {
document.body.innerHTML = '<div class="bottom"></div>';
const scrollIntoViewMock = jest.fn();
HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
scrollDown();
expect(scrollIntoViewMock).toBeCalledWith({ behavior: 'smooth' }); // Success!
})

why module.export { ...require('module') } work but not module.export { require('module') }

justExport.js
const first = () => {
console.log('frist from justExport')
}
const second = () => {
console.log('second fromt justExport')
}
module.exports = {
first,
second,
}
tmp.js
module.exports = {
...require('./justExport') // work
require('./justExport') // SyntaxError: Unexpected string
}
main.js
const justExport = require('./justExport.js')
const tmp = require('./tmp.js')
console.log('Hello World!')
I have voluntarily create a fake example with the less possible code.
{ ...require('./justExport') } is object literal spread. While { require('./justExport') } is incorrect object literal syntax because it doesn't contain a key.
Unless the intention is to create shallow copy of justExport module, object literal isn't needed. It can be:
module.exports = require('./justExport');
To further clarify the answer from #estus, note that the following works due to ES6 shorthand property names:
const justExport = require('./justExport');
module.exports = {
...justExport, // works
justExport // works because key is implicitly defined by variable name
}

Mock.mockImplementation() not working

I have a service class
Service.js
class Service {
}
export default new Service();
And I am trying to provide a mock implementation for this. If I use something like this:
jest.mock('./Service', () => { ... my mock stuff });
It works fine, however I'm not able to access any variables declared outside of the mock, which is a bit limiting as I'd like to reconfigure what the mock returns, etc.
I tried this (inspired by this other StackOverflow article: Service mocked with Jest causes "The module factory of jest.mock() is not allowed to reference any out-of-scope variables" error)
import service from './Service';
jest.mock('./Service', () => jest.fn);
service.mockImplementation(() => {
return { ... mock stuff }
);
Unfortunately when I am trying to run this, I get the below error:
TypeError: _Service2.default.mockImplementation is not a function
I had same problem as #Janos, the other answers didn't help either. You could do two things :
If you need to mock only a function from Service, in your test file:
import service from './Service';
jest.mock('./Service', () => jest.fn());
service.yourFunction = jest.fn(() => { /*your mock*/ })
 
If you need to mock the entire Module:
Say your service.js is in javascript/utils, create a javascript/utils/_mocks_ and inside it create a service.js file, you can then mock the entire class in this file, eg:
const myObj = {foo: "bar"}
const myFunction1 = jest.fn(() => { return Promise.resolve(myObj) })
const myFunction2 = ...
module.exports = {
myFunction1,
myFunction2
}
then in your test file you just add:
jest.mock('./javascript/utils/service')
...functions exported from the mockfile will be then hit through your test file execution.
The mock is equal to jest.fn. You need to call jest.fn to create a mocked function.
So this:
jest.mock('./Service', () => jest.fn);
Should be:
jest.mock('./Service', () => jest.fn());
ran into similar issues and resolved it by using .mockImplementationOnce
jest.mock('./Service', () => jest.fn()
.mockImplementationOnce(() => {
return { ... mock stuff }
})
.mockImplementationOnce(() => {
return { ... mock other stuff }
})
);
now when you run another test it will return the second mock object.
You need to store your mocked component in a variable with a name prefixed by "mock" and make sure you return an object with a default property as you import your Service from the default in your "main.js" file.
// Service.js
class Service {
}
export default new Service();
// main.test.js (main.js contains "import Service from './Service';")
const mockService = () => jest.fn();
jest.mock('./Service', () => {
return {
default: mockService
}
});
I had similar problem, and the cause was that ".spec.js" file had an
import jest from "jest-mock";
After removing this line, it worked.
My mistake was that I was resetting the mock before each test. If you do that, be sure to reconfigure the mock implementation.
For example, change this:
let value;
let onPropertyChange: OnPropertyChangeCallback = jest.fn((changes: any) => {
value = changes["testValue"];
});
const user = userEvent.setup();
beforeEach(() => {
jest.resetAllMocks();
});
to this:
let value;
let onPropertyChange: OnPropertyChangeCallback;
const user = userEvent.setup();
beforeEach(() => {
jest.resetAllMocks();
onPropertyChange = jest.fn((changes: any) => {
value = changes["testValue"];
});
});

Resources