I am trying to test a functional component (AddTodo). AddTodo is using a custom hooks which is returning todoList, addItemInTodoList and other functions. I’m mocking the custom hook and using mockResolvedValueOnce to set the todoList and other functions. Is this the right approach to test react component using custom hook.
Here is my AddTodo component
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import useTodoList from '../hooks/useTodoList';
import Button from '../Components/button';
import Input from '../Components/input';
function AddTodo() {
const navigate = useNavigate();
const { todoList, addItemInTodoList } = useTodoList();
const [todoText, setTodoText] = useState('');
const handleTodoTextChange = (e) => {
if(e.key === 'Enter') {
handleAddItemClick();
return;
};
setTodoText(e.target.value);
};
const handleAddItemClick = async () => {
let data = {
key: todoList.length + 1,
text: todoText,
date: new Date().toLocaleString()
};
const result = await addItemInTodoList(data);
if (result) {
navigate('/');
}
};
return (
<div className='flex flex-col w-25'>
<h2>Add Todo Item</h2>
<div className='flex'>
<Input placeholder='Write something' value={todoText} onChange={handleTodoTextChange} onKeyPress={handleTodoTextChange} />
<Button type='primary' value='Add' onClick={handleAddItemClick} />
</div>
</div>
);
}
export default AddTodo;
Here is my useTodo custom Hook
import { useState, useEffect } from 'react';
export default function useTodoList(todoItemKey) {
const CACHE_NAME = "todo-list";
const ENDPOINT = "https://localhost:3000";
const [todoList, setTodoList] = useState([]);
const [selectedTodoItem, setSelectedTodoItem] = useState({});
useEffect(() => {
const todoItem = todoList.find(item => item.key === parseInt(todoItemKey));
setSelectedTodoItem(todoItem);
}, [todoList]);
useEffect(() => {
const getTodoList = async () => {
try {
const cache = await caches.open(CACHE_NAME);
const response = await cache.match(ENDPOINT);
if (!response) {
return;
}
const responseBody = await response.json();
setTodoList(responseBody);
} catch (error) {
console.log("getToken error:", { error });
}
};
getTodoList();
}, []);
const addItemInTodoList = async (data) => {
const updatedTodoList = [...todoList, data]
try {
const cache = await caches.open(CACHE_NAME);
const responseBody = JSON.stringify(updatedTodoList);
const response = new Response(responseBody);
await cache.put(ENDPOINT, response);
return true;
} catch (error) {
console.log("saveToken error:", { error });
return false;
}
};
const updateItemInTodoList = async (data) => {
let updatedTodoList = todoList.filter(item => item.key !== data.key);
updatedTodoList = [...updatedTodoList, data];
updatedTodoList = updatedTodoList.sort((a, b) => new Date(a.date) - new Date(b.date));
try {
const cache = await caches.open(CACHE_NAME);
const responseBody = JSON.stringify(updatedTodoList);
const response = new Response(responseBody);
await cache.put(ENDPOINT, response);
return true;
} catch (error) {
console.log("saveToken error:", { error });
}
};
const deleteItemInTodoList = async (todoItemKey) => {
const updatedTodoList = todoList.filter(item => item.key !== todoItemKey);
try {
const cache = await caches.open(CACHE_NAME);
const responseBody = JSON.stringify(updatedTodoList);
const response = new Response(responseBody);
await cache.put(ENDPOINT, response);
setTodoList(updatedTodoList);
} catch (error) {
console.log("saveToken error:", { error });
}
};
return { todoList, addItemInTodoList, updateItemInTodoList, deleteItemInTodoList, selectedTodoItem, setSelectedTodoItem };
}
Here is my AddTodo test
import { fireEvent, getByText, render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { MemoryRouter } from 'react-router-dom';
import useTodoList from '../hooks/useTodoList';
import AddTodo from '../Pages/AddTodo';
jest.mock('../hooks/useTodoList');
afterEach(() => {
jest.clearAllMocks()
})
test('should render add todo page', () => {
useTodoList.mockResolvedValueOnce({ todoList: [], addItemInTodoList: () => { }, updateItemInTodoList: () => { }, deleteItemInTodoList: () => { }, selectedTodoItem: {}, setSelectedTodoItem: () => { } });
const { getByPlaceholderText } = render(
<MemoryRouter>
<AddTodo />
</MemoryRouter>
);
const input = getByPlaceholderText(/write something/i);
expect(input).toBeInTheDocument();
fireEvent.change(input, { target: { value: 'Todo Item 1'} });
const btn = getByText(/add/i);
fireEvent.click(btn);
expect(addItemInTodoList).toBeCalledWith(/todo item 1/i);
});