Testing iron-session `withIronSessionSsr` that uses `context` in NextJs, React, and Jest - reactjs

So I have this NextJs utility file function below that returns an imported function. Then that imported function accepts a handler, that uses context, as an argument.
import { ironSessionUtil } from './anotherUtil'
const checker = () =>
ironSessionUtil(({ req }) => {
const { user } = req.session
if (!user) {
return {
redirect: { destination: '/welcome', permanent: false }
}
}
return { props: { details: user.details } }
})
export default checker
So far I've tried just calling the method but it returns an Async Function.
it('should return props', () => {
const result = checker()
console.log('result >> ', result)
})
This returns:
[AsyncFunction: nextGetServerSidePropsHandlerWrappedWithIronSession]
I've been scouring the web on how to test this. Would you suggest refactors to make this more testable? Or do you know how this can be done in Jest?

Related

Intercepting Auth0 getSession with MSW.js and Cypress

I'm building NextJS app with SSR. I've written the getServerSideProps function that makes a call to supabase. Before making the call I'm trying to get user session by calling getSession function from #auth0/nextjs-auth0 package.
I'm trying to mock it in the handlers.ts file:
import { rest } from 'msw';
export const handlers = [
// this is the endpoint called by getSession
rest.get('/api/auth/session', (_req, res, ctx) => {
return res(ctx.json(USER_DATA));
}),
rest.get('https://<supabase-id>.supabase.co/rest/v1/something', (_req, res, ctx) => {
return res(ctx.json(SOMETHING));
}),
];
My mocks file: requestMocks/index.ts:
export const initMockServer = async () => {
const { server } = await import('./server');
server.listen();
return server;
};
export const initMockBrowser = async () => {
const { worker } = await import('./browser');
worker.start();
return worker;
};
export const initMocks = async () => {
if (typeof window === 'undefined') {
console.log('<<<< setup server');
return initMockServer();
}
console.log('<<<< setup browser');
return initMockBrowser();
};
initMocks();
Finally, I'm calling it in the _app.tsx file:
if (process.env.NEXT_PUBLIC_API_MOCKING === 'true') {
require('../requestMocks');
}
Unfortunately, it does work for me. I'm getting no user session data in the getServerSideProps function in my page component:
import { getSession } from '#auth0/nextjs-auth0';
export const getServerSideProps = async ({ req, res }: { req: NextApiRequest; res: NextApiResponse }) => {
const session = getSession(req, res);
if (!session?.user.accessToken) {
// I'm constantly falling here
console.log('no.session');
return { props: { something: [] } };
}
// DO something else
};
Any suggestions on how to make it working in Cypress tests would be great.
I'm expecting that I will be able to mock requests made in getServerSideProps function with MSW.js library.
I made it finally. Looks like I don't have to mock any calls. I need to copy my user appSession cookie and save it in cypress/fixtures/appSessionCookie.json file:
{
"appSession": "<cookie-value>"
}
Then use it in tests as follows:
before(() => {
cy.fixture('appSessionCookie').then((cookie) => {
cy.setCookie('appSession', cookie.appSession);
});
});
This makes a user automatically logged in with Auth0.

How to test public functions in Typescript with react-testing-library?

I'm having below setup in my project, whenever I extends the httpService and use the 'this.instance' in any service I'm getting the error.
If I use axios.get directly without any interceptors in my service files its working fine.
Im new to the unit testing and badly stuck with this. Please share your comments below. It'll be really helpful.
httpClient.ts
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
import { DEFAULT_HEADERS, HOST } from './ApiHelper';
export abstract class HttpService {
protected readonly instance: AxiosInstance;
public constructor(requestConfig: AxiosRequestConfig) {
this.instance = axios.create(requestConfig);
this.instance.interceptors.request.use((request) => {
request.baseURL = HOST;
request.headers = { ...DEFAULT_HEADERS };
return request;
});
this.instance.interceptors.response.use(
(response) =>
response,
(error) =>
new Promise((resolve, reject) => {
reject(error.response);
}),
);
}
}
export default HttpService;
someService.ts
import HttpService from './HttpService';
const warningListUrl = 'some/url';
class SomeService extends HttpService {
public constructor() {
super({
});
}
public async getSomething(params: any) {
this.instance({
method: 'GET',
url: warningListUrl,
params,
}).then((res) =>
res.data);
}
}
export default SomeService;
ReactComponent.tsx
const fetchList = async () => {
try {
setIsLoading(true);
const someService = new SomeService();
const response: any = await someService.getSomething({});
setWarnings(response.content);
setTotalPages(response.totalPages);
} catch (error) {
console.log(error);
} finally { setIsLoading(false); }
};
useEffect(() => {
fetchList();
}, []);
ReactComponent.test.tsx
jest.mock('../../services/SomeService');
const someService = new SomeService();
describe('page tests', () => {
test('page renders without crashing', async () => {
(someService.getWarningList as jest.Mock).mockResolvedValue(someMatchingData);
await act(async () => {
render(<ReactComponent />);
});
const text = screen.getByText('Warning 1');
expect(text).toBeInTheDocument();
});
}
Error:
TestingLibraryElementError: Unable to find an element with the text: Warning 1. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
render(<Warning />);
});
-> const text = screen.getByText('Warning 1');
expect(text).toBeInTheDocument();
});
You could use requireActual if you need to mock only specific methods.
jest.mock('../../services/SomeService', ()=> {
return {
...jest.requireActual('../../services/SomeService'),
getWarningList: new Promise.resolve(someMatchingData)
}
})
How about mocking a module like this?
Accessing methods through 'prototype' saved my day.
(someService.prototype.getWarningList as jest.Mock).mockResolvedValue(someMatchingData);
just adding it above the test description saved me.

