Unable to write unit test cases for FHIR client api calls - reactjs

I hava component Componnent1 and inside the componentDidMount i am having the below code:-
const key1 = sessionStorage.getItem("key1");
FHIR.oauth2
.ready()
.then((client) =>
client.request("<url>/" + key1, {
resolveReferences: [
],
graph: true,
})
)
.then((json) => {
console.log(json);
})
.catch(console.error);
And I am getting this error
● renders as expected
console.error internal/process/next_tick.js:68
TypeError: client.request is not a function
at _fhirclient.default.oauth2.ready.then.client (C:\Users\dbdeb\OneDrive\Desktop\patient-application\src\components\my-health-deatails.component\allergies.component\allergies.js:68:16)
at process._tickCallback (internal/process/next_tick.js:68:7)
and my Test case is like:-
import { Component1} from "../component1"
import React, { Component } from "react";
import { render } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect'
import FHIR from "fhirclient";
jest.mock('fhirclient', () => ({
oauth2: {
ready: jest.fn(() => Promise.resolve({
client: {
request: jest.fn(() => { return { data: null } })
}
}))
}
}));
test('renders as expected', () => {
FHIR.oauth2.ready.mockResolvedValueOnce({ data: { entry: '' } })
render(<Component1></Component1>)
});

Related

Jest, Apollo Client different implementations

Currently I have this working test:
import React from 'react';
import { render } from '#testing-library/react';
import AuditList from '../../../components/Audit';
jest.mock('#apollo/client', () => ({
useLazyQuery: jest.fn().mockReturnValue([
jest.fn(),
{
data: {
someTestingData: 'some Value'
},
loading: false,
error: false
}
])
}));
describe('Audit', () => {
it('should render audit', () => {
const rendered = render(<AuditList />);
const getBytext = rendered.queryByText('my title');
expect(getBytext).toBeTruthy();
});
});
But, I want to test different cases: when ´loading/error´ is true/false.
How can I make specific definitions of the mock for different ´it´?
EDIT: I tried this:
import React from 'react';
import { useLazyQuery } from '#apollo/client';
import { render } from '#testing-library/react';
import AuditList from '../../../components/communications/Audit';
jest.mock('#apollo/client');
describe('Audit', () => {
it('should render audit', () => {
useLazyQuery.mockImplementation(() => [
jest.fn(),
{
data: {
someTestingData: 'some Value'
},
loading: false,
error: false
}
]);
const rendered = render(<AuditList />);
const getBytext = rendered.queryByText('Auditoría');
expect(getBytext).toBeTruthy();
});
});
But I get ´ Cannot read property 'mockImplementation' of undefined ´

React: JSDOM 13 Error: Error: connect ECONNREFUSED 127.0.0.1:80

