// @vitest-environment happy-dom import { render, screen, waitFor } from '@testing-library/react'; import { userEvent } from '@testing-library/user-event'; import React from 'react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { AccountsTab } from '../screens/config/accounts-tab.js'; import { AccountRecord } from '../../server/check-accounts.js'; function mockFetch(initial: AccountRecord[]) { let accounts = [...initial]; const fetchMock = vi.fn(async (url: string, options?: RequestInit) => { if (url === '/api/vault/accounts') { return { ok: true, json: async () => accounts } as Response; } const putMatch = (url as string).match(/\/api\/vault\/accounts\/(.+)/); if (putMatch && options?.method === 'PUT') { const id = putMatch[1]; const { status } = JSON.parse(options.body as string) as { status: AccountRecord['status'] }; accounts = accounts.map(a => (a.id === id ? { ...a, status } : a)); return { ok: true, json: async () => accounts } as Response; } return { ok: false, json: async () => ({}) } as Response; }); vi.stubGlobal('fetch', fetchMock); return fetchMock; } const SAMPLE_ACCOUNTS: AccountRecord[] = [ { id: 'acc-1', sourceId: 'src-a', sourceType: 'poalim', accountNumber: '123456', branchNumber: '700', status: 'pending', }, { id: 'acc-2', sourceId: 'src-a', sourceType: 'poalim', accountNumber: '789012', status: 'accepted', }, { id: 'acc-3', sourceId: 'src-b', sourceType: 'max', accountNumber: '333333', status: 'ignored', }, ]; beforeEach(() => { mockFetch([]); }); afterEach(() => { vi.unstubAllGlobals(); }); describe('AccountsTab', () => { it('renders empty state when no accounts', async () => { render(); await waitFor(() => { expect(screen.getByText(/no account records discovered/i)).toBeTruthy(); }); }); it('renders status badges for each account', async () => { vi.unstubAllGlobals(); mockFetch(SAMPLE_ACCOUNTS); render(); await waitFor(() => screen.getAllByText('pending')); expect(screen.getAllByText('accepted').length).toBeGreaterThan(0); expect(screen.getAllByText('ignored').length).toBeGreaterThan(0); }); it('shows pending notice for pending accounts', async () => { vi.unstubAllGlobals(); mockFetch(SAMPLE_ACCOUNTS); render(); await waitFor(() => { expect(screen.getByText(/visit the accounter client/i)).toBeTruthy(); }); }); it('changes status via dropdown', async () => { vi.unstubAllGlobals(); const fetchMock = mockFetch(SAMPLE_ACCOUNTS); render(); await waitFor(() => screen.getAllByRole('combobox')); const selects = screen.getAllByRole('combobox') as HTMLSelectElement[]; const pendingSelect = selects.find(s => s.value === 'pending')!; await userEvent.selectOptions(pendingSelect, 'accepted'); await waitFor(() => { const putCalls = fetchMock.mock.calls.filter( ([, opts]) => (opts as RequestInit | undefined)?.method === 'PUT', ); expect(putCalls.length).toBeGreaterThan(0); }); }); it('groups accounts by source', async () => { vi.unstubAllGlobals(); mockFetch(SAMPLE_ACCOUNTS); render(); await waitFor(() => { expect(screen.getByText(/Bank Hapoalim \(src-a\)/)).toBeTruthy(); expect(screen.getByText(/Max \(src-b\)/)).toBeTruthy(); }); }); it('after status change the badge updates', async () => { vi.unstubAllGlobals(); mockFetch([SAMPLE_ACCOUNTS[0]]); render(); await waitFor(() => screen.getAllByText('pending')); const select = screen.getByRole('combobox') as HTMLSelectElement; await userEvent.selectOptions(select, 'accepted'); await waitFor(() => { expect(screen.getAllByText('accepted').length).toBeGreaterThan(0); }); }); });