How to test a method with setTimeout and jquery using jest - javascript

I'm struggling a bit to find a solution on how to test this exported function with jest.
export const scrollToError = () => {
setTimeout(() => {
const hasErrorElement = jQuery('.has-error');
if (!hasErrorElement.length) return;
jQuery('html,body').animate({
scrollTop: hasErrorElement.offset().top - 50,
}, 'slow');
}, 400);
};
I imported it in my test file and tried to start it:
import { scrollToError } from './utils';
describe('Utils', () => {
it('should scroll to error', () => {
const result = scrollToError();
expect(result).toBe(true); //added this just to force an error and got result as undefined
});
});
Could anyone give me any tips on how to test code with these dependencies?

scrollToError() is asynchronous function and you can't invoke it and expect the result to be there immediately. You need to wait that amount of ms (400 in your case), before testing for it.
Asynchronous code is tested a bit differently in Jest: Testing Asynchronous Code. You can also take control over the timers or combine it all with the manual mocks and override jQuery itself.

How are you using jQuery?
I mean, did you get it using npm or yarn? to mock node_modules you can follow this link: https://jestjs.io/docs/en/manual-mocks#mocking-node-modules
Otherwise, you will have to create a manual mock. You can see how to do it here: https://jestjs.io/docs/en/manual-mocks
Updated:
the simplest way is to override it, is while settting up your test at beforeXXX method.
You can simply put something like window.JQuery = jest.fn();
this is the simplest mock ever but you will have to create the methods like animate and other jquery related methods.
Having second thoughts here and looking to your function, if you mock jQuery what else left to be tested?
If you mock, you will be testing if your fn are doing the steps you defined here. Like check if the jQuery fn was called with .has-error class or if animate received the correct parameters.
This kind of test doesn't help you at all, it's just checking if it's following line by line your algorithm. The problem here, that you could do some refactorings like changing the .has-error class name or the animate method by other improved one.
What you really need to change, if it's doing at the end what should be doing. Displaying the div or whatever that should be displayed. If you test that, regardless the way you refactor your code the test will check if the final solution still works and that what matters.
Was I clear? English is not my first language so, it may be a little bit confusing

I finally managed to find a proper solution.
I wrote three test cases for it:
jest.useFakeTimers();
describe('utils', () => {
afterEach(() => {
document.body.innerHTML = '';
});
it('ScrollToError - should run the settimeout for 400 ms', () => {
scrollToError();
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 400);
});
it('ScrollToError - should scroll to error', () => {
document.body.innerHTML = formStep1ErrorMock;
window.setTimeout = fn => fn();
const result = scrollToError();
expect(result).toBe(true);
});
it('ScrollToError - should do nothing as has no errors', () => {
document.body.innerHTML = formStep1Mock;
window.setTimeout = fn => fn();
const result = scrollToError();
expect(result).toBe(true);
});
});
So basically, first I check if the setTimeout was called with the proper amount of seconds (not that it's important).
Then I mock the setTimeout by doing this window.setTimeout = fn => fn(); so it runs without waiting for the delay. I also mock the html with the proper details I need it to have.
And finally, I just cover another scenario.
PS: I added a return true statement to the scrollToError method to make it simpler to have an expected result.
This way I achieved 100% coverage for this method.

Related

Nodejs writable stream.write method creates a file but doesn't write in it

I tried with versions, 10/14/16 and I'm using a mac.
My code base is quite complex, so I tried out something simple as below, yet nothing changed.
Is there something, I'm missing out?
`
const fs = require('fs');
const readableStream = fs.createReadStream('file.txt', 'utf8');
const writableStream = fs.createWriteStream('file-2.txt');
readableStream.on('open', function () {
console.log('opening file')
readableStream.on('data', (chunk) => {
console.log(chunk)
writableStream.write('hey there','utf8', () => {
console.log('Im done writing!')
});
});
writableStream.end();
})
`
By the way, as expected I get no errors and the process exit quietly.
I tried the simple snippet above, expecting the file-2.txt to be created and the text "hey there" to be write on it.
Indeed the file is created but, it's empty.
Like I said before, I tried changing node versions(10/14/16) but same results.
I don't know what seems to cause this unexpected result.
Any help will be appreciated.
I think i figured out what the issue was. I must admit that, it's quite ridiculous :)
The problem was with the following line getting executed way before the write(filePath) has the time to finish creating and adding the chunk to the newly created file.
There are 2 ways to solve this problem, at last as far as I can tell.
1- Remove the line:
writeStream.end()
2- Added a callback that is called after writing is completed and call end
in the callback like so:
writeStream('sometext', () => {
done writing....
writeStream.end() })
3- Add a 'finish' event listener to the writeStream and to it's callback
function, call the end() like so
writeStream('finish', () => {
writeStream.end() })
Hope that helps some novice like myself out there!

How to run an inline-function twice?

I'm using Gametime.js to make a world chat in real time.
Messages are stored in a database.
Gametime.js uses PubNub and for some reason PubNub needs the message sent twice so it actually does it.
How can I make a function run twice?
I've tried this:
for (let i = 0; i < 2; i++) { gametime.run("msg", [msg]) }
And it works, it's just that I do this very often in my script, so is there a way to do it without a for/while loop?
Here's an example of what I'd like to achieve:
// inline code, cannot run for loop right here
function example(msg) { doSomething(), doSomethingElse, {{run twice}}, done() }
You can create another function which will run your function twice, like this:
function twice (callback) {
callback();
callback();
}
twice(() => console.log('hello'));
However, if you're experiencing a scenario where you are having to invoke a function twice to get the desired result, it sounds like there's another problem somewhere (in some code that you didn't show).
Try overwriting the run method, sounds like calling the method twice is a bug and this will make it easy to back out if it gets fixed.
gametime.oldRun = gametime.run;
gametime.run = (...params) => {
this.oldRun(...params);
this.oldRun(...params);
};
It could cause problems if internal methods call run so you could create run2
gametime.run2 = (...params) => {
this.run(...params);
this.run(...params);
};
Still easy to back out with a find and replace.

alternatives to componentDidMount....Axios?

I have recently done a few API tests for a new job. Just receiving data and passing it through. Although I have completed the tasks and it works functionally, the people I walk through it with are not huge fans of componentDidMount.
They do not suggest an alternative? Anyone know why this could be? Is it due to it being async?
The new modern way to do it is: useEffect
First some code (from the docs):
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
At the end-of-the-day, the componentDidMount purpose is to execute something(the side effect) because the component was mounted(the reason or event).
So you can specify array of dependencies (or causes) for re-running like so:
useEffect(() => {
// ....
}, [someVar]);
so if someVar changed, the function will re-run.
Special use cases are; omitting this argument, will cause it to run once, on-mount event. and specify empty array will cause it to run on each re-render.
For the componentWillUnmount:
Just return a function from the inner function like so:
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});