I've created a test for a custom hook. all test pass but I receive the error and not sure how to find the root cause. I've trying going through this link but I was unable to find any helpful information.
The hook is just has an axios request. there is no part where I'm directly assigning the location.href which is another thing i've seen that's usually the cause for this error
import axios from 'axios'
jest.mock('axios')
describe('hooks', ()=>{
it('test custom hook', async()=>{
const { result, waitForNextUpdate } = await renderHook(()=> customHook())
axios.get.mockResolvedValue(dataJSON)
expect(axios.get).toHaveBeenCalledTimes(1) // received 0
await waitForNextUpdate()
expect(axios.get).toHaveBeenCalledTimes(1) //received 2
})
})
error:
console.error node_modules/jest-environment-jsdom-thirteen/node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Error: connect ECONNREFUSED 127.0.0.1:80
at Object.dispatchError (C:\project\node_modules\jest-environment-jsdom-thirteen\node_modules\jsdom\lib\jsdom\living\xhr-utils.js:60:19)
at Request.client.on.err (C:\project\node_modules\jest-environment-jsdom-thirteen\node_modules\jsdom\lib\jsdom\living\xmlhttprequest.js:674:20)
at Request.emit (events.js:203:15)
at Request.onRequestError (C:\project\node_modules\request\request.js:877:8)
at ClientRequest.emit (events.js:198:13)
at Socket.socketErrorListener (_http_client.js:401:9)
at Socket.emit (events.js:198:13)
at emitErrorNT (internal/streams/destroy.js:91:8)
at emitErrorAndCloseNT (internal/streams/destroy.js:59:3)
at process._tickCallback (internal/process/next_tick.js:63:19) undefined
jest-config:
"jest": {
"moduleNameMapper": {
"\\.(css|less|sass)$": "<rootDir>/_mocks_/styleMock.js"
},
"collectCoverageFrom": [
"**/*.{js,jsx,ts,tsx}",
"!<rootDir>/node_modules/",
"!src/index.js",
"!src/i18n.js",
"!<rootDir>/_mocks_/*",
"!src/setupProxy.js",
]
}
custom hook:
import { useState, useEffect, useCallback } from 'react'
import axios from 'axios'
const userDetailState = {
userInfo: '',
loading: true,
errorText: '',
}
export const customHook = () => {
const [userDetails, setUserDetails] = useState(userDetailState)
const transformData = useCallback(
(result) => {
setUserDetails({
...userDetails,
userInfo: result.data,
loading: false
})
},
[userDetails]
)
const getUserData = useCallback(async () => {
try {
const result = await axios.get('/userdetails')
transformData(result)
} catch (error) {
setUserDetails({
...userDetails,
loading: false
})
}
}, [transformData, userDetails])
useEffect(() => {
getUserData()
}, [])
return {
userDetails
}
}
userProvider:
import React, { useEffect } from 'react'
import { Loading } from 'components'
import UserContext from 'context/UserContext'
import { customHook } from 'hooks/User/customHook'
const UserProvider = () => {
const { userDetails } = customHook()
const renderContent = () => {
if (userDetails.loading) {
return (
<div className='loading'>
<Loading size='large' data-testid='userProfileLoadingId' className='loading-content' />
</div>
)
} else return dashbaordComponent
}
const contextValue = {
userDetail: userDetails.userInfo,
}
return (
<UserContext.Provider value={contextValue}>
{renderContent()}
</UserContext.Provider>
)
}

axios.create of jest's axios.create.mockImplementation returns undefined

