mirror of
https://github.com/actualbudget/actual.git
synced 2026-04-30 03:23:51 -05:00
Add tests for MobilePayeesPage component (#6506)
This commit is contained in:
committed by
GitHub
parent
384128ec50
commit
235d771d58
@@ -0,0 +1,169 @@
|
||||
import React from 'react';
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { type PayeeEntity } from 'loot-core/types/models';
|
||||
|
||||
import { MobilePayeesPage } from './MobilePayeesPage';
|
||||
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { usePayeeRuleCounts } from '@desktop-client/hooks/usePayeeRuleCounts';
|
||||
import { usePayees } from '@desktop-client/hooks/usePayees';
|
||||
import { TestProvider } from '@desktop-client/redux/mock';
|
||||
|
||||
vi.mock('@use-gesture/react', () => ({
|
||||
useDrag: vi.fn().mockReturnValue(() => ({})),
|
||||
}));
|
||||
vi.mock('@desktop-client/hooks/useNavigate');
|
||||
vi.mock('@desktop-client/hooks/usePayees');
|
||||
vi.mock('@desktop-client/hooks/usePayeeRuleCounts');
|
||||
|
||||
const mockPayees: PayeeEntity[] = [
|
||||
{
|
||||
id: 'payee-1',
|
||||
name: 'Grocery Store',
|
||||
favorite: false,
|
||||
},
|
||||
{
|
||||
id: 'payee-2',
|
||||
name: 'Gas Station',
|
||||
favorite: true,
|
||||
},
|
||||
{
|
||||
id: 'payee-3',
|
||||
name: 'Restaurant',
|
||||
favorite: false,
|
||||
},
|
||||
];
|
||||
|
||||
describe('MobilePayeesPage', () => {
|
||||
const mockNavigate = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
vi.mocked(useNavigate).mockReturnValue(mockNavigate);
|
||||
vi.mocked(usePayees).mockImplementation(() => mockPayees);
|
||||
vi.mocked(usePayeeRuleCounts).mockReturnValue({
|
||||
ruleCounts: new Map([
|
||||
['payee-1', 2],
|
||||
['payee-2', 0],
|
||||
]),
|
||||
isLoading: false,
|
||||
refetch: vi.fn().mockResolvedValue(undefined),
|
||||
});
|
||||
});
|
||||
|
||||
const renderPayeesPage = () => {
|
||||
return render(
|
||||
<TestProvider>
|
||||
<MobilePayeesPage />
|
||||
</TestProvider>,
|
||||
);
|
||||
};
|
||||
|
||||
it('renders payees page with header', () => {
|
||||
renderPayeesPage();
|
||||
|
||||
expect(
|
||||
screen.getByRole('heading', { name: /payees/i }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders search box', () => {
|
||||
renderPayeesPage();
|
||||
|
||||
const searchBox = screen.getByPlaceholderText(/filter payees/i);
|
||||
expect(searchBox).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays all payees when no filter is applied', () => {
|
||||
renderPayeesPage();
|
||||
|
||||
// All payees should be visible
|
||||
expect(screen.getByText('Grocery Store')).toBeInTheDocument();
|
||||
expect(screen.getByText('Gas Station')).toBeInTheDocument();
|
||||
expect(screen.getByText('Restaurant')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('filters payees by search term', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderPayeesPage();
|
||||
|
||||
const searchBox = screen.getByPlaceholderText(/filter payees/i);
|
||||
|
||||
await user.type(searchBox, 'Grocery');
|
||||
|
||||
// Only matching payee should be visible
|
||||
expect(screen.getByText('Grocery Store')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Gas Station')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Restaurant')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('filters payees case-insensitively', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderPayeesPage();
|
||||
|
||||
const searchBox = screen.getByPlaceholderText(/filter payees/i);
|
||||
|
||||
await user.type(searchBox, 'gas');
|
||||
|
||||
// Should find "Gas Station" regardless of case
|
||||
expect(screen.getByText('Gas Station')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Grocery Store')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('clears filter when search is cleared', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderPayeesPage();
|
||||
|
||||
const searchBox = screen.getByPlaceholderText(/filter payees/i);
|
||||
|
||||
await user.type(searchBox, 'Grocery');
|
||||
expect(screen.queryByText('Gas Station')).not.toBeInTheDocument();
|
||||
|
||||
await user.clear(searchBox);
|
||||
|
||||
// All payees should be visible again
|
||||
expect(screen.getByText('Grocery Store')).toBeInTheDocument();
|
||||
expect(screen.getByText('Gas Station')).toBeInTheDocument();
|
||||
expect(screen.getByText('Restaurant')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('navigates to payee edit page when payee is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderPayeesPage();
|
||||
|
||||
const payeeButton = screen.getByText('Grocery Store');
|
||||
await user.click(payeeButton);
|
||||
|
||||
expect(mockNavigate).toBeCalledWith('/payees/payee-1');
|
||||
});
|
||||
|
||||
it('shows empty state when no payees match filter', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderPayeesPage();
|
||||
|
||||
const searchBox = screen.getByPlaceholderText(/filter payees/i);
|
||||
|
||||
await user.type(searchBox, 'NonexistentPayee123');
|
||||
|
||||
// No payees should match
|
||||
expect(screen.queryByText('Grocery Store')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Gas Station')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Restaurant')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles empty payee list', () => {
|
||||
vi.mocked(usePayees).mockReturnValue([]);
|
||||
|
||||
renderPayeesPage();
|
||||
|
||||
// Page should render even with no payees
|
||||
expect(
|
||||
screen.getByRole('heading', { name: /payees/i }),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByPlaceholderText(/filter payees/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user