Next.js router error while testing using React-testing-library - jestjs

I was just trying to do a preliminary test of rendering a component that is redirecting the user on the basis of the login status and thus using router from next/router in componentDidMount but getting the following error:
No router instance found.
You should only use "next/router" inside the client side of your app.
It appears to me that from client side it means using the router or Link has to be used inside of the render method as that is what makes the DOM and other methods like lifecycle, hooks, and server-side doesn't so in those cases it would throw an error.
I know that testing the entire component beats the purpose of unit testing but I wanted to do this anyway. Therefore, I followed this discussion and it appears that the router has to be mocked in order to be used by the React-Testing-Library but none of the solutions work for me.
Here is the code that I tried:
describe('Home Page', () => {
it('renders without crashing', async () => {
render(<Home />)
})
})

a solution like:
import { useRouter } from 'next/router'
...
jest.mock('next/router', () => ({
useRouter: jest.fn(),
}))
...
;(useRouter as jest.Mock).mockImplementation(() => ({
pathname: '/',
push: mockPush,
}))
will work.
the error you're encountering will be triggered if you do not mock useRouter in the initial mock (i.e.:
...
jest.mock('next/router')
...

Mocked the router using the next-router-mock library. Just pasted jest.mock('next/router', () => require('next-router-mock')); before the test and the tests are passing.
jest.mock('next/router', () => require('next-router-mock'));
describe('Home Page', () => {
it('renders without crashing', async () => {
render(<Home />)
})
})
Although, the test is passing but getting this warning that is not making any sense to me.
› 1 snapshot obsolete.
• Home Page renders without crashing 1
Snapshot Summary
› 1 snapshot obsolete from 1 test suite. To remove it, press `u`.
↳ tests/homePage.test.js
• Home Page renders without crashing 1
Assuming that there is a better way to mock the router.

Related

react jest mock useNavigate()

I am trying to mock the useNavigate hook using Jest. I was able to do it at the top level of my test suit (I also have a custom render with BrowserRouter as a wrapper)
const mockedUsedNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
...(jest.requireActual('react-router-dom') as any),
useNavigate: () => mockedUsedNavigate,
}));
...
expect(mockedUsedNavigate).toHaveBeenCalledWith('myurl/user-1');
My issue is that I want to have this mock available inside the __mocks__ directory so I can use it in other tests as well instead if having to write it again and again. I wasn't able to do it.
Is it possible?
You should be able create a src.setupTests.js file in your project and declare the mock there.
See Initializing Test Environment
src/setupTests.js
export const navigateMock = jest.fn(); // <-- exported for test assertions
jest.mock('react-router-dom', () => ({
...(jest.requireActual('react-router-dom') as any),
useNavigate: () => navigateMock,
}));
You can then import the exported navigateMock function in test files.

How do I remove a "window.api.receive" in Electron and React

I have an Electron app using the recommended setup from https://www.debugandrelease.com/the-ultimate-electron-guide/ where access is given via window.api.receive and window.api.send to the preloader. However I need to use window.api.receive within my react component and my issue is don't know how to remove the "receive" when the component is unmounted.
From the react side of things I think i just need to add in the code in the use effect, but I don't know what code can be used to remove the window.api.receive that was added.
// On Mount
useEffect(() => {
console.log('mount APP')
window.api.receive("helpLine", (text, loading) => {
// Things are done here
})
}, []);
// on Unmount
useEffect( () => () => {
console.log("unmount App")
// window.api.receive should be removed here
}, [] );
Use ipcRenderer.removeListener(channel, listener) to remove the specified listener.
https://www.electronjs.org/docs/latest/api/ipc-renderer#ipcrendererremovelistenerchannel-listener

How to mock fetch call inside of child´s componentDidMount with Jest/Enzyme [duplicate]