I have written a test for component CategoryListContainer for just testing axios get call in it by mocking axios as below :
CategoryListContainer.test.js
import React from 'react';
import { render, cleanup, waitForElement } from '#testing-library/react';
import { Provider } from 'react-redux';
import store from '../../Store';
import axios from 'axios';
import CategoryListContainer from './CategoryListContainer';
jest.mock('axios', () => ({
create: jest.fn(),
}));
const products = {
data: [
{
id: '0',
heading: 'Shirt',
price: '800',
},
{
id: '1',
heading: 'Polo tees',
price: '600',
},
],
};
afterEach(cleanup);
const renderComponent = () =>
render(
<Provider store={store()}>
<CategoryListContainer />
</Provider>
);
test('render loading state followed by products', async () => {
axios.create.mockImplementation((obj) => ({
get: jest.fn(() => Promise.resolve(products)),
}));
const { getByText } = renderComponent();
await waitForElement(() => {
expect(getByText(/loading/i)).toBeInTheDocument();
});
});
As we see that in test 'render loading state followed by products' I wrote mock implemenation for axios.create as axios.create.mockImplementation((obj) => ({ get: jest.fn(() => Promise.resolve(products)), }));
Now when I use axios.create in axiosInstance.js as below :
import axios from 'axios';
const axiosInstance = axios.create({
headers: {
Accept: 'application/json',
ContentType: 'application/json',
authorization: '',
},
});
console.log(axiosInstance);
export default axiosInstance;
console.log(axiosInstance) shows undefined and therefore I'm getting the below error on running the test :
TypeError: Cannot read property 'get' of undefined
4 | const fetchCategories = () => async (dispatch) => {
5 | const response = await axiosInstance
> 6 | .get('/api/category/all')
| ^
7 | .catch((error) => {
8 | dispatch(fetchErrorAction(error));
9 | if (error.message.split(' ').pop() == 504) {
console.log src/backendApiCall/axiosInstance.js:9
undefined
I want to understand why console.log(axiosInstance) shows undefined . And the solution to making the test successful with making minimum changes to code .
because 'create' return jest function so it does not has '.get'.
You can use this
jest.mock('axios', () => {
return {
create: () => {
return {
get: jest.fn()
}}
};
});
then, you can set mock value
axiosInstance.get.mockReturnValueOnce({
data : {}
})

How to mock an API call with configuration in JEST?

I am using #woocommerce/woocommerce-rest-api package for my api. I am using NextJS and React Redux. Here is my woocommerce configuration:
import WooCommerceRestApi from '#woocommerce/woocommerce-rest-api';
export const wooApi = new WooCommerceRestApi({
url: 'MY_API_URL',
consumerKey: 'MY_CONSUMER_KEY',
consumerSecret: 'MY_CONSUMER_SECRET',
version: 'wc/v3',
queryStringAuth: true,
});
I dispatch an action right away when the component mounts.
Here's how I use the API in my action:
export const fetchMainProductCategories = () => {
return async (dispatch: Dispatch) => {
try {
const response = await wooApi.get(`products/categories?hide_empty=true&parent=0`);
dispatch<FetchMainProductCategories>({
type: CategoryTypes.fetchMainProductCategories,
payload: response.data,
});
} catch (error) {
console.log(error);
}
};
};
Here's my initial test statements so far but I doesn't work:
import React from 'react';
import '../../__mocks__/matchMedia';
import MockCategories from '../../__mocks__/mockCategories';
import { render, cleanup, logDOM } from '#testing-library/react';
import Index from '../../pages/index';
import Root from '../../Root';
import { wooApi } from '../../config';
jest.mock('../../config');
describe('Homepage', () => {
beforeEach(() => {
render(
<Root>
<Index />
</Root>
);
});
afterEach(cleanup);
it('loads Product Categories', async () => {
wooApi.get.mockResolvedValueOnce({
data: MockCategories,
});
logDOM();
// const list = await waitFor(() => screen.getByTestId('category-list'));
});
});
You need to register the get method of the wooApi as a mock, while preserving the other features of the api. ie:
import { wooApi } from '../../config'
import { fetchMainProductCategories } from '../where-it-is-defined'
// mark get method as jest mock
jest.mock('../../config', () => ({
...jest.requireActual('../../config'), // to avoid overriding other methods/features
get: jest.fn(), // override get method of the api
}))
describe('Homepage', () => {
beforeEach(()=>{
wooApi.get.mockResolvedValue({
status: 200,
data: { categories: ['a', 'b'] },
})
test('loads ...', async () => {
const dispatch = jest.fn()
await fetchMainProductCategories()(dispatch)
expect(dispatch).toHaveBeenCalledWith(
{ type: '...',
payload: { categories: ['a', 'b'] }
}
)
})
})
Ref:
Bypassing Module Mocks in Jest
Edited: My bad, by doing jest.spyOn(config.wooApi, 'get') we are only mocking "get" method of a single instance. The following edited code should work
You can also use jest.spyOn to only mock the get method like below
import * as config from '../../config'
jest.spyOn(WooCommerceRestApi.prototype, 'get')
WooCommerceRestApi.prototype.get.mockResolvedValue('...')

React Apollo Testing is not working. Query data is always coming undefined

I am getting loading state only and data as undefined in testing. I don't know why I am following everything in the given example. Please help.
Testing file. When i am waiting thsi line toexecute await wait(() => getByTestId('edit-category'));. It is giving response data of query as undefined.
Error: TypeError: Cannot read property 'getCategory' of undefined
Line 34 on editConatinerCategory.tsx => category={data!.getCategory!}
import React from 'react';
import gql from 'graphql-tag';
import { cleanup, wait } from 'react-testing-library';
import { customRender } from '../../../test-utils/customRender';
import { EditCategoryContainer } from './Container';
afterEach(() => {
cleanup();
console.error;
});
console.error = jest.fn();
const getCategoryMock = {
request: {
query: gql`
query getCategory($id: Int!) {
getCategory(id: $id) {
id
name
active
position
}
}
`,
variables: {
id: 1
}
},
result: {
data: {
getCategory: {
id: 1,
name: 'category',
active: true,
position: 1
}
}
}
};
describe('create edit category module', () => {
test('Rendering correct', async () => {
const { container, debug, getByTestId } = customRender(<EditCategoryContainer />, [
getCategoryMock
]);
await wait(() => getByTestId('edit-category'));
await wait(() => expect(container).toMatchSnapshot());
//Getting this TypeError: Cannot read property 'getCategory' of undefined. Because i am data as undefined from my query response
});
});
CustomRender.tsx
import React from 'react';
import { render } from 'react-testing-library';
import { MockedProvider, MockedResponse } from 'react-apollo/test-utils';
import { Router, Switch } from 'react-router-dom';
import { createMemoryHistory } from 'history';
export const customRender = (
node: JSX.Element | null,
mocks?: MockedResponse[],
{
route = '/',
history = createMemoryHistory({ initialEntries: [route] })
} = {}
) => {
return {
history,
...render(
<MockedProvider mocks={mocks} addTypename={false}>
<Router history={history}>
<Switch>{node}</Switch>
</Router>
</MockedProvider>
)
};
};
EditCategoryContainer.tsx
import React from 'react';
import { withRouter } from 'react-router';
import { Spin } from 'antd';
import {
AddCategoryComponent,
GetCategoryComponent
} from '../../../generated/graphql';
import { EditCategory } from './Edit';
import { LoadingComponent } from '../../../components/LoadingComponent';
export const EditCategoryContainer = withRouter(({ history, match }) => {
const id: number = parseInt(match.params.id, 10);
return (
<GetCategoryComponent
variables={{
id
}}
>
{({ data, loading: getCategoryLoading }) => {
console.log(getCategoryLoading, 'getCategoryLoading');
if (getCategoryLoading) {
return <LoadingComponent />;
}
if (data && !data.getCategory) {
return <div>Category not found!</div>;
}
console.log(data);
return (
<AddCategoryComponent>
{(addCategory, { loading, error }) => {
return (
<EditCategory
data-testid="edit-category"
category={data!.getCategory!}
loading={loading || getCategoryLoading}
onSubmit={values => {
addCategory({ variables: values }).then(() => {
history.push('/dashboard/categories');
});
}}
/>
);
}}
</AddCategoryComponent>
);
}}
</GetCategoryComponent>
);
});
Edit:
I tried #mikaelrs solution which is passed match. But it is not working. I also tried to pass id:1 as fixed. But it is still giving error.
<GetCategoryComponent
variables={{
id:1
}}
>
...rest of code.
</GetCategoryComponent>
This is not working. My query without veriable is working fine. Mutation is also working fine. I am having only problem with this. When i have to pass like varible like this.
What I do to wait for the loading state of the MockedProvider to pass is to use the wait function from waait. This is actually what Apollo recommends as well.
So in your test you would do:
import React from 'react';
import gql from 'graphql-tag';
import { cleanup } from 'react-testing-library';
import wait from 'waait'
import { customRender } from '../../../test-utils/customRender';
import { EditCategoryContainer } from './Container';
afterEach(() => {
cleanup();
});
const getCategoryMock = {
request: {
query: gql`
query getCategory($id: Int!) {
getCategory(id: $id) {
id
name
active
position
}
}
`,
variables: {
id: 1
}
},
result: {
data: {
getCategory: {
id: 1,
name: 'category',
active: true,
position: 1
}
}
}
};
describe('create edit category module', () => {
test('Rendering correct', async () => {
const { container, debug } = customRender(<EditCategoryContainer />, [
getCategoryMock
]);
await wait(0);
// Your loading state should be false after this, and your component should
// get it's data from apollo for you to do any assertion you would like to
// after this point. To see that the component is rendered with data invoke
// the debug function from react-testing-library after this point
debug();
expect(container).toMatchSnapshot()
});
});
Another solution is to use react-testing-librarys wait function to wait for an element that would be present after the loading state switches to true.
For instance
describe('create edit category module', () => {
test('Rendering correct', async () => {
const { container, debug, queryByText } = customRender(<EditCategoryContainer />, [
getCategoryMock
]);
await wait(()=> queryByText("Some Data"));
// Your loading state should be false after this, and your component should
// get it's data from apollo for you to do any assertion you would like to
// after this point
expect(container).toMatchSnapshot()
});
});
I faced a similar issue. Here is how I resolved my issue.
First, wait for the query to resolve, as recommended by #mikaelrs and the docs:
await new Promise(resolve => setTimeout(resolve, 0));
After doing that, the loading property was false, but data was still undefined. I discovered that my mock result object was missing a property. Once I added that missing property to the mock result, the data was populated as expected.

Resources