How to mock Axios service wrapper using JEST

I'm using JEST testing framework to write test cases for my React JS application. I'm using our internal axios wrapper to make service call. I would like to mock that wrapper service using JEST. Can someone help me on this ?
import Client from 'service-library/dist/client';
import urls from './urls';
import { NODE_ENV, API_VERSION } from '../screens/constants';
const versionHeader = 'X-API-VERSION';
class ViewServiceClass extends Client {
getFiltersList(params) {
const config = {
method: urls.View.getFilters.requestType,
url: urls.View.getFilters.path(),
params,
headers: { [versionHeader]: API_VERSION },
};
return this.client.request(config);
}
const options = { environment: NODE_ENV };
const ViewService = new ViewServiceClass(options);
export default ViewService;
Above is the Service Implementation to make API call. Which I'm leveraging that axios implementation from our internal library.
getFiltersData = () => {
const params = {
filters: 'x,y,z',
};
let {
abc,
def,
ghi
} = this.state;
trackPromise(
ViewService.getFiltersList(params)
.then((result) => {
if (result.status === 200 && result.data) {
const filtersJson = result.data;
.catch(() =>
this.setState({
alertMessage: 'No Filters Data Found. Please try after some time',
severity: 'error',
showAlert: true,
})
)
);
};
I'm using the ViewService to get the response, and I would like to mock this service. Can someone help me on this ?
You would need to spy your getFiltersList method from ViewServiceClass class.
Then mocking some response data (a Promise), something like:
import ViewService from '..';
const mockedData = {
status: 'ok',
data: ['some-data']
};
const mockedFn = jest.fn(() => Promise.resolve(mockedData));
let getFiltersListSpy;
// spy the method and set the mocked data before all tests execution
beforeAll(() => {
getFiltersListSpy = jest.spyOn(ViewService, 'getFiltersList');
getFiltersListSpy.mockReturnValue(mockedFn);
});
// clear the mock the method after all tests execution
afterAll(() => {
getFiltersListSpy.mockClear();
});
// call your method, should be returning same content as `mockedData` const
test('init', async () => {
const response = await ViewService.getFiltersList();
expect(response).toEqual(mockedData);
});
P.D: You can pass params to the method, but you will need to configure as well the mockedData as you wish.

Jest testing service call responses witth promises, useEffect and useState hooks

I'm having some difficulty testing this useEffect in jest. The following piece of code is within a react functional component and I want to return some mock values when the serviceFn is called. The returned data is written back to state.
//from service-factory.js
const serviceFn = () => (
({ personId }) => (
ionXHR.request(
`/persons/${personId}/`,
'GET',
null,
'json',
)
)
);
//from Component.jsx
const service = useRef(serviceFn());
useEffect(() => {
service.current({ personId:123456 }).then((response) => {
if (response.data) {
setData(response.data);
setLoadingState('SUCCESS');
} else {
setLoadingState('FAILED');
}
});
}, [personId]);
I have the following, but not sure what else I would need.
function mockReturnFn() { return 'Test'; }
const wrapper = mount(<Component/>);
const somethingSpy = jest.spyOn(wrapper, 'serviceFn').mockImplementation(mockReturnFn);
Update:
So, I think I'm getting close.
In my test file I had to import the function
import { serviceFn } from './service-factory';
jest.mock('./service-factory', () => ({ serviceFn : jest.fn() }));
In my test I have
serviceFn.mockImplementation(() => Promise.resolve('test1234'));
The issue now with this is I am getting service.current is not a function
I tried to do this but now getting _serviceFactory.serviceFn.mockImplementation is not a function
jest.mock('./service-factory', () => (
{
serviceFn: {
current: jest.fn(),
},
}
));
serviceFn is factory function, it returns a function that makes requests.
Considering it's named export, it should be initially stubbed as:
jest.mock('./service-factory', () => {
const mockService = jest.fn();
return {
__esModule: true,
mockService,
serviceFn: jest.fn().mockReturnValue(mockService)
}
});
mockService is exposed and allows to mock specific responses:
mockService.mockResolvedValue({ data: ... });
Since it's basic wrapper over ionXHR, it's also possible to mock responses one level higher on ionXHR.request calls.

Jest how to mock api call

I am trying to mock my api call with jest but for some reason it's not working. I don't really understand why. Anyone has an idea?
(the test keep call the original api call function and not the mock)
my test.js
import { getStuff } from '../stuff';
import * as api from '../../util/api';
describe('Action getStuff', () => {
it('Should call the API to get stuff.', () => {
api.call = jest.fn();
getStuff('slug')(() => {}, () => {});
expect(api.call).toBeCalled();
jest.unmock('../../util/api.js');
});
});
stuff.js redux action
import api from '#util/api';
import { STUFF, API } from '../constant';
export const getStuff = slug => (dispatch, getState) => {
const state = getState();
api.call(API.STUFF.GET, (err, body) => {
if (err) {
console.error(err.message);
} else {
dispatch({
type: STUFF.GET,
results: body,
});
}
}, {
params: { slug },
state
});
};
The import are immutable so it won't work, what you should is mock the whole module. Either with a __mock__ directory or simply with:
jest.mock('../../util/api');
const { call } = require('../../util/api');
call.mockImplementation( () => console.log("some api call"));

Resources