Testing tanstack-react-qiery - (wait for is not a function in renderHook) - react-testing-library

I want to test this customHook but it's showing me an error of
waitFor is not a function.
import { QueryClient, QueryClientProvider } from "#tanstack/react-query";
import { renderHook } from "#testing-library/react";
import { useCustomHookForTestCase } from "../useQueryCustomHook";
const queryClient = new QueryClient();
// jest.mock('../../../Utilies/Apicall')
describe('Testing custom hooks of react query', () => {
const wrapper = ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
it('Should get data of todos from api',async () => {
const { result, waitFor } = await renderHook(() => useCustomHookForTestCase(), { wrapper });
await waitFor(() => result.current.isSuccess );
expect(result.current.data).toEqual("Hello");
})
})
export function useCustomHookForTestCase() {
return useQuery(['customHook'], () => 'Hello');
}

only renderHook from react-hook-testing-library returns a waitFor method. It is not available if you use the one from #testing-library/react-hooks. For that to work, you just need to import waitFor directly. You also can't return a boolean directly, but it needs to be a promise:
import { renderHook, waitFor } from '#testing-library/react'
await waitFor(() => expect(result.current.isSuccess).toBe(true))

Related

Jest mock Typeorm Datasource in unit tests (without nestjs)

I am working on creating unit tests for a project that uses Typeorm without Nestjs. The file I am creating unit tests for uses queryRunner to start a transaction. My problem is, I am not able to mock the Datasource. I tried multiple ways but the mock is never getting called to replace the actual Datasource that has access to the postgresql database. I saw some solutions to mock it, but they all use Nestjs, which I don't use in my case.
The error I am having in the tests right now is:
Received promise rejected instead of resolved
Rejected to value: [TypeORMError: Driver not Connected]
Any help would be highly appreciated since I'm not an expert in unit tests.
Here's an example of the code that I am having the issue with (replaced some names tho):
datasource.ts
//database config is defined in this file
export const datasource: DataSource = new DataSource(some_config);
dummy.service.ts
export const dummyService = () => {
//datasource is imported from the above file
const queryRunner = datasource.createQueryRunner();
await queryRunner.startTransaction();
try {
const foundObject = await queryRunner.manager.getRepository(MyObject).findOne({
where: { id: someId },
lock: { mode: 'pessimistic_write' },
});
//some more database calls
} catch (error) {
await queryRunner.rollbackTransaction();
} finally {
await queryRunner.release();
}
}
mock.datasource.ts
import { DataSource } from 'typeorm';
export const dataSourceMockFactory: () => MockType<DataSource> = jest.fn(
() => ({
createQueryRunner: jest.fn().mockImplementation(() => ({
connect: jest.fn(),
startTransaction: jest.fn(),
release: jest.fn(),
rollbackTransaction: jest.fn(),
manager: {
getRepository: jest.fn().mockImplementation(() => ({
create: jest.fn(),
findOne: jest.fn(() => {
return getMyDummyObject();
}),
})),
save: jest.fn(),
},
})),
}),
);
export type MockType<T> = {
// eslint-disable-next-line #typescript-eslint/ban-types
[P in keyof T]?: jest.Mock<{}>;
};
dummy.unit.test.ts
describe('dummy service test', () => {
let dataSourceMock: MockType<Typeorm.DataSource>;
beforeEach(async () => {
// This method did not work
jest.mock('typeorm', () => {
const actual = jest.requireActual('typeorm');
return {
...actual,
DataSource: dataSourceMockFactory(),
};
});
// The below method did not work either
jest.mock('./../../db/datasource', () => ({
datasource: dataSourceMockFactory(),
}));
});
afterEach(() => {
jest.clearAllMocks();
});
it('should test dummy service with transaction', async () => {
// getting an error here
await expect(
dummyFunction(),
).resolves.not.toThrow();
});
});

Redux Toolkit state doesn't update even after adding extraReducers

