FormData is not defined in React Jest - reactjs

I am writing a unit testing code for the React project. I am trying to test one function
//function aa
export const login = (values) => async => (dispatch) => {
let bodyFormData = new FormData();
bodyFormData.append('username', values.login);
bodyFormData.append('password', values.password);
return await axios({
method: 'post',
url: url,
data: bodyFormData
}
}
//aa test
it("Login Action", async () => {
afterEach(() => {
store.clearActions();
});
const values = {
login: "aaaaa",
password: "bbbbb"
};
const expectedResult = { type: "LOGIN_PASS" };
const result = await store.dispatch(login(values));
expect(result).toEqual(expectedResult);
});
In the browser, this works ok. but when testing I get below error
ReferenceError: FormData is not defined
I tried to use this module but no luck...
https://www.npmjs.com/package/form-data
I do not want to just test axios, I need to test full function.

You will need to mock FormData within your unit test, as the FormData web API is not available in the node.js/jsdom environment.
function FormDataMock() {
this.append = jest.fn();
}
global.FormData = FormDataMock
If you wish to mock other methods within the FormData global:
const entries = jest.fn()
global.FormData = () => ({ entries })

I was also facing this issue and it turned out that testEnvironment (inside jest.config.js) was set to 'node'. Changing it to 'jsdom' resolved it.

You need to mock the FormData for same, simply add below lines in top of test file.
// #ts-ignore
global.FormData = require('react-native/Libraries/Network/FormData');

Related

All my TRPC queries fail with a 500. What is wrong with my setup?

I am new to TRPC and have set up a custom hook in my NextJS app to make queries. This hook is sending out a query to generateRandomWorker but the response always returns a generic 500 error. I am completely stuck until I can figure out this issue.
The hook:
// filepath: src\utilities\hooks\useCreateRandomWorker.ts
type ReturnType = {
createWorker: () => Promise<Worker>,
isCreating: boolean,
}
const useCreateRandomWorker = (): ReturnType => {
const [isCreating, setIsCreating] = useState(false);
const createWorker = async (): Promise<Worker> => {
setIsCreating(true);
const randomWorker: CreateWorker = await client.generateRandomWorker.query(null);
const createdWorker: Worker = await client.createWorker.mutate(randomWorker);
setIsCreating(false);
return createdWorker;
}
return { createWorker, isCreating };
Here is the router. I know the WorkerService calls work because they are returning the proper values when passed into getServerSideProps which directly calls them. WorkerService.generateRandomWorker is synchronous, the others are async.
// filepath: src\server\routers\WorkerAPI.ts
export const WorkerRouter = router({
generateRandomWorker: procedure
.input(z.null()) // <---- I have tried completely omitting `.input` and with a `null` property
.output(PrismaWorkerCreateInputSchema)
.query(() => WorkerService.generateRandomWorker()),
getAllWorkers: procedure
.input(z.null())
.output(z.array(WorkerSchema))
.query(async () => await WorkerService.getAllWorkers()),
createWorker: procedure
.input(PrismaWorkerCreateInputSchema)
.output(WorkerSchema)
.mutation(async ({ input }) => await WorkerService.createWorker(input)),
});
The Next API listener is at filepath: src\pages\api\trpc\[trpc].ts
When the .input is omitted the request URL is /api/trpc/generateRandomWorker?batch=1&input={"0":{"json":null,"meta":{"values":["undefined"]}}} and returns a 500.
When the .input is z.null() the request URL is /api/trpc/generateRandomWorker?batch=1&input={"0":{"json":null}} and returns a 500.
Can anyone help on what I might be missing?
Additional Info
The client declaration.
// filepath: src\utilities\trpc.ts
export const client = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: `${getBaseUrl() + trpcUrl}`, // "http://localhost:3000/api/trpc"
fetch: async (input, init?) => {
const fetch = getFetch();
return fetch(input, {
...init,
credentials: "include",
})
}
}),
],
transformer: SuperJSON,
});
The init:
// filepath: src\server\trpc.ts
import SuperJSON from "superjson";
import { initTRPC } from "#trpc/server";
export const t = initTRPC.create({
transformer: SuperJSON,
});
export const { router, middleware, procedure, mergeRouters } = t;
Sorry I am not familiar with the vanilla client. But since you're in react you might be interested in some ways you can call a trpc procedure from anywhere while using the react client:
By using the context you can pretty much do anything from anywhere:
const client = trpc.useContext()
const onClick = async () => {
const data = await client.playlist.get.fetch({id})
}
For a known query, you can disable it at declaration and refetch it on demand
const {refetch} = trpc.playlist.get.useQuery({id}, {enabled: false})
const onClick = async () => {
const data = await refetch()
}
If your procedure is a mutation, it's trivial, so maybe you can turn your GET into a POST
const {mutateAsync: getMore} = trpc.playlist.more.useMutation()
const onClick = async () => {
const data = await getMore({id})
}
Answered.
Turns out I was missing the export for the API handler in api/trpc/[trpc].ts