jest: How to teardown after (just) an individual test

jest provides afterEach, beforeEach, afterAll and beforeAll to complete setup and teardown logic. What I would like to do, is to clear up after one particular test. Consider the following:
describe("a family of tests it makes sense to group together", () => {
...
test("something I want to test", () => {
// some setup needed for just this test
global.foo = "bar"
// the test
expect(myTest()).toBe(true)
// clear up
delete global.foo
}
...
}
The problem with the above...
If the test above fails for some reason, then delete global.foo is never run. This means that potentially all of the tests following it will fail. Rather than seeing 1 test fail, I see a whole load of tests fail, which could be confusing.
Potential (non-ideal) solutions
One solution is just to add delete global.foo into my afterEach. It doesn't really need to be run after every test, but it doesn't do any harm either. Another solution would be to put the particular test by itself so that afterEach would only apply to it. But this doesn't seem ideal either - if that test belongs with other tests, it aught to be possible for it to remain with them.
My question:
Is there a way to run teardown logic for just a specific test (without running it inside the actual test). In my particular use-case the first outlined solution is fine, but I can imagine there might be situations where finer grained control is needed. If my teardown method took a long time for example I wouldn't want to repeat it lots, as this would slow down the whole test-suite.
In many cases tests can share a common afterEach clean-up even if it's needed for one of them, as long as it doesn't affect others.
Otherwise, this is what block structure is responsible for. One or several tests can be grouped with nested describe just to have their own afterEach, etc blocks, and the only downside is that it makes the report less pretty:
describe("a family of tests it makes sense to group together", () => {
...
describe("something I want to test", () => {
beforeEach(() => {
global.foo = "bar"
});
test("something I want to test", () => {
expect(myTest()).toBe(true)
}
afterEach(() => {
delete global.foo
});
});
beforeEach and afterEach can be desugared to try..finally:
test("something I want to test", () => {
try {
global.foo = "bar"
expect(myTest()).toBe(true)
} finally {
delete global.foo
}
})
This also allows for asynchronous tests but requires them to be written with async instead of done.
I know that this is an old question, but for anyone who stumbles upon this challenge in the future, here's a small library I wrote way back when, called jest-after-this that does just that:
import { afterThis } from 'jest-after-this';
it('should do something that requires a cleanup', () => {
global.foo = 'something';
afterThis(() => {
delete global.foo;
});
// ... rest of test here can fail, the cleanup method will run anyways
});
Hope this helps :)

Testing style set using Mocha

I'm trying to test some "style set" functions, and I'm facing issues because I need to wait until the element if fully rendered, then read its property and check if it was sucesfully changed. I know that this is pretty obvious, but I was wondering if there is a way to do the same but without waiting.
For example, I want to test this function:
function changeWidth() {
document.getElementById('test').style.width = '200px';
}
And I use this test in Mocha:
it('Width should be 200px', () => {
changeWidth();
assert.equal(document.getElementById('test').style.width, '200px');
});
That assertion will always returns false. The following test will work:
it('Width should be 200px', () => {
changeWidth();
window.setTimeout( () => {
assert.equal(document.getElementById('test').style.width, '200px');
}, 1000);
});
It has to be a better way to accomplish the same without using timeouts. Can someone guide me? Thanks!
There are two things I noted:
No, you cannot force a render to happen synchronously. The browser decides, so the test will be async and a bit messy. That being said, there are (a bit) more elegant answers than setTimeout.
Your test is synchronous, while the logic is asynchronous. That means your test will always pass, as the assertion is called after the test has finished (see this). You need to pass a callback to the test that can be called when the test has finished.
You could do the tests somewhat cleaner by using requestAnimationFrame. Just create a helper that will run each function passed to it in a seperate animation frame and you will be guaranteed separate render phases.
function runSteps(fns) {
if(!fns.length) return;
var current = fns[0];
var rest = fns.slice(1);
requestAnimationFrame(function() {
current();
runSteps(rest);
})
}
// note the `done` argument - it is what makes Mocha able to know
// when the test is finished. See https://stackoverflow.com/questions/20748918/cannot-run-mocha-js-in-synchronous-mode
it('should run dom render steps chronologically', function(done) {
function assertWidthChanged(){
assert.equal(
document.getElementById('test').style.width,
'200px'
);
}
runSteps([
changeWidth,
assertWidthChanged,
done
]);
}

Categories