I have a very limited understanding of the redux toolkit compared to its previous version. I am struggling to understand why isn't my state getting updated on the trigger of getUsers.
I have added the extraReducers as well.
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import { fetchSample } from './filterAPI';
export const getUsers = createAsyncThunk(
'primaryFilters/getUsers',
async (dispatch, getState) => {
console.log(getState, dispatch);
const response = await fetchSample();
return response;
}
);
const primaryFiltersSlice = createSlice({
name: 'primaryFilters',
initialState: {
dateFilter: {
dates: {
key: 'selection',
startDate: new Date(),
endDate: new Date(),
},
dummyData: null,
},
status: null,
},
extraReducers: (builder) => {
builder
.addCase(getUsers.pending, (state) => {
state.status = 'loading';
})
.addCase(getUsers.fulfilled, (state, action) => {
state.status = 'idle';
state.dummyData = action.payload;
})
.addCase(getUsers.rejected, (state, action) => {
state.status = 'failed';
});
},
});
export default primaryFiltersSlice.reducer;
Here's fetchSample function:
export const fetchSample = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
return response.json();
};
Additionally, I want to point out that my status is triggering from pending to idle and so on but my actual state isn't updating.
Here's the screenshot for the same:
I would also like to know how can we log the messages within those extraReducers.
For one, looking at your data structure, you probably want to update state.dateFilter.dummyData, not state.dummyData - at least assuming you want to match your initialState structure.
Also, createAsyncThunk does not take a callback dispatch, getState:
correct would be
export const getUsers = createAsyncThunk(
'primaryFilters/getUsers',
async (arg, {dispatch, getState}) => {
(but that does not make a difference here since you are using neither)
As for logging... just console.log? Or do you mean you are getting a Proxz object? In that case console.log(current(state))
For some reason, I am able to dispatch actions by keeping the status update at the bottom instead of the top. I would love to have a better explanation for the same, here's what exactly fixed my code:
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import { fetchSample } from './filterAPI';
export const getUsers = createAsyncThunk(
'primaryFilters/getUsers',
async (dispatch, getState) => {
console.log(getState, dispatch);
const response = await fetchSample();
return response;
}
);
// export const updateDates = () => {
// }
const primaryFiltersSlice = createSlice({
name: 'primaryFilters',
initialState: {
dateFilter: {
dates: {
key: 'selection',
startDate: new Date(),
endDate: new Date(),
},
dummyData: null,
},
status: null,
},
reducer: {
updateDate: (state, action) => {
console.log('Actions = ', action);
},
},
extraReducers: (builder) => {
builder
.addCase(getUsers.pending, (state) => {
state.status = 'loading';
})
.addCase(getUsers.fulfilled, (state, action) => {
state.dummyData = action.payload;
state.status = 'idle';
})
.addCase(getUsers.rejected, (state, action) => {
state.status = 'failed';
});
},
});
export default primaryFiltersSlice.reducer;

How to await a build task in a VS Code extension?

let result = await vscode.commands.executeCommand('workbench.action.tasks.build');
resolves immediately.
How can I await a build task with VS Code API?
I figured it out! Tasks cannot be awaited from vscode.tasks.executeTask, but we can await vscode.tasks.onDidEndTask and check if ended task is our task.
async function executeBuildTask(task: vscode.Task) {
const execution = await vscode.tasks.executeTask(task);
return new Promise<void>(resolve => {
let disposable = vscode.tasks.onDidEndTask(e => {
if (e.execution.task.group === vscode.TaskGroup.Build) {
disposable.dispose();
resolve();
}
});
});
}
async function getBuildTasks() {
return new Promise<vscode.Task[]>(resolve => {
vscode.tasks.fetchTasks().then((tasks) => {
resolve(tasks.filter((task) => task.group === vscode.TaskGroup.Build));
});
});
}
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.commands.registerCommand('extension.helloWorld', async () => {
const buildTasks = await getBuildTasks();
await executeBuildTask(buildTasks[0]);
}));
}
Note that currently there is a bug #96643, which prevents us from doing a comparison of vscode.Task objects: if (e.execution.task === execution.task) { ... }
I think this depends on how the main command is executed in the extension.ts
Being new to JS/TS, I may be wrong here, but just trying to help:
make sure the vscode.command.registerCommand is not asyncronous, like below:
context.subscriptions.push(vscode.commands.registerCommand('extension.openSettings', () => {
return vscode.commands.executeCommand("workbench.action.openSettings", "settingsName");
}));
This would be compared to something async, like below:
context.subscriptions.push(vscode.commands.registerCommand('extension.removeHost', async (hostID) => {
const bigipHosts: Array<string> | undefined = vscode.workspace.getConfiguration().get('extension.hosts');
const newHosts = Hosts?.filter( item => item != hostID.label)
await vscode.workspace.getConfiguration().update('f5-fast.hosts', newBigipHosts, vscode.ConfigurationTarget.Global);
hostsTreeProvider.refresh();
}));

Mocking classes with Jest