React Axios - Pass Hook as Parameter for Axios Params

I am new to React & Axios, I'm trying to work my head around how to change the GET instance properties based on user inputs... If I am going about it the wrong way please direct me.
I want the selected dataFormat to pass to the params of the Axios.getData()
At the moment I can only get it to pass the object rather than its value.
Thanks in advance
Here is the code to fetch the data:
function App() {
let [responseData, setResponseData] = React.useState([]);
const [dataFormat, setDataFormat] = React.useState("json");
const fetchData = (e) => {
e.preventDefault();
console.log({dataFormat});
api
.getData(dataFormat)
.then((response) => {
console.log("Hello");
console.log(response);
setResponseData(response.data);
})
.catch((error) => {
console.log(error);
});
};
Here is the Axios instance
enter image description here
Here is the error I am receiving:
enter image description here
First you need to install the express library. Then, import cors and also use express.json() for parsing the json as shown below:
const express = require("express");
const app = express();
const cors = require("cors");
app.use(express.json());
app.use(cors());
function App() {
let [responseData, setResponseData] = React.useState([]);
const [dataFormat, setDataFormat] = React.useState("json");
const fetchData = (e) => {
e.preventDefault();
console.log({dataFormat});
api
.getData(dataFormat)
.then((response) => {
console.log("Hello");
console.log(response);
setResponseData(response.data);
})
.catch((error) => {
console.log(error);
});
};

Mock axios create in test file. React, typescript

I've seen some similar posts about mocking axios but I have spend some hours and I didn't manage to solve my problem and make my test work. I've tried solutions that I have found but they didn't work.
I'm writing small app using React, Typescript, react-query, axios. I write tests with React Testing Library, Jest, Mock Service Worker.
To test delete element functionality I wanted just to mock axios delete function and check if it was called with correct parameter.
Here is the PROBLEM:
I'm using axios instance:
//api.ts
const axiosInstance = axios.create({
baseURL: url,
timeout: 1000,
headers: {
Authorization: `Bearer ${process.env.REACT_APP_AIRTABLE_API_KEY}`,
},
//api.ts
export const deleteRecipe = async (
recipeId: string
): Promise<ApiDeleteRecipeReturnValue> => {
try {
const res = await axiosInstance.delete(`/recipes/${recipeId}`);
return res.data;
} catch (err) {
throw new Error(err.message);
}
};
});
//RecipeItem.test.tsx
import axios, { AxiosInstance } from 'axios';
jest.mock('axios', () => {
const mockAxios = jest.createMockFromModule<AxiosInstance>('axios');
return {
...jest.requireActual('axios'),
create: jest.fn(() => mockAxios),
delete: jest.fn(),
};
});
test('delete card after clicking delete button ', async () => {
jest
.spyOn(axios, 'delete')
.mockImplementation(
jest.fn(() =>
Promise.resolve({ data: { deleted: 'true', id: `${recipeData.id}` } })
)
);
render(
<WrappedRecipeItem recipe={recipeData.fields} recipeId={recipeData.id} />
);
const deleteBtn = screen.getByRole('button', { name: /delete/i });
user.click(deleteBtn);
await waitFor(() => {
expect(axios.delete).toBeCalledWith(getUrl(`/recipes/${recipeData.id}`));
});
});
In test I get error "Error: Cannot read property 'data' of undefined"
However if I would not use axios instance and have code like below, the test would work.
//api.ts
const res = await axios.delete(`/recipes/${recipeId}`);
I'm pretty lost and stuck. I've tried a lot of things and some answers on similar problem that I've found on stackoverflow, but they didn't work for me. Anybody can help?
I don't want to mock axios module in mocks, only in specific test file.
I don't have also experience in Typescript and testing. This project I'm writing is to learn.
I found some workaround and at least it's working. I moved axiosInstance declaration to a separate module and then I mocked this module and delete function.
//RecipeItem.test.tsx
jest.mock('axiosInstance', () => ({
delete: jest.fn(),
}));
test('delete card after clicking delete button and user confirmation', async () => {
jest
.spyOn(axiosInstance, 'delete')
.mockImplementation(
jest.fn(() =>
Promise.resolve({ data: { deleted: 'true', id: `${recipeData.id}` } })
)
);
render(
<WrappedRecipeItem recipe={recipeData.fields} recipeId={recipeData.id} />
);
const deleteBtn = screen.getByRole('button', { name: /delete/i });
user.click(deleteBtn);
await waitFor(() => {
expect(axiosInstance.delete).toBeCalledWith(`/recipes/${recipeData.id}`);
});
});
If you have a better solution I would like to see it.

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.

Mocking a HttpClient with Jest

I'm having trouble with my React Native + Jest + Typescript setup.
I'm trying to test a thunk/network operation. I've created a networkClient function:
export const networkClient = async (
apiPath: string,
method = RequestType.GET,
body = {},
authenticate = true,
appState: IAppState,
dispatch: Dispatch<any>
) => {
... validate/renew token, validate request and stuff...
const queryParams = {
method,
headers: authenticate
? helpers.getHeadersWithAuth(tokenToUse)
: helpers.getBaseHeaders(),
body: method === RequestType.POST ? body : undefined,
};
const fullUri = baseURL + apiPath;
const result = await fetch(fullUri, queryParams);
if (result.ok) {
const json = await result.json();
console.log(`Result ${result.status} for request to ${fullUri}`);
return json;
} else {
... handle error codes
}
} catch (error) {
handleNetworkError(error, apiPath);
}
};
Now, when writing my tests for the operations which uses the networkClient above to request server data like so:
const uri = `/subscriptions/media` + tokenParam;
const json = await networkClient(
uri,
RequestType.GET,
undefined,
true,
getState(),
dispatch
);
I'd like to mock the implementation to return a mock response pr. test.
As pr the docs, I thought this could be done like so:
import { RequestType, networkClient} from './path/to/NetworkClient';
and in the test:
networkClient = jest.fn(
(
apiPath: string,
method = RequestType.GET,
body = {},
authenticate = true,
appState: IAppState,
dispatch: Dispatch<any>
) => {
return 'my test json';
}
);
const store = mockStore(initialState);
return store
.dispatch(operations.default.getMoreFeedData(false))
.then(() => {
expect(store.getActions()).toEqual(expectedActions);
expect(store.getState().feedData).toEqual(testFeed);
// fetchMock.restore();
});
but networkClient is not defined, and ts tells me
[ts] Cannot assign to 'networkClient' because it is not a variable.
What did I get wrong? I must have missed something about how Jest mocks modules and how to provide a mock implementation somewhere, but I can't find it on neither the docs, nor on Google/SO.
Any help is much appreciated
So I found the solution
The import should not be
import { RequestType, networkClient} from './path/to/NetworkClient';
but instead the module should be required like so:
const network = require('./../../../../networking/NetworkClient');
After that, i could successfully mock the implementation and complete the test:
const testFeed = { items: feed, token: 'nextpage' };
network.networkClient = jest.fn(() => {
return testFeed;
});
I hope it helps someone

Resources