I would like to test a small React web app where I use the global fetch method.
I tried to mock fetch in this way:
global.fetch = jest.spyOn(global, 'fetch').mockImplementation(endpoint =>
Promise.resolve({
json: () => Promise.resolve(mockResponse)
})
);
... but the mock seems to be ignored, while the built-in fetch seems to be used: Error: connect ECONNREFUSED 127.0.0.1:80 ... looks like a failed call to the built-in fetch.
I then tried to use jest.fn instead of jest.spyOn:
global.fetch = jest.fn(endpoint =>
Promise.resolve({
json: () => Promise.resolve(mockResponse)
})
);
... and was surprised to see a different error. Now the mock seems to be taken into consideration, but at the same time is not working correctly:
TypeError: Cannot read property 'then' of undefined
8 | this.updateTypes = this.props.updateTypes;
9 | this.updateTimeline = this.props.updateTimeline;
> 10 | fetch('/timeline/tags')
| ^
11 | .then(res => res.json())
12 | .then(tags => tags.map(tag => <option value={tag} key={tag} />))
13 | .then(options => options.sort((a, b) => a.key.localeCompare(b.key)))
I find the documentation of Jest and React Testing Library a bit confusing, honestly. What might be the problem with what I am doing?
Edit
The React component I am trying to test is called "App", was generated with Create React App, and was changed to include a call to fetch. I can gladly provide the code for this component, but I believe that the problem lies in the tests.
At the beginning of my App.test.js file, I import React from 'react';, then import { render, fireEvent, waitFor, screen } from '#testing-library/react';, and finally import App from './App';. I subsequently attempt to mock fetch in one of the ways I described, and then declare the following test:
test('renders a list of items, upon request', async () => {
const app = render(<App />);
fireEvent.click(screen.getByText('Update'));
await waitFor(() => screen.getByRole('list'));
expect(screen.getByRole('list')).toBeInTheDocument();
expect(screen.getByRole('list')).toHaveClass('Timeline');
});
Finally, I end my test file with global.fetch.mockRestore();.
That there's ECONNREFUSED error instead of fetch is not defined means that fetch has been polyfilled. It's not a part of JSDOM and isn't polyfilled by Jest itself but is specific to current setup. In this case the polyfill is provided by create-react-app.
It's always preferable to mock existing global function with jest.spyOn and not by assigning them as global properties, this allows Jest to do a cleanup. A thing like global.fetch = jest.spyOn(global, 'fetch') should never be done because this prevents fetch from being restored. This can explain TypeError: Cannot read property 'then' of undefined error for seemingly correctly mocked function.
A correct and safe way to mock globals is to mock them before each test and restore after each test:
beforeEach(() => {
jest.spyOn(global, 'fetch').mockResolvedValue({
json: jest.fn().mockResolvedValue(mockResponse)
})
});
afterEach(() => {
jest.restoreAllMocks();
});
There should be no other modifications to global.fetch in order for a mock to work correctly.
A preferable way to restore mocks and spies is to use configuration option instead of jest.restoreAllMocks because not doing this may result in accidental test cross-contamination which is never desirable.
Another reason for TypeError: Cannot read property 'then' of undefined error to appear is that Jest incorrectly points at fetch line, and the error actually refers to another line. This can happen if source maps don't work correctly. If fetch is mocked correctly and there are other then in the same component, it's a plausible explanation for the error.

Jasmine - Load nested describes from external files?

I'm writing e2e tests using Jasmine and Protractor with AngularJS. I have a parent describe which describes the page, and some setup call in beforeAll that goes to the correct page for the following tests. I've broken these tests up into multiple describes per feature. Here is an example:
describe('Page Detail', () => {
beforeAll(() => {
utils.doSomething();
})
describe('Location Section'), () => ...
describe('Information Section', () => ...
The problem I'm having is that there are a lot of features within this module, and the tests are starting to push 300-400 lines of code. I would ideally like to put the nested describes in seperate files and import them. I've done something like this:
const describeLocation = require('./folder/location'),
describeInformation = require('./folder/information');
describe('Page Detail', () => {
beforeAll(() => {
utils.doSomething();
})
describeLocation();
describeInformation();
In the other files I'm simply exporting an anonymous function and including the nested describe's code. This works, but unfortunately the tests don't have the jasmine context (can't access anything in beforeAll).
I'm curious if there is a standard or better way to accomplish this?
//export.js
describe(...
)
//Import or your main specs file
describe('',()=>{
require('./export');
})
Don't export in a form of a method just write your spec and import it using require in the main describe.

nrwl-Nx and Cypress, verification timing out with failed --smoke-test in Windows 7

I'm trying to follow along with this tutorial on the NX website. The 2nd part has us setting up e2e testing with Cypress. I followed everything as said and even went as far as commenting out my code and pasting theirs into my files. I'm not getting any errors in the console. The error I see in Node says
Cypress verification timed out
This command failed with the following output:
C:.....\Cache\3.3.1\Cypress\Cypress.exe --smoke-test --ping=852
The tutorial also says there's a UI that should pop up on our app, which I don't see anything of the sort.
After generating the workspace and the application it has us modify the app.po.ts file by adding a couple constants, so far mine looks like this
export const getGreeting = () => cy.get('h1');
export const getTodos = () => cy.get('li.todo');
export const getAddTodoButton = () => cy.get('button#add-todo');
next it tells us to update the app.spec.ts file of the e2e test by adding this
import { getAddTodoButton, getTodos } from '../support/app.po';
describe('TodoApps', () => {
beforeEach(() => cy.visit('/'));
it('should display todos', () => {
getTodos().should(t => expect(t.length).equal(2));
getAddTodoButton().click();
getTodos().should(t => expect(t.length).equal(3));
});
});
The version of this file generated by Nx comes with this already in it
import { getGreeting } from '../support/app.po';
describe('todos', () => {
beforeEach(() => cy.visit('/'));
it('should display welcome message', () => {
getGreeting().contains('Welcome to todos!');
});
});
I originally tried adding the extra test underneath it and added the new imports. After getting the error message I thought maybe I needed to combine the tests into one test which looks like this.
describe('TodoApps', () => {
beforeEach(() => cy.visit('/'));
it('should display welcome message', () => {
getGreeting().contains('Welcome to todos!');
});
it('should display todos', () => {
getTodos().should(t => expect(t.length).equal(2));
getAddTodoButton().click();
getTodos().should(t => expect(t.length).equal(3));
});
});
I'm still getting the same error in Node and have no clue as to what I'm doing wrong. Prior to starting the project I updated node, npm and angular cli. I downloaded Angular Console for VS Code but am running into problems with it so I've just been using the Node Terminal and Brackets. Can anyone help?
if you in windows then you can solve this verification timeout issue by navigating to:
'C:\Users\<user>\AppData\Local\Cypress\Cache\3.4.0\Cypress'
then just double click on Cypress.exe.
After this close it and go back to your ide or terminal and redo what threw the error

Resources