Given I have a class that calls a function doStuff like so:
const myService = require(`./myService`),
service = new myService();
exports.doStuff = async (callback) => {
try {
const data = await service.doOtherStuff(); //I want to mock this
return callback(null, data);
} catch (err) {
callback(new Error(` > feed failed for ${key}, error: ${err}`));
}
};
my tests work like this:
const myClass = require(`../index`);
jest.mock(`./../myService`, () => {
return function() {
return {
doOtherStuff: () => {
return 1;
}
};
};
});
describe(`service-tests - index`, () => {
test(`doStuff and test otherStuff`, async () => {
const result = await myClass.doStuff((err, data) => {
return data;
});
expect(result).toBe(1);
});
});
And my service:
class myService{
constructor() {
//Do constructor stuff
}
async doOtherStuff() {
//Do other stuff
}
This works but now I just have my mock on this file and not by test.
What I need is to have my mock be changable test by test but cannot seem to figure out how this works together with require.
I tried just doing jest.mock('./../myService') and have the mockImplementation on the beforeAll but that will keep my function in mocked as the automatic mock it seems, returning undefined
Anyone ever done this before?
If you want to mock a method within a class, which it looks like you're doing, I would suggest you use jest.spyOn. It's simple, and you can mock the return value to be whatever you want per test.
const myClass = require('../index');
const myService = require('../myService');
describe('service-tests - index', () => {
let doOtherStuffMock;
beforeAll(() => {
doOtherStuffMock = jest.spyOn(myService.prototype, 'doOtherStuff');
});
it('mocks doOtherStuff one way', async () => {
doOtherStuffMock.mockResolvedValue('I am a mocked value');
const result = await myClass.doStuff((err, data) => data);
expect(result).toBe("I am a mocked value");
});
it('mocks doOtherStuff another way', async () => {
doOtherStuffMock.mockResolvedValue('I am DIFFERENT mocked value');
const result = await myClass.doStuff((err, data) => data);
expect(result).toBe('I am DIFFERENT mocked value');
});
});
Update
I'll leave this answer here since both of these approaches work and they can be useful...
...but for this particular case #Daniel is right, mocking the prototype method is easiest
An easy way to handle this is to mock myService.js as a singleton...
...then you can grab the mock function for doOtherStuff and change it per test:
const myClass = require(`../index`);
jest.mock(`../myService`, () => {
const doOtherStuff = jest.fn(); // <= create a mock function for doOtherStuff
const result = { doOtherStuff };
return function() { return result; }; // <= always return the same object
});
const doOtherStuff = require('./myService')().doOtherStuff; // <= get doOtherStuff
describe(`service-tests - index`, () => {
test(`doStuff and test otherStuff`, async () => {
doOtherStuff.mockReturnValue(1); // <= mock it to return something
const result = await myClass.doStuff((err, data) => data);
expect(result).toBe(1); // Success!
});
test(`doStuff and test otherStuff`, async () => {
doOtherStuff.mockReturnValue('something else'); // <= mock it to return something else
const result = await myClass.doStuff((err, data) => data);
expect(result).toBe('something else'); // Success!
});
});
It also works to auto-mock myService.js and use mockImplementation...
...but since index.js creates a myService instance as soon as it runs you have to make sure the mock is in place before you require index.js:
jest.mock(`../myService`); // <= auto-mock
const myService = require('../myService');
const doOtherStuff = jest.fn();
myService.mockImplementation(function() { return { doOtherStuff }; });
const myClass = require(`../index`); // <= now require index
describe(`service-tests - index`, () => {
test(`doStuff and test otherStuff`, async () => {
doOtherStuff.mockReturnValue(1); // <= mock it to return something
const result = await myClass.doStuff((err, data) => data);
expect(result).toBe(1); // Success!
});
test(`doStuff and test otherStuff`, async () => {
doOtherStuff.mockReturnValue('something else'); // <= mock it to return something else
const result = await myClass.doStuff((err, data) => data);
expect(result).toBe('something else'); // Success!
});
});

How to test axios interceptors using jest?

I'm trying to test the following code:
import axios from 'axios';
import { history } from './ReduxService';
axios.interceptors.response.use(response => response,
(error) => {
if ((error.response && error.response.status === 408) || error.code === 'ECONNABORTED') {
history.push('/error');
}
return Promise.reject(error);
}
);
Any advice on how to cover it?
First, modify the code so that you can pass a mocked version of axios in:
import axios, { AxiosInstance } from 'axios';
import { history } from './ReduxService';
export const addResponseInterceptor(client: AxiosInstance) => {
client.interceptors.response.use(response => response,
(error) => {
if ((error.response && error.response.status === 408) || error.code ===
'ECONNABORTED') {
history.push('/error');
}
return Promise.reject(error);
});
};
Then set up your tests like this:
import { addResponseInterceptor } from './yourInterceptorFile'
import axios from 'axios';
jest.mock('axios', () => {
return {
create: jest.fn(),
interceptors: {
request: {
use: jest.fn(),
eject: jest.fn(),
},
response: {
use: jest.fn(),
eject: jest.fn(),
},
}
};
});
describe('addResponseInterceptor tests', () => {
beforeEach(() => {
(axios.create as jest.Mock).mockReset();
(axios.interceptors.request.use as jest.Mock).mockReset();
(axios.interceptors.request.eject as jest.Mock).mockReset();
(axios.interceptors.response.use as jest.Mock).mockReset();
(axios.interceptors.response.eject as jest.Mock).mockReset();
});
it('should add a response interceptor to the axios instance', () => {
addResponseInterceptor(axios);
expect(axios.interceptors.response.use).toHaveBeenCalled();
});
it('should push to history when an error occurs', async() => {
const historySpy = jest.spyOn(history, 'push');
const axiosClient = axios;
addResponseInterceptor(axios);
const interceptorErrorHandler = (axiosClient.interceptors.response.use as jest.Mock).mock.calls[0][1];
try {
await interceptorErrorHandler({
response: {
status: 408
}
});
//this should not be called--the promise should be rejected
expect(true).toBe(false);
} catch {
expect(historySpy).toHaveBeenCalledWith('/error');
}
});
. . .
});