diff --git a/frontend/src/entity/cards/ui/AddBankCardForm/ui/AddBankCardForm.test.tsx b/frontend/src/entity/cards/ui/AddBankCardForm/ui/AddBankCardForm.test.tsx index ecd0cc3..0972620 100644 --- a/frontend/src/entity/cards/ui/AddBankCardForm/ui/AddBankCardForm.test.tsx +++ b/frontend/src/entity/cards/ui/AddBankCardForm/ui/AddBankCardForm.test.tsx @@ -1,59 +1,29 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { AddBankCardFormProps } from './AddBankCardForm.ui'; - -import AddBankCardForm from './AddBankCardForm.ui'; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render(); - - const nameInputElem = screen.getByLabelText('Название'); - const balanceInputElem = screen.getByLabelText('Баланс'); - const submitButtonElem = screen.getByText('Добавить'); - - return { - balanceInputElem, - nameInputElem, - onSubmit, - submitButtonElem, - }; -}; +import { AddBankCardFormHelper } from '@tests/helpers'; describe('Test AddBankCardForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('add-balance-card-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + AddBankCardFormHelper.renderForm(); + const { formElem } = AddBankCardFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); + AddBankCardFormHelper.renderForm(options); + const { formElem, ...formElems } = AddBankCardFormHelper.getFormElements(); Object.values(formElems).forEach(assertion); }); test('should call onSubmit with values', async () => { - const { - balanceInputElem, - nameInputElem, - onSubmit, - submitButtonElem, - } = renderForm(); + const { onSubmit } = AddBankCardFormHelper.renderForm(); - await userEvent.type(nameInputElem, 'Тестовая карта'); - await userEvent.type(balanceInputElem, '500'); - - await userEvent.click(submitButtonElem); + await AddBankCardFormHelper.fillForm({ + balance: 500, + name: 'Тестовая карта', + }); expect(onSubmit).toHaveBeenCalledTimes(1); expect(onSubmit).toHaveBeenCalledWith({ diff --git a/frontend/src/entity/cards/ui/DeleteBankCardForm/ui/DeleteBankCardForm.test.tsx b/frontend/src/entity/cards/ui/DeleteBankCardForm/ui/DeleteBankCardForm.test.tsx index b62ac95..eeb5d84 100644 --- a/frontend/src/entity/cards/ui/DeleteBankCardForm/ui/DeleteBankCardForm.test.tsx +++ b/frontend/src/entity/cards/ui/DeleteBankCardForm/ui/DeleteBankCardForm.test.tsx @@ -1,55 +1,18 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { DeleteBankCardFormProps } from './DeleteBankCardForm.ui'; - -import DeleteBankCardForm from './DeleteBankCardForm.ui'; - -const deletingBankCard: DeleteBankCardFormProps['bankCard'] = { - balance: 1000, - createdAt: '2025-08-29T07:57:40.503Z', - id: 'af6da4e1-f24f-4b43-8556-fa885626897d', - name: 'Тестовая карта', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const submitButtonElem = screen.getByText('Удалить'); - - return { - onSubmit, - submitButtonElem, - }; -}; +import { DeleteBankCardFormHelper } from '@tests/helpers'; describe('Test DeleteExpensesListForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('delete-bank-card-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + DeleteBankCardFormHelper.renderForm(); + const { formElem } = DeleteBankCardFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should call onSubmit with values', async () => { - const { - onSubmit, - submitButtonElem, - } = renderForm(); - - await userEvent.click(submitButtonElem); + const { onSubmit } = DeleteBankCardFormHelper.renderForm(); + await DeleteBankCardFormHelper.submitForm(); expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith(deletingBankCard); + expect(onSubmit).toHaveBeenCalledWith(DeleteBankCardFormHelper.bankCard); }); }); diff --git a/frontend/src/entity/cards/ui/EditBankCardForm/ui/EditBankCardForm.test.tsx b/frontend/src/entity/cards/ui/EditBankCardForm/ui/EditBankCardForm.test.tsx index 2854526..aee8856 100644 --- a/frontend/src/entity/cards/ui/EditBankCardForm/ui/EditBankCardForm.test.tsx +++ b/frontend/src/entity/cards/ui/EditBankCardForm/ui/EditBankCardForm.test.tsx @@ -1,91 +1,38 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { EditBankCardFormProps } from './EditBankCardForm.ui'; - -import EditBankCardForm from './EditBankCardForm.ui'; - -const editingBankCard = { - balance: 1000, - createdAt: '2025-08-29T07:57:40.503Z', - id: 'af6da4e1-f24f-4b43-8556-fa885626897d', - name: 'Тестовая карта', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const titleInputElem = screen.getByLabelText('Название'); - const balanceInputElem = screen.getByLabelText('Баланс'); - const submitButtonElem = screen.getByText('Сохранить'); - - return { - balanceInputElem, - onSubmit, - submitButtonElem, - titleInputElem, - }; -}; +import { EditBankCardFormHelper } from '@tests/helpers'; describe('Test EditBankCardForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('edit-bank-card-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + EditBankCardFormHelper.renderForm(); + const { formElem } = EditBankCardFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should inputs have default value', () => { - const { - balanceInputElem, - titleInputElem, - } = renderForm(); - expect(titleInputElem).toHaveValue('Тестовая карта'); - expect(balanceInputElem).toHaveValue(1000); + EditBankCardFormHelper.renderForm(); + const { balanceInputElem, nameInputElem } = EditBankCardFormHelper.getFormElements(); + expect(nameInputElem).toHaveValue('Сбербанк'); + expect(balanceInputElem).toHaveValue('0'); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); + EditBankCardFormHelper.renderForm(options); + const { formElem, ...formElems } = EditBankCardFormHelper.getFormElements(); Object.values(formElems).forEach(assertion); }); test('should call onSubmit with values', async () => { - const { - balanceInputElem, - onSubmit, - submitButtonElem, - titleInputElem, - } = renderForm(); + const { onSubmit } = EditBankCardFormHelper.renderForm(); - await userEvent.clear(titleInputElem); - await userEvent.type(titleInputElem, 'Новое название карты'); - - await userEvent.clear(balanceInputElem); - await userEvent.type(balanceInputElem, '5000'); - - await userEvent.click(submitButtonElem); - - expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith({ + const data = { balance: 5000, name: 'Новое название карты', - }); + }; + await EditBankCardFormHelper.fillForm(data); + expect(onSubmit).toHaveBeenCalledTimes(1); + expect(onSubmit).toHaveBeenCalledWith(data); }); }); diff --git a/frontend/src/entity/cash/ui/AddCashForm/ui/AddCashForm.test.tsx b/frontend/src/entity/cash/ui/AddCashForm/ui/AddCashForm.test.tsx index b1a0694..d3ea080 100644 --- a/frontend/src/entity/cash/ui/AddCashForm/ui/AddCashForm.test.tsx +++ b/frontend/src/entity/cash/ui/AddCashForm/ui/AddCashForm.test.tsx @@ -1,64 +1,28 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { AddCashFormProps } from './AddCashForm.ui'; - -import AddCashForm from './AddCashForm.ui'; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render(); - - const nameInputElem = screen.getByLabelText('Название'); - const balanceInputElem = screen.getByLabelText('Баланс'); - const submitButtonElem = screen.getByText('Добавить'); - - return { - balanceInputElem, - nameInputElem, - onSubmit, - submitButtonElem, - }; -}; +import { AddCashFormHelper } from '@tests/helpers'; describe('Test AddCashForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('add-cash-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + AddCashFormHelper.renderForm(); + const { formElem } = AddCashFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); + AddCashFormHelper.renderForm(options); + const { formElem, ...formElems } = AddCashFormHelper.getFormElements(); Object.values(formElems).forEach(assertion); }); test('should call onSubmit with values', async () => { - const { - balanceInputElem, - nameInputElem, - onSubmit, - submitButtonElem, - } = renderForm(); - - await userEvent.type(nameInputElem, 'Наличные'); - await userEvent.type(balanceInputElem, '500'); - - await userEvent.click(submitButtonElem); + const { onSubmit } = AddCashFormHelper.renderForm(); + const data = { balance: 500, name: 'Наличные' }; + await AddCashFormHelper.fillForm(data); expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith({ - balance: 500, - name: 'Наличные', - }); + expect(onSubmit).toHaveBeenCalledWith(data); }); }); diff --git a/frontend/src/entity/cash/ui/DeleteCashForm/ui/DeleteCashForm.test.tsx b/frontend/src/entity/cash/ui/DeleteCashForm/ui/DeleteCashForm.test.tsx index dfbab58..c376fbb 100644 --- a/frontend/src/entity/cash/ui/DeleteCashForm/ui/DeleteCashForm.test.tsx +++ b/frontend/src/entity/cash/ui/DeleteCashForm/ui/DeleteCashForm.test.tsx @@ -1,55 +1,19 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { DeleteCashFormProps } from './DeleteCashForm.ui'; - -import DeleteCashForm from './DeleteCashForm.ui'; - -const deletingCash: DeleteCashFormProps['cash'] = { - balance: 1000, - createdAt: '2025-08-29T07:57:40.503Z', - id: 'af6da4e1-f24f-4b43-8556-fa885626897d', - name: 'Наличные', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const submitButtonElem = screen.getByText('Удалить'); - - return { - onSubmit, - submitButtonElem, - }; -}; +import { DeleteCashFormHelper } from '@tests/helpers'; describe('Test DeleteCashForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('delete-cash-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + DeleteCashFormHelper.renderForm(); + const { formElem } = DeleteCashFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should call onSubmit with values', async () => { - const { - onSubmit, - submitButtonElem, - } = renderForm(); + const { onSubmit } = DeleteCashFormHelper.renderForm(); - await userEvent.click(submitButtonElem); + await DeleteCashFormHelper.submitForm(); expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith(deletingCash); + expect(onSubmit).toHaveBeenCalledWith(DeleteCashFormHelper.cash); }); }); diff --git a/frontend/src/entity/cash/ui/EditCashForm/ui/EditCashForm.test.tsx b/frontend/src/entity/cash/ui/EditCashForm/ui/EditCashForm.test.tsx index b88116d..a7f91df 100644 --- a/frontend/src/entity/cash/ui/EditCashForm/ui/EditCashForm.test.tsx +++ b/frontend/src/entity/cash/ui/EditCashForm/ui/EditCashForm.test.tsx @@ -1,91 +1,35 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { EditCashFormProps } from './EditCashForm.ui'; - -import EditCashForm from './EditCashForm.ui'; - -const editingCash = { - balance: 1000, - createdAt: '2025-08-29T07:57:40.503Z', - id: 'af6da4e1-f24f-4b43-8556-fa885626897d', - name: 'Наличные', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const titleInputElem = screen.getByLabelText('Название'); - const balanceInputElem = screen.getByLabelText('Баланс'); - const submitButtonElem = screen.getByText('Сохранить'); - - return { - balanceInputElem, - onSubmit, - submitButtonElem, - titleInputElem, - }; -}; +import { EditCashFormHelper } from '@tests/helpers'; describe('Test EditCashForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('edit-bank-card-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + EditCashFormHelper.renderForm(); + const { formElem } = EditCashFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should inputs have default value', () => { - const { - balanceInputElem, - titleInputElem, - } = renderForm(); - expect(titleInputElem).toHaveValue('Наличные'); - expect(balanceInputElem).toHaveValue(1000); + EditCashFormHelper.renderForm(); + const { balanceInputElem, nameInputElem } = EditCashFormHelper.getFormElements(); + expect(nameInputElem).toHaveValue('Аренда квартиры'); + expect(balanceInputElem).toHaveValue('41300'); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); + EditCashFormHelper.renderForm(options); + const { formElem, ...formElems } = EditCashFormHelper.getFormElements(); Object.values(formElems).forEach(assertion); }); test('should call onSubmit with values', async () => { - const { - balanceInputElem, - onSubmit, - submitButtonElem, - titleInputElem, - } = renderForm(); - - await userEvent.clear(titleInputElem); - await userEvent.type(titleInputElem, 'Новое название наличных'); - - await userEvent.clear(balanceInputElem); - await userEvent.type(balanceInputElem, '5000'); - - await userEvent.click(submitButtonElem); + const { onSubmit } = EditCashFormHelper.renderForm(); + const data = { balance: 5000, name: 'Новое название наличных' }; + await EditCashFormHelper.fillForm(data); expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith({ - balance: 5000, - name: 'Новое название наличных', - }); + expect(onSubmit).toHaveBeenCalledWith(data); }); }); diff --git a/frontend/src/entity/expenses/item/ui/AddExpenseItemForm/AddExpenseItemForm.test.tsx b/frontend/src/entity/expenses/item/ui/AddExpenseItemForm/AddExpenseItemForm.test.tsx index c9f1abb..c69597f 100644 --- a/frontend/src/entity/expenses/item/ui/AddExpenseItemForm/AddExpenseItemForm.test.tsx +++ b/frontend/src/entity/expenses/item/ui/AddExpenseItemForm/AddExpenseItemForm.test.tsx @@ -1,184 +1,55 @@ import { bankCardList, cashList } from '@mocks/browser/data'; -import { getBankCardList, getCashList } from '@mocks/browser/handlers'; -import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { BankCard } from '@/entity/cards'; -import type { CashItem } from '@/entity/cash'; - -import type { AddExpenseItemFormProps } from './AddExpenseItemForm.ui'; - -import AddExpenseItemForm from './AddExpenseItemForm.ui'; - -const getExpenseItemFormElements = () => { - const titleInputElem = screen.getByLabelText('Название'); - const descriptionInputElem = screen.getByLabelText('Описание'); - const dateInputElem = screen.getByLabelText('Дата'); - const amountInputElem = screen.getByLabelText('Сумма'); - const countInputElem = screen.getByLabelText('Количество'); - const submitButtonElem = screen.getByText('Добавить'); - const paymentTypeSelect = screen.getByLabelText('Способ оплаты'); - const bankCardSelect = screen.queryByLabelText('Карта'); - const cashSelect = screen.queryByLabelText('Наличные'); - - return { - amountInputElem, - bankCardSelect, - cashSelect, - countInputElem, - dateInputElem, - descriptionInputElem, - paymentTypeSelect, - submitButtonElem, - titleInputElem, - }; -}; - -type ExpenseFormValues = { - amount?: string; - count?: string; - date?: string; - description?: string; - // eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents - paymentId?: BankCard['id'] | CashItem['id']; - paymentType?: 'BANK_CARD' | 'CASH' | 'none'; - title?: string; -}; - -// eslint-disable-next-line jest/no-export -export const fillAddExpenseItemForm = async (data: ExpenseFormValues) => { - const { - amount, - count, - date, - description, - paymentId, - paymentType = 'none', - title, - } = data; - const { - amountInputElem, - // bankCardSelect, - // cashSelect, - countInputElem, - dateInputElem, - descriptionInputElem, - paymentTypeSelect, - titleInputElem, - } = getExpenseItemFormElements(); - - if (title) { - await userEvent.clear(titleInputElem); - await userEvent.type(titleInputElem, title); - } - - if (description) { - await userEvent.clear(descriptionInputElem); - await userEvent.type(descriptionInputElem, description); - } - - if (date) { - await userEvent.clear(dateInputElem); - await userEvent.type(dateInputElem, date); - } - - if (amount) { - await userEvent.clear(amountInputElem); - await userEvent.type(amountInputElem, amount); - } - - if (count) { - await userEvent.clear(countInputElem); - await userEvent.type(countInputElem, count); - } - - if (paymentType !== 'none') { - await userEvent.selectOptions(paymentTypeSelect, paymentType); - const { bankCardSelect, cashSelect } = getExpenseItemFormElements(); - - if (paymentType === 'BANK_CARD' && bankCardSelect && paymentId) { - await userEvent.selectOptions(bankCardSelect, paymentId); - } else if (paymentType === 'CASH' && cashSelect && paymentId) { - await userEvent.selectOptions(cashSelect, paymentId); - } - } -}; - -const renderForm = (props?: Partial) => { - server.use(getBankCardList, getCashList); - const onSubmit = jest.fn(); - - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const { - amountInputElem, - countInputElem, - dateInputElem, - descriptionInputElem, - submitButtonElem, - titleInputElem, - } = getExpenseItemFormElements(); - - return { - amountInputElem, - countInputElem, - dateInputElem, - descriptionInputElem, - onSubmit, - submitButtonElem, - titleInputElem, - }; -}; +import { within } from '@testing-library/react'; +import { AddExpenseItemFormHelper } from '@tests/helpers'; describe('Test AddExpenseItemForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('add-expense-item-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + AddExpenseItemFormHelper.renderForm(); + const { formElem } = AddExpenseItemFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); - Object.values(formElems).forEach(assertion); + AddExpenseItemFormHelper.renderForm(options); + const { + bankCardSelect, + bankCardSelectInput, + cashSelect, + cashSelectInput, + formElem, + paymentTypeSelect, + ...formElems + } = AddExpenseItemFormHelper.getFormElements(); + [ + ...Object.values(formElems), + within(paymentTypeSelect).getByTestId('payment-type-select-input'), + ].forEach(assertion); }); describe('should call onSubmit with values', () => { const setup = async () => { - const { - onSubmit, - submitButtonElem, - } = renderForm(); + const { onSubmit } = AddExpenseItemFormHelper.renderForm(); - await fillAddExpenseItemForm({ + await AddExpenseItemFormHelper.fillForm({ amount: '12', count: '1', - date: '2025-08-29', + date: '29.08.2025', description: 'Описание элемента', title: 'Название элемента', - }); + }, { autoSubmit: false }); return { onSubmit, - submitButtonElem, }; }; test('no payment', async () => { - const { onSubmit, submitButtonElem } = await setup(); - await userEvent.click(submitButtonElem); + const { onSubmit } = await setup(); + await AddExpenseItemFormHelper.submitForm(); expect(onSubmit).toHaveBeenCalledTimes(1); expect(onSubmit).toHaveBeenCalledWith({ @@ -193,16 +64,18 @@ describe('Test AddExpenseItemForm', () => { }); test('bank card payment', async () => { - const { onSubmit, submitButtonElem } = await setup(); - await fillAddExpenseItemForm({ paymentType: 'BANK_CARD' }); - const { bankCardSelect } = getExpenseItemFormElements(); + const { onSubmit } = await setup(); + await AddExpenseItemFormHelper.fillForm({ paymentType: 'BANK_CARD' }, { autoSubmit: false }); + + const { bankCardSelect, bankCardSelectInput } = AddExpenseItemFormHelper.getFormElements(); expect(bankCardSelect).toBeVisible(); - expect(bankCardSelect).toBeEnabled(); - expect(bankCardSelect).toHaveValue(bankCardList[0].id); + expect(bankCardSelectInput).toBeEnabled(); + expect(bankCardSelectInput).toHaveValue(bankCardList[0].id); - await fillAddExpenseItemForm({ paymentId: bankCardList[1].id, paymentType: 'BANK_CARD' }); + await AddExpenseItemFormHelper.fillForm({ paymentId: bankCardList[1].id, paymentType: 'BANK_CARD' }, { autoSubmit: false }); + expect(bankCardSelectInput).toHaveValue(bankCardList[1].id); - await userEvent.click(submitButtonElem); + await AddExpenseItemFormHelper.submitForm(); expect(onSubmit).toHaveBeenCalledTimes(1); expect(onSubmit).toHaveBeenCalledWith({ @@ -220,17 +93,15 @@ describe('Test AddExpenseItemForm', () => { }); test('cash payment', async () => { - const { onSubmit, submitButtonElem } = await setup(); + const { onSubmit } = await setup(); - await fillAddExpenseItemForm({ paymentType: 'CASH' }); - const { cashSelect } = getExpenseItemFormElements(); + await AddExpenseItemFormHelper.fillForm({ paymentType: 'CASH' }); + const { cashSelect, cashSelectInput } = AddExpenseItemFormHelper.getFormElements(); expect(cashSelect).toBeVisible(); - expect(cashSelect).toBeEnabled(); - expect(cashSelect).toHaveValue(cashList[0].id); + expect(cashSelectInput).toBeEnabled(); + expect(cashSelectInput).toHaveValue(cashList[0].id); - await fillAddExpenseItemForm({ paymentId: cashList[1].id, paymentType: 'CASH' }); - - await userEvent.click(submitButtonElem); + await AddExpenseItemFormHelper.fillForm({ paymentId: cashList[1].id, paymentType: 'CASH' }); expect(onSubmit).toHaveBeenCalledTimes(1); expect(onSubmit).toHaveBeenCalledWith({ diff --git a/frontend/src/entity/expenses/item/ui/DeleteExpenseItemForm/DeleteExpenseItemForm.test.tsx b/frontend/src/entity/expenses/item/ui/DeleteExpenseItemForm/DeleteExpenseItemForm.test.tsx index 7b00cc8..b1475ae 100644 --- a/frontend/src/entity/expenses/item/ui/DeleteExpenseItemForm/DeleteExpenseItemForm.test.tsx +++ b/frontend/src/entity/expenses/item/ui/DeleteExpenseItemForm/DeleteExpenseItemForm.test.tsx @@ -1,61 +1,18 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { DeleteExpenseItemFormProps } from './DeleteExpenseItemForm.ui'; - -import DeleteExpenseItemForm from './DeleteExpenseItemForm.ui'; - -const deletingItem = { - amount: 12, - count: 12, - createdAt: '2025-08-29T07:57:40.503Z', - currency: 'RUB', - date: '2025-08-22T00:00:03.000Z', - description: 'Mock description', - expenseListId: '10cc093c-3875-4d33-a40a-df526266e262', - id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', - payment: null, - title: 'Mock item', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const submitButtonElem = screen.getByText('Удалить'); - - return { - onSubmit, - submitButtonElem, - }; -}; +import { DeleteExpenseItemFormHelper } from '@tests/helpers'; describe('Test DeleteExpenseItemForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('delete-expense-item-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + DeleteExpenseItemFormHelper.renderForm(); + const { formElem } = DeleteExpenseItemFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should call onSubmit with values', async () => { - const { - onSubmit, - submitButtonElem, - } = renderForm(); - - await userEvent.click(submitButtonElem); + const { onSubmit } = DeleteExpenseItemFormHelper.renderForm(); + await DeleteExpenseItemFormHelper.submitForm(); expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith(deletingItem); + expect(onSubmit).toHaveBeenCalledWith(DeleteExpenseItemFormHelper.expenseItem); }); }); diff --git a/frontend/src/entity/expenses/item/ui/EditExpenseItemForm/EditExpenseItemForm.test.tsx b/frontend/src/entity/expenses/item/ui/EditExpenseItemForm/EditExpenseItemForm.test.tsx index 7d41401..ab7320f 100644 --- a/frontend/src/entity/expenses/item/ui/EditExpenseItemForm/EditExpenseItemForm.test.tsx +++ b/frontend/src/entity/expenses/item/ui/EditExpenseItemForm/EditExpenseItemForm.test.tsx @@ -1,116 +1,48 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { EditExpenseItemFormProps } from './EditExpenseItemForm.ui'; - -import EditExpenseItemForm from './EditExpenseItemForm.ui'; - -const editingItem = { - amount: 12, - count: 12, - createdAt: '2025-08-29T07:57:40.503Z', - currency: 'RUB', - date: '2025-08-22T00:00:03.000Z', - description: 'Mock description', - expenseListId: '10cc093c-3875-4d33-a40a-df526266e262', - id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', - payment: null, - title: 'Mock item', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const titleInputElem = screen.getByLabelText('Название'); - const descriptionInputElem = screen.getByLabelText('Описание'); - const dateInputElem = screen.getByLabelText('Дата'); - const amountInputElem = screen.getByLabelText('Сумма'); - const countInputElem = screen.getByLabelText('Количество'); - const submitButtonElem = screen.getByText('Сохранить'); - - return { - amountInputElem, - countInputElem, - dateInputElem, - descriptionInputElem, - onSubmit, - submitButtonElem, - titleInputElem, - }; -}; +import { EditExpenseItemFormHelper } from '@tests/helpers'; describe('Test EditExpenseItemForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('edit-expense-item-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + EditExpenseItemFormHelper.renderForm(); + const { formElem } = EditExpenseItemFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should inputs have default value', () => { + EditExpenseItemFormHelper.renderForm(); const { amountInputElem, countInputElem, dateInputElem, descriptionInputElem, titleInputElem, - } = renderForm(); + } = EditExpenseItemFormHelper.getFormElements(); expect(titleInputElem).toHaveValue('Mock item'); expect(descriptionInputElem).toHaveValue('Mock description'); - expect(dateInputElem).toHaveValue('2025-08-22'); - expect(amountInputElem).toHaveValue(12); + expect(dateInputElem).toHaveValue('22.08.2025'); + expect(amountInputElem).toHaveValue('12'); expect(countInputElem).toHaveValue(12); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); + EditExpenseItemFormHelper.renderForm(options); + const { formElem, ...formElems } = EditExpenseItemFormHelper.getFormElements(); Object.values(formElems).forEach(assertion); }); test('should call onSubmit with values', async () => { - const { - amountInputElem, - countInputElem, - dateInputElem, - descriptionInputElem, - onSubmit, - submitButtonElem, - titleInputElem, - } = renderForm(); + const { onSubmit } = EditExpenseItemFormHelper.renderForm(); - await userEvent.clear(titleInputElem); - await userEvent.type(titleInputElem, 'Название элемента'); - - await userEvent.clear(descriptionInputElem); - await userEvent.type(descriptionInputElem, 'Описание элемента'); - - await userEvent.clear(dateInputElem); - await userEvent.type(dateInputElem, '2025-08-29'); - - await userEvent.clear(amountInputElem); - await userEvent.type(amountInputElem, '12'); - - await userEvent.clear(countInputElem); - await userEvent.type(countInputElem, '1'); - - await userEvent.click(submitButtonElem); + await EditExpenseItemFormHelper.fillForm({ + amount: 12, + count: 1, + date: '29.08.2025', + description: 'Описание элемента', + title: 'Название элемента', + }); expect(onSubmit).toHaveBeenCalledTimes(1); expect(onSubmit).toHaveBeenCalledWith({ diff --git a/frontend/src/entity/expenses/list/ui/AddExpensesListForm/ui/AddExpensesListForm.test.tsx b/frontend/src/entity/expenses/list/ui/AddExpensesListForm/ui/AddExpensesListForm.test.tsx index ed4fb91..e056b79 100644 --- a/frontend/src/entity/expenses/list/ui/AddExpensesListForm/ui/AddExpensesListForm.test.tsx +++ b/frontend/src/entity/expenses/list/ui/AddExpensesListForm/ui/AddExpensesListForm.test.tsx @@ -1,58 +1,28 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { AddExpenseListFormProps } from './AddExpensesListForm.ui'; - -import AddExpensesListForm from './AddExpensesListForm.ui'; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render(); - - const titleInputElem = screen.getByLabelText('Название'); - const submitButtonElem = screen.getByText('Добавить'); - - return { - onSubmit, - submitButtonElem, - titleInputElem, - }; -}; +import { AddExpensesListFormHelper } from '@tests/helpers'; describe('Test AddExpensesListForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('add-expense-list-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + AddExpensesListFormHelper.renderForm(); + const { formElem } = AddExpensesListFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); + AddExpensesListFormHelper.renderForm(options); + const { formElem, submitButtonElem, ...formElems } = AddExpensesListFormHelper.getFormElements(); Object.values(formElems).forEach(assertion); }); test('should call onSubmit with values', async () => { - const { - onSubmit, - submitButtonElem, - titleInputElem, - } = renderForm(); + const { onSubmit } = AddExpensesListFormHelper.renderForm(); - await userEvent.type(titleInputElem, 'Название списка'); - await userEvent.click(submitButtonElem); + await AddExpensesListFormHelper.fillForm({ title: 'Название списка' }); expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith({ - title: 'Название списка', - }); + expect(onSubmit).toHaveBeenCalledWith({ title: 'Название списка' }); }); }); diff --git a/frontend/src/entity/expenses/list/ui/DeleteExpensesListForm/ui/DeleteExpensesListForm.test.tsx b/frontend/src/entity/expenses/list/ui/DeleteExpensesListForm/ui/DeleteExpensesListForm.test.tsx index 86a5c91..fa3c026 100644 --- a/frontend/src/entity/expenses/list/ui/DeleteExpensesListForm/ui/DeleteExpensesListForm.test.tsx +++ b/frontend/src/entity/expenses/list/ui/DeleteExpensesListForm/ui/DeleteExpensesListForm.test.tsx @@ -1,55 +1,18 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { DeleteExpenseListFormProps } from './DeleteExpensesListForm.ui'; - -import DeleteExpensesListForm from './DeleteExpensesListForm.ui'; - -const deletingList = { - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - items: [], - title: 'Mock list', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const submitButtonElem = screen.getByText('Удалить'); - - return { - onSubmit, - submitButtonElem, - }; -}; +import { DeleteExpensesListFormHelper } from '@tests/helpers'; describe('Test DeleteExpensesListForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('delete-expense-list-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + DeleteExpensesListFormHelper.renderForm(); + const { formElem } = DeleteExpensesListFormHelper.getFormElems(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should call onSubmit with values', async () => { - const { - onSubmit, - submitButtonElem, - } = renderForm(); - - await userEvent.click(submitButtonElem); + const { onSubmit } = DeleteExpensesListFormHelper.renderForm(); + await DeleteExpensesListFormHelper.submitForm(); expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith(deletingList); + expect(onSubmit).toHaveBeenCalledWith(DeleteExpensesListFormHelper.expensesList); }); }); diff --git a/frontend/src/entity/expenses/list/ui/EditExpensesListForm/ui/EditExpensesListForm.test.tsx b/frontend/src/entity/expenses/list/ui/EditExpensesListForm/ui/EditExpensesListForm.test.tsx index 4a212a9..e72331f 100644 --- a/frontend/src/entity/expenses/list/ui/EditExpensesListForm/ui/EditExpensesListForm.test.tsx +++ b/frontend/src/entity/expenses/list/ui/EditExpensesListForm/ui/EditExpensesListForm.test.tsx @@ -1,78 +1,32 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { EditExpensesListFormProps } from './EditExpensesListForm.ui'; - -import EditExpensesListForm from './EditExpensesListForm.ui'; - -const editingList = { - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - items: [], - title: 'Mock list', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const titleInputElem = screen.getByLabelText('Название'); - const submitButtonElem = screen.getByText('Сохранить'); - - return { - onSubmit, - submitButtonElem, - titleInputElem, - }; -}; +import { EditExpensesListFormHelper } from '@tests/helpers'; describe('Test EditExpensesListForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('edit-expense-list-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + EditExpensesListFormHelper.renderForm(); + const { formElem } = EditExpensesListFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should inputs have default value', () => { - const { - titleInputElem, - } = renderForm(); + EditExpensesListFormHelper.renderForm(); + const { titleInputElem } = EditExpensesListFormHelper.getFormElements(); expect(titleInputElem).toHaveValue('Mock list'); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); + EditExpensesListFormHelper.renderForm(options); + const { formElem, ...formElems } = EditExpensesListFormHelper.getFormElements(); Object.values(formElems).forEach(assertion); }); test('should call onSubmit with values', async () => { - const { - onSubmit, - submitButtonElem, - titleInputElem, - } = renderForm(); + const { onSubmit } = EditExpensesListFormHelper.renderForm(); - await userEvent.clear(titleInputElem); - await userEvent.type(titleInputElem, 'Название списка'); - - await userEvent.click(submitButtonElem); + await EditExpensesListFormHelper.fillForm({ title: 'Название списка' }); expect(onSubmit).toHaveBeenCalledTimes(1); expect(onSubmit).toHaveBeenCalledWith({ diff --git a/frontend/src/entity/incomes/item/ui/AddIncomeItemForm/AddIncomeItemForm.test.tsx b/frontend/src/entity/incomes/item/ui/AddIncomeItemForm/AddIncomeItemForm.test.tsx index aa2d766..a0a3e69 100644 --- a/frontend/src/entity/incomes/item/ui/AddIncomeItemForm/AddIncomeItemForm.test.tsx +++ b/frontend/src/entity/incomes/item/ui/AddIncomeItemForm/AddIncomeItemForm.test.tsx @@ -1,67 +1,31 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { AddIncomeItemFormProps } from './AddIncomeItemForm.ui'; - -import AddIncomeItemForm from './AddIncomeItemForm.ui'; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render(); - - const titleInputElem = screen.getByLabelText('Название'); - const descriptionInputElem = screen.getByLabelText('Описание'); - const dateInputElem = screen.getByLabelText('Дата'); - const amountInputElem = screen.getByLabelText('Сумма'); - const submitButtonElem = screen.getByText('Добавить'); - - return { - amountInputElem, - dateInputElem, - descriptionInputElem, - onSubmit, - submitButtonElem, - titleInputElem, - }; -}; +import { AddIncomeItemFormHelper } from '@tests/helpers'; describe('Test AddIncomeItemForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('add-income-item-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + AddIncomeItemFormHelper.renderForm(); + const { formElem } = AddIncomeItemFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); + AddIncomeItemFormHelper.renderForm(options); + const { formElem, ...formElems } = AddIncomeItemFormHelper.getFormElements(); Object.values(formElems).forEach(assertion); }); test('should call onSubmit with values', async () => { - const { - amountInputElem, - dateInputElem, - descriptionInputElem, - onSubmit, - submitButtonElem, - titleInputElem, - } = renderForm(); + const { onSubmit } = AddIncomeItemFormHelper.renderForm(); - await userEvent.type(titleInputElem, 'Название элемента'); - await userEvent.type(descriptionInputElem, 'Описание элемента'); - await userEvent.type(dateInputElem, '2025-08-29'); - await userEvent.type(amountInputElem, '12'); - - await userEvent.click(submitButtonElem); + await AddIncomeItemFormHelper.fillForm({ + amount: 12, + date: '29.08.2025', + description: 'Описание элемента', + title: 'Название элемента', + }); expect(onSubmit).toHaveBeenCalledTimes(1); expect(onSubmit).toHaveBeenCalledWith({ diff --git a/frontend/src/entity/incomes/item/ui/DeleteIncomeItemForm/DeleteIncomeItemForm.test.tsx b/frontend/src/entity/incomes/item/ui/DeleteIncomeItemForm/DeleteIncomeItemForm.test.tsx index afda87e..9fb49e4 100644 --- a/frontend/src/entity/incomes/item/ui/DeleteIncomeItemForm/DeleteIncomeItemForm.test.tsx +++ b/frontend/src/entity/incomes/item/ui/DeleteIncomeItemForm/DeleteIncomeItemForm.test.tsx @@ -1,59 +1,18 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { DeleteIncomeItemFormProps } from './DeleteIncomeItemForm.ui'; - -import DeleteIncomeItemForm from './DeleteIncomeItemForm.ui'; - -const deletingItem = { - amount: 12, - createdAt: '2025-08-29T07:57:40.503Z', - currency: 'RUB', - date: '2025-08-22T00:00:03.000Z', - description: 'Mock description', - id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', - incomeListId: '10cc093c-3875-4d33-a40a-df526266e262', - title: 'Mock item', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const submitButtonElem = screen.getByText('Удалить'); - - return { - onSubmit, - submitButtonElem, - }; -}; +import { DeleteIncomeItemFormHelper } from '@tests/helpers'; describe('Test DeleteIncomeItemForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('delete-income-item-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + DeleteIncomeItemFormHelper.renderForm(); + const { formElem } = DeleteIncomeItemFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should call onSubmit with values', async () => { - const { - onSubmit, - submitButtonElem, - } = renderForm(); - - await userEvent.click(submitButtonElem); + const { onSubmit } = DeleteIncomeItemFormHelper.renderForm(); + await DeleteIncomeItemFormHelper.submitForm(); expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith(deletingItem); + expect(onSubmit).toHaveBeenCalledWith(DeleteIncomeItemFormHelper.incomeItem); }); }); diff --git a/frontend/src/entity/incomes/item/ui/EditIncomeItemForm/EditIncomeItemForm.test.tsx b/frontend/src/entity/incomes/item/ui/EditIncomeItemForm/EditIncomeItemForm.test.tsx index 88f01df..8b1af35 100644 --- a/frontend/src/entity/incomes/item/ui/EditIncomeItemForm/EditIncomeItemForm.test.tsx +++ b/frontend/src/entity/incomes/item/ui/EditIncomeItemForm/EditIncomeItemForm.test.tsx @@ -1,107 +1,45 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { EditIncomeItemFormProps } from './EditIncomeItemForm.ui'; - -import EditIncomeItemForm from './EditIncomeItemForm.ui'; - -const editingItem = { - amount: 12, - count: 12, - createdAt: '2025-08-29T07:57:40.503Z', - currency: 'RUB', - date: '2025-08-22T00:00:03.000Z', - description: 'Mock description', - id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', - incomeListId: '10cc093c-3875-4d33-a40a-df526266e262', - title: 'Mock item', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const titleInputElem = screen.getByLabelText('Название'); - const descriptionInputElem = screen.getByLabelText('Описание'); - const dateInputElem = screen.getByLabelText('Дата'); - const amountInputElem = screen.getByLabelText('Сумма'); - const submitButtonElem = screen.getByText('Сохранить'); - - return { - amountInputElem, - dateInputElem, - descriptionInputElem, - onSubmit, - submitButtonElem, - titleInputElem, - }; -}; +import { EditIncomeItemFormHelper } from '@tests/helpers'; describe('Test EditIncomeItemForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('edit-income-item-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + EditIncomeItemFormHelper.renderForm(); + const { formElem } = EditIncomeItemFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should inputs have default value', () => { + EditIncomeItemFormHelper.renderForm(); const { amountInputElem, dateInputElem, descriptionInputElem, titleInputElem, - } = renderForm(); + } = EditIncomeItemFormHelper.getFormElements(); expect(titleInputElem).toHaveValue('Mock item'); expect(descriptionInputElem).toHaveValue('Mock description'); - expect(dateInputElem).toHaveValue('2025-08-22'); + expect(dateInputElem).toHaveValue('22.08.2025'); expect(amountInputElem).toHaveValue(12); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); + EditIncomeItemFormHelper.renderForm(options); + const { formElem, ...formElems } = EditIncomeItemFormHelper.getFormElements(); Object.values(formElems).forEach(assertion); }); test('should call onSubmit with values', async () => { - const { - amountInputElem, - dateInputElem, - descriptionInputElem, - onSubmit, - submitButtonElem, - titleInputElem, - } = renderForm(); + const { onSubmit } = EditIncomeItemFormHelper.renderForm(); - await userEvent.clear(titleInputElem); - await userEvent.type(titleInputElem, 'Название элемента'); - - await userEvent.clear(descriptionInputElem); - await userEvent.type(descriptionInputElem, 'Описание элемента'); - - await userEvent.clear(dateInputElem); - await userEvent.type(dateInputElem, '2025-08-29'); - - await userEvent.clear(amountInputElem); - await userEvent.type(amountInputElem, '12'); - - await userEvent.click(submitButtonElem); + await EditIncomeItemFormHelper.fillForm({ + amount: 12, + date: '29.08.2025', + description: 'Описание элемента', + title: 'Название элемента', + }); expect(onSubmit).toHaveBeenCalledTimes(1); expect(onSubmit).toHaveBeenCalledWith({ diff --git a/frontend/src/entity/incomes/list/ui/AddIncomeListForm/ui/AddIncomeListForm.test.tsx b/frontend/src/entity/incomes/list/ui/AddIncomeListForm/ui/AddIncomeListForm.test.tsx index 2a28d2d..da9d46d 100644 --- a/frontend/src/entity/incomes/list/ui/AddIncomeListForm/ui/AddIncomeListForm.test.tsx +++ b/frontend/src/entity/incomes/list/ui/AddIncomeListForm/ui/AddIncomeListForm.test.tsx @@ -1,58 +1,27 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { AddIncomeListFormProps } from './AddIncomeListForm.ui'; - -import AddIncomeListForm from './AddIncomeListForm.ui'; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render(); - - const titleInputElem = screen.getByLabelText('Название'); - const submitButtonElem = screen.getByText('Добавить'); - - return { - onSubmit, - submitButtonElem, - titleInputElem, - }; -}; +import { AddIncomeListFormHelper } from '@tests/helpers'; describe('Test AddIncomeListForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('add-income-list-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + AddIncomeListFormHelper.renderForm(); + const { formElem } = AddIncomeListFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); + AddIncomeListFormHelper.renderForm(options); + const { formElem, ...formElems } = AddIncomeListFormHelper.getFormElements(); Object.values(formElems).forEach(assertion); }); test('should call onSubmit with values', async () => { - const { - onSubmit, - submitButtonElem, - titleInputElem, - } = renderForm(); - - await userEvent.type(titleInputElem, 'Название списка'); - await userEvent.click(submitButtonElem); + const { onSubmit } = AddIncomeListFormHelper.renderForm(); + await AddIncomeListFormHelper.fillForm({ title: 'Название списка' }); expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith({ - title: 'Название списка', - }); + expect(onSubmit).toHaveBeenCalledWith({ title: 'Название списка' }); }); }); diff --git a/frontend/src/entity/incomes/list/ui/DeleteIncomeListForm/ui/DeleteIncomeListForm.test.tsx b/frontend/src/entity/incomes/list/ui/DeleteIncomeListForm/ui/DeleteIncomeListForm.test.tsx index d365461..201d9e1 100644 --- a/frontend/src/entity/incomes/list/ui/DeleteIncomeListForm/ui/DeleteIncomeListForm.test.tsx +++ b/frontend/src/entity/incomes/list/ui/DeleteIncomeListForm/ui/DeleteIncomeListForm.test.tsx @@ -1,55 +1,18 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { DeleteIncomeListFormProps } from './DeleteIncomeListForm.ui'; - -import DeleteIncomeListForm from './DeleteIncomeListForm.ui'; - -const deletingList = { - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - items: [], - title: 'Mock list', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const submitButtonElem = screen.getByText('Удалить'); - - return { - onSubmit, - submitButtonElem, - }; -}; +import { DeleteIncomeListFormHelper } from '@tests/helpers'; describe('Test DeleteIncomeListForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('delete-income-list-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + DeleteIncomeListFormHelper.renderForm(); + const { formElem } = DeleteIncomeListFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should call onSubmit with values', async () => { - const { - onSubmit, - submitButtonElem, - } = renderForm(); - - await userEvent.click(submitButtonElem); + const { onSubmit } = DeleteIncomeListFormHelper.renderForm(); + await DeleteIncomeListFormHelper.submitForm(); expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith(deletingList); + expect(onSubmit).toHaveBeenCalledWith(DeleteIncomeListFormHelper.incomeList); }); }); diff --git a/frontend/src/entity/incomes/list/ui/EditIncomeListForm/ui/EditIncomeListForm.test.tsx b/frontend/src/entity/incomes/list/ui/EditIncomeListForm/ui/EditIncomeListForm.test.tsx index f0644c1..0f0b96f 100644 --- a/frontend/src/entity/incomes/list/ui/EditIncomeListForm/ui/EditIncomeListForm.test.tsx +++ b/frontend/src/entity/incomes/list/ui/EditIncomeListForm/ui/EditIncomeListForm.test.tsx @@ -1,82 +1,34 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import type { EditExpensesListFormProps } from './EditIncomeListForm.ui'; - -import EditIncomeListForm from './EditIncomeListForm.ui'; - -const editingList = { - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - items: [], - title: 'Mock list', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderForm = (props?: Partial) => { - const onSubmit = jest.fn(); - - render( - , - ); - - const titleInputElem = screen.getByLabelText('Название'); - const submitButtonElem = screen.getByText('Сохранить'); - - return { - onSubmit, - submitButtonElem, - titleInputElem, - }; -}; +import { EditIncomeListFormHelper } from '@tests/helpers'; describe('Test EditIncomeListForm', () => { test('should render form', () => { - renderForm(); - const form = screen.getByTestId('edit-income-list-form'); - expect(form).toBeInTheDocument(); - expect(form).toBeVisible(); + EditIncomeListFormHelper.renderForm(); + const { formElem } = EditIncomeListFormHelper.getFormElements(); + expect(formElem).toBeInTheDocument(); + expect(formElem).toBeVisible(); }); test('should inputs have default value', () => { - const { - titleInputElem, - } = renderForm(); + EditIncomeListFormHelper.renderForm(); + const { titleInputElem } = EditIncomeListFormHelper.getFormElements(); expect(titleInputElem).toHaveValue('Mock list'); }); test.each([ - ['enabled', {}, (el: HTMLElement) => { - expect(el).toBeEnabled(); - }], - ['disabled', { disabled: true }, (el: HTMLElement) => { - expect(el).toBeDisabled(); - }], + ['enabled', {}, (el: HTMLElement) => { expect(el).toBeEnabled(); }], + ['disabled', { disabled: true }, (el: HTMLElement) => { expect(el).toBeDisabled(); }], ])('should render fields %s', (_state, options, assertion) => { - const { onSubmit, ...formElems } = renderForm(options); + EditIncomeListFormHelper.renderForm(options); + const { formElem, ...formElems } = EditIncomeListFormHelper.getFormElements(); Object.values(formElems).forEach(assertion); }); test('should call onSubmit with values', async () => { - const { - onSubmit, - submitButtonElem, - titleInputElem, - } = renderForm(); + const { onSubmit } = EditIncomeListFormHelper.renderForm(); - await userEvent.clear(titleInputElem); - await userEvent.type(titleInputElem, 'Название списка'); - - await userEvent.click(submitButtonElem); + await EditIncomeListFormHelper.fillForm({ title: 'Название списка' }); expect(onSubmit).toHaveBeenCalledTimes(1); - expect(onSubmit).toHaveBeenCalledWith({ - title: 'Название списка', - }); + expect(onSubmit).toHaveBeenCalledWith({ title: 'Название списка' }); }); }); diff --git a/frontend/src/features/bank-cards/AddBankCardButton/ui/AddBankCardButton.test.tsx b/frontend/src/features/bank-cards/AddBankCardButton/ui/AddBankCardButton.test.tsx index bac6cd6..13505a1 100644 --- a/frontend/src/features/bank-cards/AddBankCardButton/ui/AddBankCardButton.test.tsx +++ b/frontend/src/features/bank-cards/AddBankCardButton/ui/AddBankCardButton.test.tsx @@ -1,72 +1,22 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { AddBankCardBody, AddBankCardResponse } from '@/entity/cards'; - -import AddBankCardButton from './AddBankCardButton.ui'; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('test-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const fillForm = async (data: AddBankCardBody) => { - await userEvent.type(screen.getByLabelText('Название'), data.name); - await userEvent.type(screen.getByLabelText('Баланс'), String(data.balance)); -}; - -const submitHandler = (requestSpy: jest.Mock) => http.post( - '/bank-card', - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json({ - balance: 999, - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - name: 'Название карты', - updatedAt: '2025-08-29T07:57:40.503Z', - }); - }, -); +import { screen, waitFor } from '@testing-library/react'; +import { AddBankCardButtonHelper, AddBankCardFormHelper } from '@tests/helpers'; describe('Test AddBankCardButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = AddBankCardButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Добавить'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await AddBankCardButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -78,18 +28,18 @@ describe('Test AddBankCardButton', () => { const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = within(modalElem).getByText('Добавить'); + server.use(AddBankCardButtonHelper.submitHandler(requestSpy)); + modalElem = (await AddBankCardButtonHelper.openModal()).modalElem; + submitButtonElem = AddBankCardFormHelper.getFormElements().submitButtonElem; - await fillForm({ + await AddBankCardFormHelper.fillForm({ balance: 999, name: 'Тестовая карта', - }); + }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await AddBankCardFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); expect(requestSpy).toHaveBeenCalledWith({ balance: 999, @@ -99,8 +49,10 @@ describe('Test AddBankCardButton', () => { test('should close modal after submit', async () => { expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await AddBankCardFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/src/features/bank-cards/DeleteBankCardButton/ui/DeleteBankCardButton.test.tsx b/frontend/src/features/bank-cards/DeleteBankCardButton/ui/DeleteBankCardButton.test.tsx index ef02a95..a4635a2 100644 --- a/frontend/src/features/bank-cards/DeleteBankCardButton/ui/DeleteBankCardButton.test.tsx +++ b/frontend/src/features/bank-cards/DeleteBankCardButton/ui/DeleteBankCardButton.test.tsx @@ -1,75 +1,35 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { BankCard } from '@/entity/cards'; -import type { DeleteExpenseListPath } from '@/entity/expenses/list'; - -import DeleteBankCardButton from './DeleteBankCardButton.ui'; - -const deletingBankCard: BankCard = { - balance: 1000, - createdAt: '2025-08-29T07:57:40.503Z', - id: 'af6da4e1-f24f-4b43-8556-fa885626897d', - name: 'Тестовая карта', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderButton = () => { - const testId = 'delete-bank-card-button'; - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId(testId); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = http.delete(`/bank-card/${deletingBankCard.id}`, () => HttpResponse.json(deletingBankCard)); +import { screen, waitFor } from '@testing-library/react'; +import { DeleteBankCardButtonHelper, DeleteBankCardFormHelper } from '@tests/helpers'; describe('Test DeleteBankCardButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = DeleteBankCardButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Удалить'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await DeleteBankCardButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); }); test('should close modal after submit', async () => { - server.use(submitHandler); - const { modalElem } = await openModal(); - const submitButtonElem = within(modalElem).getByText('Удалить'); - + server.use(DeleteBankCardButtonHelper.submitHandler); + const { modalElem } = await DeleteBankCardButtonHelper.openModal(); + const { submitButtonElem } = DeleteBankCardFormHelper.getFormElements(); expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await DeleteBankCardFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); diff --git a/frontend/src/features/bank-cards/EditBankCardButton/ui/EditBankCardButton.test.tsx b/frontend/src/features/bank-cards/EditBankCardButton/ui/EditBankCardButton.test.tsx index f2b2790..915dbc9 100644 --- a/frontend/src/features/bank-cards/EditBankCardButton/ui/EditBankCardButton.test.tsx +++ b/frontend/src/features/bank-cards/EditBankCardButton/ui/EditBankCardButton.test.tsx @@ -1,92 +1,22 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { - BankCard, - EditBankCardBody, - EditBankCardPath, - EditBankCardResponse, -} from '@/entity/cards'; - -import EditBankCardButton from './EditBankCardButton.ui'; - -const editingBankCard: BankCard = { - balance: 1000, - createdAt: '2025-08-29T07:57:40.503Z', - id: 'af6da4e1-f24f-4b43-8556-fa885626897d', - name: 'Тестовая карта', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const fillForm = async (data: EditBankCardBody) => { - const nameInputElem = screen.getByLabelText('Название'); - const balanceInputElem = screen.getByLabelText('Баланс'); - - await userEvent.clear(nameInputElem); - await userEvent.type(nameInputElem, data.name); - - await userEvent.clear(balanceInputElem); - await userEvent.type(balanceInputElem, String(data.balance)); -}; - -const renderButton = () => { - const testId = 'edit-bank-card-button'; - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId(testId); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = (requestSpy: jest.Mock) => http.patch( - `/bank-card/${editingBankCard.id}`, - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json({ - balance: 1000, - createdAt: '2025-08-29T07:57:40.503Z', - id: 'af6da4e1-f24f-4b43-8556-fa885626897d', - name: 'Тестовая карта', - updatedAt: '2025-08-29T07:57:40.503Z', - }); - }, -); +import { screen, waitFor } from '@testing-library/react'; +import { EditBankCardButtonHelper, EditBankCardFormHelper } from '@tests/helpers'; describe('Test EditBankCardButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = EditBankCardButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Редактировать'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await EditBankCardButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -98,18 +28,18 @@ describe('Test EditBankCardButton', () => { const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = within(modalElem).getByText('Сохранить'); + server.use(EditBankCardButtonHelper.submitHandler(requestSpy)); + modalElem = (await EditBankCardButtonHelper.openModal()).modalElem; + submitButtonElem = EditBankCardFormHelper.getFormElements().submitButtonElem; - await fillForm({ + await EditBankCardFormHelper.fillForm({ balance: 999, name: 'Тестовая карта', - }); + }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await EditBankCardFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); expect(requestSpy).toHaveBeenCalledWith({ balance: 999, @@ -119,8 +49,10 @@ describe('Test EditBankCardButton', () => { test('should close modal after submit', async () => { expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await EditBankCardFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/src/features/cash/AddCashButton/ui/AddCashButton.test.tsx b/frontend/src/features/cash/AddCashButton/ui/AddCashButton.test.tsx index 8cf7c31..76542c7 100644 --- a/frontend/src/features/cash/AddCashButton/ui/AddCashButton.test.tsx +++ b/frontend/src/features/cash/AddCashButton/ui/AddCashButton.test.tsx @@ -1,72 +1,22 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { AdCashResponse, AddCashBody } from '@/entity/cash'; - -import AddCashButton from './AddCashButton.ui'; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('test-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const fillForm = async (data: AddCashBody) => { - await userEvent.type(screen.getByLabelText('Название'), data.name); - await userEvent.type(screen.getByLabelText('Баланс'), String(data.balance)); -}; - -const submitHandler = (requestSpy: jest.Mock) => http.post( - '/cash', - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json({ - balance: 999, - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - name: 'Наличные', - updatedAt: '2025-08-29T07:57:40.503Z', - }); - }, -); +import { screen, waitFor } from '@testing-library/react'; +import { AddCashButtonHelper, AddCashFormHelper } from '@tests/helpers'; describe('Test AddCashButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = AddCashButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Добавить'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await AddCashButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -78,18 +28,18 @@ describe('Test AddCashButton', () => { const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = within(modalElem).getByText('Добавить'); + server.use(AddCashButtonHelper.submitHandler(requestSpy)); + modalElem = (await AddCashButtonHelper.openModal()).modalElem; + submitButtonElem = AddCashFormHelper.getFormElements().submitButtonElem; - await fillForm({ + await AddCashFormHelper.fillForm({ balance: 999, name: 'Наличные', - }); + }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await AddCashFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); expect(requestSpy).toHaveBeenCalledWith({ balance: 999, @@ -99,8 +49,10 @@ describe('Test AddCashButton', () => { test('should close modal after submit', async () => { expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await AddCashFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/src/features/cash/DeleteCashButton/ui/DeleteCashButton.test.tsx b/frontend/src/features/cash/DeleteCashButton/ui/DeleteCashButton.test.tsx index 1e8d176..e42ea68 100644 --- a/frontend/src/features/cash/DeleteCashButton/ui/DeleteCashButton.test.tsx +++ b/frontend/src/features/cash/DeleteCashButton/ui/DeleteCashButton.test.tsx @@ -1,75 +1,37 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { CashItem } from '@/entity/cash'; -import type { DeleteExpenseListPath } from '@/entity/expenses/list'; - -import DeleteCashButton from './DeleteCashButton.ui'; - -const deletingCash: CashItem = { - balance: 1000, - createdAt: '2025-08-29T07:57:40.503Z', - id: 'af6da4e1-f24f-4b43-8556-fa885626897d', - name: 'Наличные', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderButton = () => { - const testId = 'delete-cash-button'; - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId(testId); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = http.delete(`/cash/${deletingCash.id}`, () => HttpResponse.json(deletingCash)); +import { DeleteCashButtonHelper, DeleteCashFormHelper } from '@tests/helpers'; describe('Test DeleteCashButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = DeleteCashButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Удалить'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await DeleteCashButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); }); test('should close modal after submit', async () => { - server.use(submitHandler); - const { modalElem } = await openModal(); - const submitButtonElem = within(modalElem).getByText('Удалить'); + server.use(DeleteCashButtonHelper.submitHandler); + const { modalElem } = await DeleteCashButtonHelper.openModal(); + const { submitButtonElem } = DeleteCashFormHelper.getFormElements(); expect(submitButtonElem).toBeEnabled(); await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); diff --git a/frontend/src/features/cash/EditCashButton/ui/EditCashButton.test.tsx b/frontend/src/features/cash/EditCashButton/ui/EditCashButton.test.tsx index 902a971..1221fb7 100644 --- a/frontend/src/features/cash/EditCashButton/ui/EditCashButton.test.tsx +++ b/frontend/src/features/cash/EditCashButton/ui/EditCashButton.test.tsx @@ -1,92 +1,22 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { - CashItem, - EditCashBody, - EditCashPath, - EditCashResponse, -} from '@/entity/cash'; - -import EditCashButton from './EditCashButton.ui'; - -const editingCash: CashItem = { - balance: 1000, - createdAt: '2025-08-29T07:57:40.503Z', - id: 'af6da4e1-f24f-4b43-8556-fa885626897d', - name: 'Наличные', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const fillForm = async (data: EditCashBody) => { - const nameInputElem = screen.getByLabelText('Название'); - const balanceInputElem = screen.getByLabelText('Баланс'); - - await userEvent.clear(nameInputElem); - await userEvent.type(nameInputElem, data.name); - - await userEvent.clear(balanceInputElem); - await userEvent.type(balanceInputElem, String(data.balance)); -}; - -const renderButton = () => { - const testId = 'edit-cash-button'; - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId(testId); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = (requestSpy: jest.Mock) => http.patch( - `/cash/${editingCash.id}`, - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json({ - balance: 1000, - createdAt: '2025-08-29T07:57:40.503Z', - id: 'af6da4e1-f24f-4b43-8556-fa885626897d', - name: 'Наличные', - updatedAt: '2025-08-29T07:57:40.503Z', - }); - }, -); +import { screen, waitFor } from '@testing-library/react'; +import { EditCashButtonHelper, EditCashFormHelper } from '@tests/helpers'; describe('Test EditCashButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = EditCashButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Редактировать'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await EditCashButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -98,18 +28,18 @@ describe('Test EditCashButton', () => { const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = within(modalElem).getByText('Сохранить'); + server.use(EditCashButtonHelper.submitHandler(requestSpy)); + modalElem = (await EditCashButtonHelper.openModal()).modalElem; + submitButtonElem = EditCashFormHelper.getFormElements().submitButtonElem; - await fillForm({ + await EditCashFormHelper.fillForm({ balance: 999, name: 'Наличные', - }); + }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await EditCashFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); expect(requestSpy).toHaveBeenCalledWith({ balance: 999, @@ -119,8 +49,10 @@ describe('Test EditCashButton', () => { test('should close modal after submit', async () => { expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await EditCashFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/src/features/expenses/items/AddExpenseItemButton/ui/AddExpenseItemButton.test.tsx b/frontend/src/features/expenses/items/AddExpenseItemButton/ui/AddExpenseItemButton.test.tsx index a155be6..b263123 100644 --- a/frontend/src/features/expenses/items/AddExpenseItemButton/ui/AddExpenseItemButton.test.tsx +++ b/frontend/src/features/expenses/items/AddExpenseItemButton/ui/AddExpenseItemButton.test.tsx @@ -1,85 +1,22 @@ -import { getBankCardList, getCashList } from '@mocks/browser/handlers'; import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { - AddExpenseItemBody, - AddExpenseItemPath, - AddExpenseItemSuccessResponse, -} from '@/entity/expenses/item'; - -import { fillAddExpenseItemForm } from '@/entity/expenses/item/ui/AddExpenseItemForm/AddExpenseItemForm.test'; - -import AddExpenseItemButton from './AddExpenseItemButton.ui'; - -const renderButton = () => { - server.use(getBankCardList, getCashList); - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('test-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = (requestSpy: jest.Mock) => http.post< - AddExpenseItemPath, - AddExpenseItemBody, - AddExpenseItemSuccessResponse ->( - '/expenses/list/123/items', - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json({ - amount: 12, - count: 12, - createdAt: '2025-08-29T07:57:40.503Z', - currency: 'RUB', - date: '2025-08-22T00:00:03.000Z', - description: 'Mock description', - expenseListId: '10cc093c-3875-4d33-a40a-df526266e262', - id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', - payment: null, - title: 'Mock item', - updatedAt: '2025-08-29T07:57:40.503Z', - }); - }, -); +import { screen, waitFor } from '@testing-library/react'; +import { AddExpenseItemButtonHelper, AddExpenseItemFormHelper } from '@tests/helpers'; describe('Test AddExpenseItemButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = AddExpenseItemButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Добавить элемент'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await AddExpenseItemButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -87,25 +24,23 @@ describe('Test AddExpenseItemButton', () => { describe('should close modal after submit and send request', () => { let modalElem: HTMLElement; - let submitButtonElem: HTMLElement; const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = screen.getByText('Добавить'); + server.use(AddExpenseItemButtonHelper.submitHandler(requestSpy)); + modalElem = (await AddExpenseItemButtonHelper.openModal()).modalElem; - await fillAddExpenseItemForm({ + await AddExpenseItemFormHelper.fillForm({ amount: '12', count: '1', - date: '2025-08-29', + date: '29.08.2025', description: 'Описание элемента', title: 'Название элемента', - }); + }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await AddExpenseItemFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); expect(requestSpy).toHaveBeenCalledWith({ amount: 12, @@ -119,9 +54,10 @@ describe('Test AddExpenseItemButton', () => { }); test('should close modal after submit', async () => { - expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await AddExpenseItemFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/src/features/expenses/items/DeleteExpenseItemButton/ui/DeleteExpenseItemButton.test.tsx b/frontend/src/features/expenses/items/DeleteExpenseItemButton/ui/DeleteExpenseItemButton.test.tsx index 8e35602..a3b5fb5 100644 --- a/frontend/src/features/expenses/items/DeleteExpenseItemButton/ui/DeleteExpenseItemButton.test.tsx +++ b/frontend/src/features/expenses/items/DeleteExpenseItemButton/ui/DeleteExpenseItemButton.test.tsx @@ -1,79 +1,37 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { DeleteExpenseItemPath } from '@/entity/expenses/item'; - -import DeleteExpenseItemButton from './DeleteExpenseItemButton.ui'; - -const deletedItem = { - amount: 12, - count: 12, - createdAt: '2025-08-29T07:57:40.503Z', - currency: 'RUB', - date: '2025-08-22T00:00:03.000Z', - description: 'Mock description', - expenseListId: '10cc093c-3875-4d33-a40a-df526266e262', - id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', - payment: null, - title: 'Mock item', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('delete-expense-item-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = http.delete(`/expenses/list/${deletedItem.expenseListId}/items/${deletedItem.id}`, () => HttpResponse.json(deletedItem)); +import { DeleteExpenseItemButtonHelper, DeleteExpenseItemFormHelper } from '@tests/helpers'; describe('Test DeleteExpenseItemButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = DeleteExpenseItemButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Удалить'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await DeleteExpenseItemButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); }); test('should close modal after submit', async () => { - server.use(submitHandler); - const { modalElem } = await openModal(); - const submitButtonElem = within(modalElem).getByText('Удалить'); + server.use(DeleteExpenseItemButtonHelper.submitHandler); + const { modalElem } = await DeleteExpenseItemButtonHelper.openModal(); + const { submitButtonElem } = DeleteExpenseItemFormHelper.getFormElements(); expect(submitButtonElem).toBeEnabled(); await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); diff --git a/frontend/src/features/expenses/items/EditExpenseItemButton/ui/EditExpenseItemButton.test.tsx b/frontend/src/features/expenses/items/EditExpenseItemButton/ui/EditExpenseItemButton.test.tsx index 6eae0ef..b66e430 100644 --- a/frontend/src/features/expenses/items/EditExpenseItemButton/ui/EditExpenseItemButton.test.tsx +++ b/frontend/src/features/expenses/items/EditExpenseItemButton/ui/EditExpenseItemButton.test.tsx @@ -1,105 +1,22 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { - EditExpenseItemBody, - EditExpenseItemPath, - EditExpenseItemSuccessResponse, -} from '@/entity/expenses/item'; - -import EditExpenseItemButton from './EditExpenseItemButton.ui'; - -const editingItem = { - amount: 12, - count: 12, - createdAt: '2025-08-29T07:57:40.503Z', - currency: 'RUB', - date: '2025-08-22T00:00:03.000Z', - description: 'Mock description', - expenseListId: '10cc093c-3875-4d33-a40a-df526266e262', - id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', - payment: null, - title: 'Mock item', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('edit-expense-item-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = (requestSpy: jest.Mock) => http.patch< - EditExpenseItemPath, - EditExpenseItemBody, - EditExpenseItemSuccessResponse ->( - `/expenses/list/${editingItem.expenseListId}/items/${editingItem.id}`, - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json(editingItem); - }, -); - -const fillForm = async (data: { amount: string; count: string; date: string; description: string; title: string }) => { - const titleInputElem = screen.getByLabelText('Название'); - await userEvent.clear(titleInputElem); - await userEvent.type(titleInputElem, data.title); - - const descriptionInputElem = screen.getByLabelText('Описание'); - await userEvent.clear(descriptionInputElem); - await userEvent.type(descriptionInputElem, data.description); - - const dateInputElem = screen.getByLabelText('Дата'); - await userEvent.clear(dateInputElem); - await userEvent.type(dateInputElem, data.date); - - const amountInputElem = screen.getByLabelText('Сумма'); - await userEvent.clear(amountInputElem); - await userEvent.type(amountInputElem, data.amount); - - const countInputElem = screen.getByLabelText('Количество'); - await userEvent.clear(countInputElem); - await userEvent.type(countInputElem, data.count); -}; +import { screen, waitFor } from '@testing-library/react'; +import { EditExpenseItemButtonHelper, EditExpenseItemFormHelper } from '@tests/helpers'; describe('TestEditExpenseItemButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = EditExpenseItemButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Редактировать'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await EditExpenseItemButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -107,25 +24,23 @@ describe('TestEditExpenseItemButton', () => { describe('should close modal after submit and send request', () => { let modalElem: HTMLElement; - let submitButtonElem: HTMLElement; const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = screen.getByText('Сохранить'); + server.use(EditExpenseItemButtonHelper.submitHandler(requestSpy)); + modalElem = (await EditExpenseItemButtonHelper.openModal()).modalElem; - await fillForm({ - amount: '12', - count: '1', - date: '2025-08-29', + await EditExpenseItemFormHelper.fillForm({ + amount: 12, + count: 1, + date: '29.08.2025', description: 'Описание элемента', title: 'Название элемента', - }); + }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await EditExpenseItemFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); expect(requestSpy).toHaveBeenCalledWith({ amount: 12, @@ -138,9 +53,10 @@ describe('TestEditExpenseItemButton', () => { }); test('should close modal after submit', async () => { - expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await EditExpenseItemFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/src/features/expenses/list/AddExpensesListButton/ui/AddExpensesListButton.test.tsx b/frontend/src/features/expenses/list/AddExpensesListButton/ui/AddExpensesListButton.test.tsx index f6fa0f8..bd8795e 100644 --- a/frontend/src/features/expenses/list/AddExpensesListButton/ui/AddExpensesListButton.test.tsx +++ b/frontend/src/features/expenses/list/AddExpensesListButton/ui/AddExpensesListButton.test.tsx @@ -1,71 +1,24 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { AddExpenseListBody, AddExpenseListSuccessResponse } from '@/entity/expenses/list'; - -import AddExpensesListButton from './AddExpensesListButton.ui'; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('test-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const fillForm = async (data: { title: string }) => { - await userEvent.type(screen.getByLabelText('Название'), data.title); -}; - -const submitHandler = (requestSpy: jest.Mock) => http.post( - '/expenses/list', - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json({ - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - items: [], - title: 'Mock list', - updatedAt: '2025-08-29T07:57:40.503Z', - }); - }, -); +import { screen, waitFor } from '@testing-library/react'; +import { AddExpensesListButtonHelper, AddExpensesListFormHelper } from '@tests/helpers'; describe('Test AddExpensesListButton', () => { - test('should correct render button', () => { - const { buttonElem } = renderButton(); + test('should correct render button', async () => { + const { buttonElem } = AddExpensesListButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Добавить'); - const modal = screen.queryByRole('dialog'); - expect(modal).not.toBeInTheDocument(); + const modal = screen.queryByRole('presentation'); + await waitFor(() => { + expect(modal).not.toBeInTheDocument(); + }); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await AddExpensesListButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -73,31 +26,26 @@ describe('Test AddExpensesListButton', () => { describe('should close modal after submit and send request', () => { let modalElem: HTMLElement; - let submitButtonElem: HTMLElement; const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = within(modalElem).getByText('Добавить'); + server.use(AddExpensesListButtonHelper.submitHandler(requestSpy)); + modalElem = (await AddExpensesListButtonHelper.openModal()).modalElem; - await fillForm({ - title: 'Название списка', - }); + await AddExpensesListFormHelper.fillForm({ title: 'Название списка' }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await AddExpensesListFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); - expect(requestSpy).toHaveBeenCalledWith({ - title: 'Название списка', - }); + expect(requestSpy).toHaveBeenCalledWith({ title: 'Название списка' }); }); test('should close modal after submit', async () => { - expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await AddExpensesListFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/src/features/expenses/list/DeleteExpensesListButton/ui/DeleteExpensesListButton.test.tsx b/frontend/src/features/expenses/list/DeleteExpensesListButton/ui/DeleteExpensesListButton.test.tsx index 27e9f59..d344364 100644 --- a/frontend/src/features/expenses/list/DeleteExpensesListButton/ui/DeleteExpensesListButton.test.tsx +++ b/frontend/src/features/expenses/list/DeleteExpensesListButton/ui/DeleteExpensesListButton.test.tsx @@ -1,73 +1,39 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { DeleteExpenseListPath } from '@/entity/expenses/list'; - -import DeleteExpensesListButton from './DeleteExpensesListButton.ui'; - -const deletingList = { - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - items: [], - title: 'Mock list', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('delete-expense-list-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = http.delete(`/expenses/list/${deletingList.id}`, () => HttpResponse.json(deletingList)); +import { DeleteExpensesListButtonHelper, DeleteExpensesListFormHelper } from '@tests/helpers'; describe('Test DeleteExpensesListButton', () => { - test('should correct render button', () => { - const { buttonElem } = renderButton(); + test('should correct render button', async () => { + const { buttonElem } = DeleteExpensesListButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Удалить'); - const modal = screen.queryByRole('dialog'); - expect(modal).not.toBeInTheDocument(); + const modal = screen.queryByRole('presentation'); + await waitFor(() => { + expect(modal).not.toBeInTheDocument(); + }); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await DeleteExpensesListButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); }); test('should close modal after submit', async () => { - server.use(submitHandler); - const { modalElem } = await openModal(); - const submitButtonElem = within(modalElem).getByText('Удалить'); + server.use(DeleteExpensesListButtonHelper.submitHandler); + const { modalElem } = await DeleteExpensesListButtonHelper.openModal(); + const { submitButtonElem } = DeleteExpensesListFormHelper.getFormElems(); expect(submitButtonElem).toBeEnabled(); await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); diff --git a/frontend/src/features/expenses/list/EditExpensesListButton/ui/EditExpensesListButton.test.tsx b/frontend/src/features/expenses/list/EditExpensesListButton/ui/EditExpensesListButton.test.tsx index 2277c40..ef3cd6d 100644 --- a/frontend/src/features/expenses/list/EditExpensesListButton/ui/EditExpensesListButton.test.tsx +++ b/frontend/src/features/expenses/list/EditExpensesListButton/ui/EditExpensesListButton.test.tsx @@ -1,83 +1,24 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { - EditExpenseListBody, - EditExpenseListPath, - EditExpenseListSuccessResponse, -} from '@/entity/expenses/list'; - -import EditExpensesListButton from './EditExpensesListButton.ui'; - -const editingList = { - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - items: [], - title: 'Mock list', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('edit-expense-list-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = (requestSpy: jest.Mock) => http.patch< - EditExpenseListPath, - EditExpenseListBody, - EditExpenseListSuccessResponse ->( - `/expenses/list/${editingList.id}`, - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json(editingList); - }, -); - -const fillForm = async (data: { title: string }) => { - const titleInputElem = screen.getByLabelText('Название'); - await userEvent.clear(titleInputElem); - await userEvent.type(titleInputElem, data.title); -}; +import { screen, waitFor } from '@testing-library/react'; +import { EditExpensesListButtonHelper, EditExpensesListFormHelper } from '@tests/helpers'; describe('Test EditExpensesListButton', () => { - test('should correct render button', () => { - const { buttonElem } = renderButton(); + test('should correct render button', async () => { + const { buttonElem } = EditExpensesListButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Редактировать'); - const modal = screen.queryByRole('dialog'); - expect(modal).not.toBeInTheDocument(); + const modal = screen.queryByRole('presentation'); + await waitFor(() => { + expect(modal).not.toBeInTheDocument(); + }); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await EditExpensesListButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -85,21 +26,17 @@ describe('Test EditExpensesListButton', () => { describe('should close modal after submit and send request', () => { let modalElem: HTMLElement; - let submitButtonElem: HTMLElement; const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = screen.getByText('Сохранить'); + server.use(EditExpensesListButtonHelper.submitHandler(requestSpy)); + modalElem = (await EditExpensesListButtonHelper.openModal()).modalElem; - await fillForm({ - title: 'Название списка', - }); + await EditExpensesListFormHelper.fillForm({ title: 'Название списка' }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await EditExpensesListFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); expect(requestSpy).toHaveBeenCalledWith({ title: 'Название списка', @@ -107,9 +44,10 @@ describe('Test EditExpensesListButton', () => { }); test('should close modal after submit', async () => { - expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await EditExpensesListFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/src/features/incomes/items/AddIncomeItemButton/ui/AddIncomeItemButton.test.tsx b/frontend/src/features/incomes/items/AddIncomeItemButton/ui/AddIncomeItemButton.test.tsx index 315ada2..0ee21ef 100644 --- a/frontend/src/features/incomes/items/AddIncomeItemButton/ui/AddIncomeItemButton.test.tsx +++ b/frontend/src/features/incomes/items/AddIncomeItemButton/ui/AddIncomeItemButton.test.tsx @@ -1,82 +1,22 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { AddIncomeItemBody, AddIncomeItemPath, AddIncomeItemSuccessResponse } from '@/entity/incomes/item'; - -import AddIncomeItemButton from './AddIncomeItemButton.ui'; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('test-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const fillForm = async (data: { amount: string; date: string; description: string; title: string }) => { - await userEvent.type(screen.getByLabelText('Название'), data.title); - await userEvent.type(screen.getByLabelText('Описание'), data.description); - await userEvent.type(screen.getByLabelText('Дата'), data.date); - await userEvent.type(screen.getByLabelText('Сумма'), data.amount); -}; - -const submitHandler = (requestSpy: jest.Mock) => http.post< - AddIncomeItemPath, - AddIncomeItemBody, - AddIncomeItemSuccessResponse ->( - '/incomes/list/123/items', - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json({ - amount: 12, - createdAt: '2025-08-29T07:57:40.503Z', - currency: 'RUB', - date: '2025-08-22T00:00:03.000Z', - description: 'Mock description', - id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', - incomeListId: '10cc093c-3875-4d33-a40a-df526266e262', - title: 'Mock item', - updatedAt: '2025-08-29T07:57:40.503Z', - }); - }, -); +import { screen, waitFor } from '@testing-library/react'; +import { AddIncomeItemButtonHelper, AddIncomeItemFormHelper } from '@tests/helpers'; describe('Test AddIncomeItemButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = AddIncomeItemButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); - expect(buttonElem).toHaveTextContent('Добавить элемент'); + expect(buttonElem).toHaveTextContent('Добавить'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await AddIncomeItemButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -84,24 +24,22 @@ describe('Test AddIncomeItemButton', () => { describe('should close modal after submit and send request', () => { let modalElem: HTMLElement; - let submitButtonElem: HTMLElement; const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = screen.getByText('Добавить'); + server.use(AddIncomeItemButtonHelper.submitHandler(requestSpy)); + modalElem = (await AddIncomeItemButtonHelper.openModal()).modalElem; - await fillForm({ - amount: '12', - date: '2025-08-29', + await AddIncomeItemFormHelper.fillForm({ + amount: 12, + date: '29.08.2025', description: 'Описание элемента', title: 'Название элемента', - }); + }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await AddIncomeItemFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); expect(requestSpy).toHaveBeenCalledWith({ amount: 12, @@ -113,9 +51,10 @@ describe('Test AddIncomeItemButton', () => { }); test('should close modal after submit', async () => { - expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await AddIncomeItemFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/src/features/incomes/items/DeleteIncomeItemButton/ui/DeleteIncomeItemButton.test.tsx b/frontend/src/features/incomes/items/DeleteIncomeItemButton/ui/DeleteIncomeItemButton.test.tsx index 6f19945..6cee691 100644 --- a/frontend/src/features/incomes/items/DeleteIncomeItemButton/ui/DeleteIncomeItemButton.test.tsx +++ b/frontend/src/features/incomes/items/DeleteIncomeItemButton/ui/DeleteIncomeItemButton.test.tsx @@ -1,77 +1,37 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { DeleteIncomeItemPath, IncomeListItem } from '@/entity/incomes/item'; - -import DeleteIncomeItemButton from './DeleteIncomeItemButton.ui'; - -const deletedItem: IncomeListItem = { - amount: 12, - createdAt: '2025-08-29T07:57:40.503Z', - currency: 'RUB', - date: '2025-08-22T00:00:03.000Z', - description: 'Mock description', - id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', - incomeListId: '10cc093c-3875-4d33-a40a-df526266e262', - title: 'Mock item', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('delete-income-item-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = http.delete(`/incomes/list/${deletedItem.incomeListId}/items/${deletedItem.id}`, () => HttpResponse.json(deletedItem)); +import { DeleteIncomeItemButtonHelper, DeleteIncomeItemFormHelper } from '@tests/helpers'; describe('Test DeleteIncomeItemButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = DeleteIncomeItemButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Удалить'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await DeleteIncomeItemButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); }); test('should close modal after submit', async () => { - server.use(submitHandler); - const { modalElem } = await openModal(); - const submitButtonElem = within(modalElem).getByText('Удалить'); + server.use(DeleteIncomeItemButtonHelper.submitHandler); + const { modalElem } = await DeleteIncomeItemButtonHelper.openModal(); + const { submitButtonElem } = DeleteIncomeItemFormHelper.getFormElements(); expect(submitButtonElem).toBeEnabled(); await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); diff --git a/frontend/src/features/incomes/items/EditIncomeItemButton/ui/EditIncomeItemButton.test.tsx b/frontend/src/features/incomes/items/EditIncomeItemButton/ui/EditIncomeItemButton.test.tsx index 8985cc9..5761e54 100644 --- a/frontend/src/features/incomes/items/EditIncomeItemButton/ui/EditIncomeItemButton.test.tsx +++ b/frontend/src/features/incomes/items/EditIncomeItemButton/ui/EditIncomeItemButton.test.tsx @@ -1,100 +1,22 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { - EditIncomeItemBody, - EditIncomeItemPath, - EditIncomeItemSuccessResponse, - IncomeListItem, -} from '@/entity/incomes/item'; - -import EditIncomeItemButton from './EditIncomeItemButton.ui'; - -const editingItem: IncomeListItem = { - amount: 12, - createdAt: '2025-08-29T07:57:40.503Z', - currency: 'RUB', - date: '2025-08-22T00:00:03.000Z', - description: 'Mock description', - id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', - incomeListId: '10cc093c-3875-4d33-a40a-df526266e262', - title: 'Mock item', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('edit-income-item-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = (requestSpy: jest.Mock) => http.patch< - EditIncomeItemPath, - EditIncomeItemBody, - EditIncomeItemSuccessResponse ->( - `/incomes/list/${editingItem.incomeListId}/items/${editingItem.id}`, - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json(editingItem); - }, -); - -const fillForm = async (data: { amount: string; date: string; description: string; title: string }) => { - const titleInputElem = screen.getByLabelText('Название'); - await userEvent.clear(titleInputElem); - await userEvent.type(titleInputElem, data.title); - - const descriptionInputElem = screen.getByLabelText('Описание'); - await userEvent.clear(descriptionInputElem); - await userEvent.type(descriptionInputElem, data.description); - - const dateInputElem = screen.getByLabelText('Дата'); - await userEvent.clear(dateInputElem); - await userEvent.type(dateInputElem, data.date); - - const amountInputElem = screen.getByLabelText('Сумма'); - await userEvent.clear(amountInputElem); - await userEvent.type(amountInputElem, data.amount); -}; +import { screen, waitFor } from '@testing-library/react'; +import { EditIncomeItemButtonHelper, EditIncomeItemFormHelper } from '@tests/helpers'; describe('TestEditIncomeItemButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = EditIncomeItemButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Редактировать'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await EditIncomeItemButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -102,24 +24,22 @@ describe('TestEditIncomeItemButton', () => { describe('should close modal after submit and send request', () => { let modalElem: HTMLElement; - let submitButtonElem: HTMLElement; const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = screen.getByText('Сохранить'); + server.use(EditIncomeItemButtonHelper.submitHandler(requestSpy)); + modalElem = (await EditIncomeItemButtonHelper.openModal()).modalElem; - await fillForm({ - amount: '12', - date: '2025-08-29', + await EditIncomeItemFormHelper.fillForm({ + amount: 12, + date: '29.08.2025', description: 'Описание элемента', title: 'Название элемента', - }); + }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await EditIncomeItemFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); expect(requestSpy).toHaveBeenCalledWith({ amount: 12, @@ -131,9 +51,10 @@ describe('TestEditIncomeItemButton', () => { }); test('should close modal after submit', async () => { - expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await EditIncomeItemFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/src/features/incomes/list/AddIncomeListButton/ui/AddIncomeListButton.test.tsx b/frontend/src/features/incomes/list/AddIncomeListButton/ui/AddIncomeListButton.test.tsx index f2dc294..ed126e1 100644 --- a/frontend/src/features/incomes/list/AddIncomeListButton/ui/AddIncomeListButton.test.tsx +++ b/frontend/src/features/incomes/list/AddIncomeListButton/ui/AddIncomeListButton.test.tsx @@ -1,71 +1,22 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { AddExpenseListBody, AddExpenseListSuccessResponse } from '@/entity/expenses/list'; - -import AddIncomeListButton from './AddIncomeListButton.ui'; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('test-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const fillForm = async (data: { title: string }) => { - await userEvent.type(screen.getByLabelText('Название'), data.title); -}; - -const submitHandler = (requestSpy: jest.Mock) => http.post( - '/incomes/list', - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json({ - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - items: [], - title: 'Mock list', - updatedAt: '2025-08-29T07:57:40.503Z', - }); - }, -); +import { screen, waitFor } from '@testing-library/react'; +import { AddIncomeListButtonHelper, AddIncomeListFormHelper } from '@tests/helpers'; describe('Test AddIncomeListButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = AddIncomeListButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Добавить'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await AddIncomeListButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -73,31 +24,26 @@ describe('Test AddIncomeListButton', () => { describe('should close modal after submit and send request', () => { let modalElem: HTMLElement; - let submitButtonElem: HTMLElement; const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = within(modalElem).getByText('Добавить'); + server.use(AddIncomeListButtonHelper.submitHandler(requestSpy)); + modalElem = (await AddIncomeListButtonHelper.openModal()).modalElem; - await fillForm({ - title: 'Название списка', - }); + await AddIncomeListFormHelper.fillForm({ title: 'Название списка' }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await AddIncomeListFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); - expect(requestSpy).toHaveBeenCalledWith({ - title: 'Название списка', - }); + expect(requestSpy).toHaveBeenCalledWith({ title: 'Название списка' }); }); test('should close modal after submit', async () => { - expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await AddIncomeListFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/src/features/incomes/list/DeleteIncomeListButton/ui/DeleteIncomeListButton.test.tsx b/frontend/src/features/incomes/list/DeleteIncomeListButton/ui/DeleteIncomeListButton.test.tsx index 528b6af..8aaeebe 100644 --- a/frontend/src/features/incomes/list/DeleteIncomeListButton/ui/DeleteIncomeListButton.test.tsx +++ b/frontend/src/features/incomes/list/DeleteIncomeListButton/ui/DeleteIncomeListButton.test.tsx @@ -1,74 +1,37 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen, within } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { DeleteExpenseListPath } from '@/entity/expenses/list'; -import type { IncomeList } from '@/entity/incomes/list'; - -import DeleteIncomeListButton from './DeleteIncomeListButton.ui'; - -const deletingList: IncomeList = { - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - items: [], - title: 'Mock list', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('delete-income-list-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = http.delete(`/incomes/list/${deletingList.id}`, () => HttpResponse.json(deletingList)); +import { DeleteIncomeListButtonHelper, DeleteIncomeListFormHelper } from '@tests/helpers'; describe('Test DeleteIncomeListButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = DeleteIncomeListButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Удалить'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await DeleteIncomeListButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); }); test('should close modal after submit', async () => { - server.use(submitHandler); - const { modalElem } = await openModal(); - const submitButtonElem = within(modalElem).getByText('Удалить'); + server.use(DeleteIncomeListButtonHelper.submitHandler); + const { modalElem } = await DeleteIncomeListButtonHelper.openModal(); + const { submitButtonElem } = DeleteIncomeListFormHelper.getFormElements(); expect(submitButtonElem).toBeEnabled(); await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); diff --git a/frontend/src/features/incomes/list/EditIncomeListButton/ui/EditIncomeListButton.test.tsx b/frontend/src/features/incomes/list/EditIncomeListButton/ui/EditIncomeListButton.test.tsx index beb307e..8aecef9 100644 --- a/frontend/src/features/incomes/list/EditIncomeListButton/ui/EditIncomeListButton.test.tsx +++ b/frontend/src/features/incomes/list/EditIncomeListButton/ui/EditIncomeListButton.test.tsx @@ -1,83 +1,22 @@ import { server } from '@mocks/jest/server'; -import { createReactQueryWrapper } from '@mocks/jest/wrappers'; -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { http, HttpResponse } from 'msw'; - -import type { - EditExpenseListBody, - EditExpenseListPath, - EditExpenseListSuccessResponse, -} from '@/entity/expenses/list'; - -import EditIncomeListButton from './EditIncomeListButton.ui'; - -const editingList = { - createdAt: '2025-08-29T07:57:40.503Z', - id: '10cc093c-3875-4d33-a40a-df526266e262', - items: null, - title: 'Mock list', - updatedAt: '2025-08-29T07:57:40.503Z', -}; - -const renderButton = () => { - render( - , - { wrapper: createReactQueryWrapper() }, - ); - - const buttonElem = screen.getByTestId('edit-expense-list-button'); - - return { - buttonElem, - }; -}; - -const openModal = async () => { - const { buttonElem } = renderButton(); - await userEvent.click(buttonElem); - const modalElem = screen.getByRole('dialog'); - - return { - modalElem, - }; -}; - -const submitHandler = (requestSpy: jest.Mock) => http.patch< - EditExpenseListPath, - EditExpenseListBody, - EditExpenseListSuccessResponse ->( - `/incomes/list/${editingList.id}`, - async ({ request }) => { - const body = await request.json(); - requestSpy(body); - - return HttpResponse.json(editingList); - }, -); - -const fillForm = async (data: { title: string }) => { - const titleInputElem = screen.getByLabelText('Название'); - await userEvent.clear(titleInputElem); - await userEvent.type(titleInputElem, data.title); -}; +import { screen, waitFor } from '@testing-library/react'; +import { EditIncomeListButtonHelper, EditIncomeListFormHelper } from '@tests/helpers'; describe('Test EditIncomeListButton', () => { test('should correct render button', () => { - const { buttonElem } = renderButton(); + const { buttonElem } = EditIncomeListButtonHelper.renderButton(); expect(buttonElem).toBeInTheDocument(); expect(buttonElem).toBeVisible(); expect(buttonElem).toBeEnabled(); expect(buttonElem).toHaveTextContent('Редактировать'); - const modal = screen.queryByRole('dialog'); + const modal = screen.queryByRole('presentation'); expect(modal).not.toBeInTheDocument(); }); test('should open modal after click', async () => { - const { modalElem } = await openModal(); + const { modalElem } = await EditIncomeListButtonHelper.openModal(); expect(modalElem).toBeInTheDocument(); expect(modalElem).toBeVisible(); @@ -85,31 +24,26 @@ describe('Test EditIncomeListButton', () => { describe('should close modal after submit and send request', () => { let modalElem: HTMLElement; - let submitButtonElem: HTMLElement; const requestSpy = jest.fn(); beforeEach(async () => { - server.use(submitHandler(requestSpy)); - modalElem = (await openModal()).modalElem; - submitButtonElem = screen.getByText('Сохранить'); + server.use(EditIncomeListButtonHelper.submitHandler(requestSpy)); + modalElem = (await EditIncomeListButtonHelper.openModal()).modalElem; - await fillForm({ - title: 'Название списка', - }); + await EditIncomeListFormHelper.fillForm({ title: 'Название списка' }, { autoSubmit: false }); }); test('should send correct request', async () => { - await userEvent.click(submitButtonElem); + await EditIncomeListFormHelper.submitForm(); expect(requestSpy).toHaveBeenCalledTimes(1); - expect(requestSpy).toHaveBeenCalledWith({ - title: 'Название списка', - }); + expect(requestSpy).toHaveBeenCalledWith({ title: 'Название списка' }); }); test('should close modal after submit', async () => { - expect(submitButtonElem).toBeEnabled(); - await userEvent.click(submitButtonElem); - expect(modalElem).not.toBeInTheDocument(); + await EditIncomeListFormHelper.submitForm(); + await waitFor(() => { + expect(modalElem).not.toBeInTheDocument(); + }); }); }); }); diff --git a/frontend/tests/helpers/bank-card/AddBankCardButton.tsx b/frontend/tests/helpers/bank-card/AddBankCardButton.tsx new file mode 100644 index 0000000..775b1a5 --- /dev/null +++ b/frontend/tests/helpers/bank-card/AddBankCardButton.tsx @@ -0,0 +1,55 @@ +import { bankCardList } from '@mocks/browser/data'; +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { AddBankCardBody, AddBankCardResponse } from '@/entity/cards'; + +import { AddBankCardButton } from '@/features/bank-cards/AddBankCardButton'; +import { DataTestId } from '@/shared/constants'; + +const bankCard = bankCardList[0]; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.ADD_BANK_CARD_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.post< + never, + AddBankCardBody, + AddBankCardResponse +>( + '/bank-card', + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json(bankCard); + }, +); + +export const AddBankCardButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/bank-card/AddBankCardForm.tsx b/frontend/tests/helpers/bank-card/AddBankCardForm.tsx new file mode 100644 index 0000000..f5d2532 --- /dev/null +++ b/frontend/tests/helpers/bank-card/AddBankCardForm.tsx @@ -0,0 +1,61 @@ +import { typeTextField } from '@mocks/jest/helpers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { AddBankCardFormProps, AddBankCardFormValues } from '@/entity/cards'; + +import { AddBankCardForm } from '@/entity/cards'; +import { DataTestId } from '@/shared/constants'; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.ADD_BANK_CARD_FORM); + const nameInputElem = screen.getByLabelText(/Название/); + const balanceInputElem = screen.getByLabelText(/Баланс/); + const submitButtonElem = screen.getByTestId(DataTestId.ADD_BANK_CARD_SUBMIT_BUTTON); + + return { + balanceInputElem, + formElem, + nameInputElem, + submitButtonElem, + }; +}; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render(); + + return { + onSubmit, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + + await userEvent.click(submitButtonElem); +}; + +const fillForm = async ( + data: AddBankCardFormValues, + options: { autoSubmit?: boolean } = {}, +) => { + const { balance, name } = data; + const { autoSubmit = true } = options; + const { balanceInputElem, nameInputElem } = getFormElements(); + + await typeTextField(balanceInputElem, balance.toString()); + await typeTextField(nameInputElem, name); + + if (autoSubmit) { + await submitForm(); + } +}; + +export const AddBankCardFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/bank-card/DeleteBankCardButton.tsx b/frontend/tests/helpers/bank-card/DeleteBankCardButton.tsx new file mode 100644 index 0000000..cebabf7 --- /dev/null +++ b/frontend/tests/helpers/bank-card/DeleteBankCardButton.tsx @@ -0,0 +1,46 @@ +import { bankCardList } from '@mocks/browser/data'; +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { DeleteExpenseListPath } from '@/entity/expenses/list'; + +import { DeleteBankCardButton } from '@/features/bank-cards/DeleteBankCardButton'; +import { DataTestId } from '@/shared/constants'; + +const bankCard = bankCardList[0]; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.DELETE_BANK_CARD_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = http.delete( + `/bank-card/${bankCard.id}`, + () => HttpResponse.json(bankCard), +); + +export const DeleteBankCardButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/bank-card/DeleteBankCardForm.tsx b/frontend/tests/helpers/bank-card/DeleteBankCardForm.tsx new file mode 100644 index 0000000..ec87a4b --- /dev/null +++ b/frontend/tests/helpers/bank-card/DeleteBankCardForm.tsx @@ -0,0 +1,48 @@ +import { bankCardList } from '@mocks/browser/data'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { DeleteBankCardFormProps } from '@/entity/cards'; + +import { DeleteBankCardForm } from '@/entity/cards'; +import { DataTestId } from '@/shared/constants'; + +const bankCard = bankCardList[0]; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + ); + + return { + onSubmit, + }; +}; + +function getFormElements() { + const formElem = screen.getByTestId(DataTestId.DELETE_BANK_CARD_FORM); + const submitButtonElem = screen.getByTestId(DataTestId.DELETE_BANK_CARD_SUBMIT_BUTTON); + + return { + formElem, + submitButtonElem, + }; +} + +async function submitForm() { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +} + +export const DeleteBankCardFormHelper = { + bankCard, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/bank-card/EditBankCardButton.tsx b/frontend/tests/helpers/bank-card/EditBankCardButton.tsx new file mode 100644 index 0000000..3c10bad --- /dev/null +++ b/frontend/tests/helpers/bank-card/EditBankCardButton.tsx @@ -0,0 +1,59 @@ +import { bankCardList } from '@mocks/browser/data'; +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { + EditBankCardBody, + EditBankCardPath, + EditBankCardResponse, +} from '@/entity/cards'; + +import { EditBankCardButton } from '@/features/bank-cards/EditBankCardButton'; +import { DataTestId } from '@/shared/constants'; + +const bankCard = bankCardList[0]; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.EDIT_BANK_CARD_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.patch< + EditBankCardPath, + EditBankCardBody, + EditBankCardResponse +>( + `/bank-card/${bankCard.id}`, + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json(bankCard); + }, +); + +export const EditBankCardButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/bank-card/EditBankCardForm.tsx b/frontend/tests/helpers/bank-card/EditBankCardForm.tsx new file mode 100644 index 0000000..77f4045 --- /dev/null +++ b/frontend/tests/helpers/bank-card/EditBankCardForm.tsx @@ -0,0 +1,69 @@ +import { bankCardList } from '@mocks/browser/data'; +import { typeTextField } from '@mocks/jest/helpers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { EditBankCardFormProps, EditBankCardFormValues } from '@/entity/cards'; + +import { EditBankCardForm } from '@/entity/cards'; +import { DataTestId } from '@/shared/constants'; + +const bankCard = bankCardList[0]; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + ); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.EDIT_BANK_CARD_FORM); + const nameInputElem = screen.getByLabelText(/Название/); + const balanceInputElem = screen.getByLabelText(/Баланс/); + const submitButtonElem = screen.getByRole('button', { name: /Сохранить/i }); + + return { + balanceInputElem, + formElem, + nameInputElem, + submitButtonElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +const fillForm = async ( + data: EditBankCardFormValues, + options: { autoSubmit?: boolean } = {}, +) => { + const { balance, name } = data; + const { autoSubmit = true } = options; + const { balanceInputElem, nameInputElem } = getFormElements(); + + await typeTextField(balanceInputElem, balance.toString()); + await typeTextField(nameInputElem, name); + + if (autoSubmit) { + await submitForm(); + } +}; + +export const EditBankCardFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/cash/AddCashButton.tsx b/frontend/tests/helpers/cash/AddCashButton.tsx new file mode 100644 index 0000000..b145c7a --- /dev/null +++ b/frontend/tests/helpers/cash/AddCashButton.tsx @@ -0,0 +1,55 @@ +import { cashList } from '@mocks/browser/data'; +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { AdCashResponse, AddCashBody } from '@/entity/cash'; + +import { AddCashButton } from '@/features/cash/AddCashButton'; +import { DataTestId } from '@/shared/constants'; + +const cash = cashList[0]; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.ADD_CASH_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.post< + never, + AddCashBody, + AdCashResponse +>( + '/cash', + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json(cash); + }, +); + +export const AddCashButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/cash/AddCashForm.tsx b/frontend/tests/helpers/cash/AddCashForm.tsx new file mode 100644 index 0000000..549e38e --- /dev/null +++ b/frontend/tests/helpers/cash/AddCashForm.tsx @@ -0,0 +1,61 @@ +import { typeTextField } from '@mocks/jest/helpers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { AddCashFormProps, AddCashFormValues } from '@/entity/cash'; + +import { AddCashForm } from '@/entity/cash'; +import { DataTestId } from '@/shared/constants'; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render(); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.ADD_CASH_FORM); + const nameInputElem = screen.getByLabelText(/Название/); + const balanceInputElem = screen.getByLabelText(/Баланс/); + const submitButtonElem = screen.getByTestId(DataTestId.ADD_CASH_SUBMIT_BUTTON); + + return { + balanceInputElem, + formElem, + nameInputElem, + submitButtonElem, + }; +}; + +async function submitForm() { + const { submitButtonElem } = getFormElements(); + + await userEvent.click(submitButtonElem); +} + +const fillForm = async ( + data: AddCashFormValues, + options: { autoSubmit?: boolean } = {}, +) => { + const { balance, name } = data; + const { autoSubmit = true } = options; + const { balanceInputElem, nameInputElem, submitButtonElem } = getFormElements(); + + await typeTextField(balanceInputElem, balance.toString()); + await typeTextField(nameInputElem, name); + + if (autoSubmit) { + await userEvent.click(submitButtonElem); + } +}; + +export const AddCashFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/cash/DeleteCashButton.tsx b/frontend/tests/helpers/cash/DeleteCashButton.tsx new file mode 100644 index 0000000..2af8d5d --- /dev/null +++ b/frontend/tests/helpers/cash/DeleteCashButton.tsx @@ -0,0 +1,46 @@ +import { cashList } from '@mocks/browser/data'; +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { DeleteExpenseListPath } from '@/entity/expenses/list'; + +import { DeleteCashButton } from '@/features/cash/DeleteCashButton'; +import { DataTestId } from '@/shared/constants'; + +const cash = cashList[0]; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.DELETE_CASH_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = http.delete( + `/cash/${cash.id}`, + () => HttpResponse.json(cash), +); + +export const DeleteCashButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/cash/DeleteCashForm.tsx b/frontend/tests/helpers/cash/DeleteCashForm.tsx new file mode 100644 index 0000000..bf4203e --- /dev/null +++ b/frontend/tests/helpers/cash/DeleteCashForm.tsx @@ -0,0 +1,48 @@ +import { cashList } from '@mocks/browser/data'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { DeleteCashFormProps } from '@/entity/cash'; + +import { DeleteCashForm } from '@/entity/cash'; +import { DataTestId } from '@/shared/constants'; + +const cash = cashList[0]; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + ); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.DELETE_CASH_FORM); + const submitButtonElem = screen.getByTestId(DataTestId.DELETE_CASH_SUBMIT_BUTTON); + + return { + formElem, + submitButtonElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +export const DeleteCashFormHelper = { + cash, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/cash/EditCashButton.tsx b/frontend/tests/helpers/cash/EditCashButton.tsx new file mode 100644 index 0000000..24ac339 --- /dev/null +++ b/frontend/tests/helpers/cash/EditCashButton.tsx @@ -0,0 +1,59 @@ +import { cashList } from '@mocks/browser/data'; +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { + EditCashBody, + EditCashPath, + EditCashResponse, +} from '@/entity/cash'; + +import { EditCashButton } from '@/features/cash/EditCashButton'; +import { DataTestId } from '@/shared/constants'; + +const cash = cashList[0]; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.EDIT_CASH_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.patch< + EditCashPath, + EditCashBody, + EditCashResponse +>( + `/cash/${cash.id}`, + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json(cash); + }, +); + +export const EditCashButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/cash/EditCashForm.tsx b/frontend/tests/helpers/cash/EditCashForm.tsx new file mode 100644 index 0000000..5a47846 --- /dev/null +++ b/frontend/tests/helpers/cash/EditCashForm.tsx @@ -0,0 +1,69 @@ +import { cashList } from '@mocks/browser/data'; +import { typeTextField } from '@mocks/jest/helpers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { EditCashFormProps, EditCashFormValues } from '@/entity/cash'; + +import { EditCashForm } from '@/entity/cash'; +import { DataTestId } from '@/shared/constants'; + +const cash = cashList[0]; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + ); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.EDIT_CASH_FORM); + const nameInputElem = screen.getByLabelText(/Название/); + const balanceInputElem = screen.getByLabelText(/Баланс/); + const submitButtonElem = screen.getByRole('button', { name: /Сохранить/i }); + + return { + balanceInputElem, + formElem, + nameInputElem, + submitButtonElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +const fillForm = async ( + data: EditCashFormValues, + options: { autoSubmit?: boolean } = {}, +) => { + const { balance, name } = data; + const { autoSubmit = true } = options; + const { balanceInputElem, nameInputElem } = getFormElements(); + + await typeTextField(balanceInputElem, balance.toString()); + await typeTextField(nameInputElem, name); + + if (autoSubmit) { + await submitForm(); + } +}; + +export const EditCashFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/expense/item/AddExpenseItemButton.tsx b/frontend/tests/helpers/expense/item/AddExpenseItemButton.tsx new file mode 100644 index 0000000..93582df --- /dev/null +++ b/frontend/tests/helpers/expense/item/AddExpenseItemButton.tsx @@ -0,0 +1,69 @@ +import { getBankCardList, getCashList } from '@mocks/browser/handlers'; +import { server } from '@mocks/jest/server'; +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { AddExpenseItemBody, AddExpenseItemPath, AddExpenseItemSuccessResponse } from '@/entity/expenses/item'; + +import { AddExpenseItemButton } from '@/features/expenses/items/AddExpenseItemButton'; +import { DataTestId } from '@/shared/constants'; + +const expenseItem = { + amount: 12, + count: 12, + createdAt: '2025-08-29T07:57:40.503Z', + currency: 'RUB', + date: '2025-08-22T00:00:03.000Z', + description: 'Mock description', + expenseListId: '10cc093c-3875-4d33-a40a-df526266e262', + id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', + payment: null, + title: 'Mock item', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderButton = () => { + server.use(getBankCardList, getCashList); + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.ADD_EXPENSE_ITEM_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.post< + AddExpenseItemPath, + AddExpenseItemBody, + AddExpenseItemSuccessResponse +>( + '/expenses/list/123/items', + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json(expenseItem); + }, +); + +export const AddExpenseItemButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/expense/item/AddExpenseItemForm.tsx b/frontend/tests/helpers/expense/item/AddExpenseItemForm.tsx new file mode 100644 index 0000000..54f7b9d --- /dev/null +++ b/frontend/tests/helpers/expense/item/AddExpenseItemForm.tsx @@ -0,0 +1,144 @@ +import { getBankCardList, getCashList } from '@mocks/browser/handlers'; +import { selectOption, typeDateField, typeTextField } from '@mocks/jest/helpers'; +import { server } from '@mocks/jest/server'; +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { BankCard } from '@/entity/cards'; +import type { CashItem } from '@/entity/cash'; +import type { AddExpenseItemFormProps } from '@/entity/expenses/item'; + +import { AddExpenseItemForm } from '@/entity/expenses/item'; +import { DataTestId } from '@/shared/constants'; + +const renderForm = (props?: Partial) => { + server.use(getBankCardList, getCashList); + const onSubmit = jest.fn(); + + render( + , + { wrapper: createAppWrapper() }, + ); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.ADD_EXPENSE_ITEM_FORM); + const titleInputElem = screen.getByLabelText(/Название/); + const descriptionInputElem = screen.getByLabelText(/Описание/); + const dateInputElem = screen.getAllByLabelText(/Дата/i)[1]; + const amountInputElem = screen.getByLabelText(/Сумма/); + const countInputElem = screen.getByLabelText(/Количество/); + const submitButtonElem = screen.getByTestId(DataTestId.ADD_EXPENSE_ITEM_SUBMIT_BUTTON); + const paymentTypeSelect = screen.getByTestId('payment-type-select'); + + const bankCardSelect = screen.queryByTestId('bank-card-select'); + const bankCardSelectInput = screen.queryByTestId('bank-card-select-input'); + + const cashSelect = screen.queryByTestId('cash-select'); + const cashSelectInput = screen.queryByTestId('cash-select-input'); + + return { + amountInputElem, + bankCardSelect, + bankCardSelectInput, + cashSelect, + cashSelectInput, + countInputElem, + dateInputElem, + descriptionInputElem, + formElem, + paymentTypeSelect, + submitButtonElem, + titleInputElem, + }; +}; + +type ExpenseFormValues = { + amount?: string; + count?: string; + date?: string; + description?: string; + // eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents + paymentId?: BankCard['id'] | CashItem['id']; + paymentType?: 'BANK_CARD' | 'CASH' | 'none'; + title?: string; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +const fillForm = async ( + data: ExpenseFormValues, + options: { autoSubmit?: boolean } = {}, +) => { + const { + amount, + count, + date, + description, + paymentId, + paymentType = 'none', + title, + } = data; + const { autoSubmit = true } = options; + const { + amountInputElem, + countInputElem, + dateInputElem, + descriptionInputElem, + paymentTypeSelect, + titleInputElem, + } = getFormElements(); + + await typeTextField(titleInputElem, title); + await typeTextField(descriptionInputElem, description); + await typeTextField(amountInputElem, amount); + await typeTextField(countInputElem, count); + await typeDateField(dateInputElem, date); + + if (paymentType !== 'none') { + switch (paymentType) { + case 'BANK_CARD': + await selectOption(paymentTypeSelect, 'BANK_CARD'); + break; + case 'CASH': + await selectOption(paymentTypeSelect, 'CASH'); + break; + default: + break; + } + + if (!paymentId) { + return; + } + const { bankCardSelect, cashSelect } = getFormElements(); + switch (paymentType) { + case 'BANK_CARD': + await selectOption(bankCardSelect, paymentId); + break; + case 'CASH': + await selectOption(cashSelect, paymentId); + break; + default: + break; + } + } + + if (autoSubmit) { + await submitForm(); + } +}; + +export const AddExpenseItemFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/expense/item/DeleteExpenseItemButton.tsx b/frontend/tests/helpers/expense/item/DeleteExpenseItemButton.tsx new file mode 100644 index 0000000..4f15c50 --- /dev/null +++ b/frontend/tests/helpers/expense/item/DeleteExpenseItemButton.tsx @@ -0,0 +1,57 @@ +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { DeleteExpenseItemPath } from '@/entity/expenses/item'; + +import { DeleteExpenseItemButton } from '@/features/expenses/items/DeleteExpenseItemButton'; +import { DataTestId } from '@/shared/constants'; + +const expenseItem = { + amount: 12, + count: 12, + createdAt: '2025-08-29T07:57:40.503Z', + currency: 'RUB', + date: '2025-08-22T00:00:03.000Z', + description: 'Mock description', + expenseListId: '10cc093c-3875-4d33-a40a-df526266e262', + id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', + payment: null, + title: 'Mock item', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.DELETE_EXPENSE_ITEM_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = http.delete( + `/expenses/list/${expenseItem.expenseListId}/items/${expenseItem.id}`, + () => HttpResponse.json(expenseItem), +); + +export const DeleteExpenseItemButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/expense/item/DeleteExpenseItemForm.tsx b/frontend/tests/helpers/expense/item/DeleteExpenseItemForm.tsx new file mode 100644 index 0000000..5b5b2c7 --- /dev/null +++ b/frontend/tests/helpers/expense/item/DeleteExpenseItemForm.tsx @@ -0,0 +1,59 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { DeleteExpenseItemFormProps } from '@/entity/expenses/item'; + +import { DeleteExpenseItemForm } from '@/entity/expenses/item'; +import { DataTestId } from '@/shared/constants'; + +const expenseItem = { + amount: 12, + count: 12, + createdAt: '2025-08-29T07:57:40.503Z', + currency: 'RUB', + date: '2025-08-22T00:00:03.000Z', + description: 'Mock description', + expenseListId: '10cc093c-3875-4d33-a40a-df526266e262', + id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', + payment: null, + title: 'Mock item', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + ); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.DELETE_EXPENSE_ITEM_FORM); + const submitButtonElem = screen.getByTestId(DataTestId.DELETE_EXPENSE_ITEM_SUBMIT_BUTTON); + + return { + formElem, + submitButtonElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +export const DeleteExpenseItemFormHelper = { + expenseItem, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/expense/item/EditExpenseItemButton.tsx b/frontend/tests/helpers/expense/item/EditExpenseItemButton.tsx new file mode 100644 index 0000000..addc818 --- /dev/null +++ b/frontend/tests/helpers/expense/item/EditExpenseItemButton.tsx @@ -0,0 +1,70 @@ +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { + EditExpenseItemBody, + EditExpenseItemPath, + EditExpenseItemSuccessResponse, +} from '@/entity/expenses/item'; + +import { EditExpenseItemButton } from '@/features/expenses/items/EditExpenseItemButton'; +import { DataTestId } from '@/shared/constants'; + +const expenseItem = { + amount: 12, + count: 12, + createdAt: '2025-08-29T07:57:40.503Z', + currency: 'RUB', + date: '2025-08-22T00:00:03.000Z', + description: 'Mock description', + expenseListId: '10cc093c-3875-4d33-a40a-df526266e262', + id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', + payment: null, + title: 'Mock item', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.EDIT_EXPENSE_ITEM_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.patch< + EditExpenseItemPath, + EditExpenseItemBody, + EditExpenseItemSuccessResponse +>( + `/expenses/list/${expenseItem.expenseListId}/items/${expenseItem.id}`, + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json(expenseItem); + }, +); + +export const EditExpenseItemButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/expense/item/EditExpenseItemForm.tsx b/frontend/tests/helpers/expense/item/EditExpenseItemForm.tsx new file mode 100644 index 0000000..5c287ef --- /dev/null +++ b/frontend/tests/helpers/expense/item/EditExpenseItemForm.tsx @@ -0,0 +1,103 @@ +import { typeDateField, typeTextField } from '@mocks/jest/helpers'; +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { EditExpenseItemFormProps, EditExpenseItemFormValues } from '@/entity/expenses/item'; + +import { EditExpenseItemForm } from '@/entity/expenses/item'; +import { DataTestId } from '@/shared/constants'; + +const expenseItem = { + amount: 12, + count: 12, + createdAt: '2025-08-22T07:57:40.503Z', + currency: 'RUB', + date: '2025-08-22T00:00:03.000Z', + description: 'Mock description', + expenseListId: '10cc093c-3875-4d33-a40a-df526266e262', + id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', + payment: null, + title: 'Mock item', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + { wrapper: createAppWrapper() }, + ); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.EDIT_EXPENSE_ITEM_FORM); + const titleInputElem = screen.getByLabelText(/Название/); + const descriptionInputElem = screen.getByLabelText(/Описание/); + const dateInputElem = screen.getAllByLabelText(/Дата/i)[1]; + const amountInputElem = screen.getByLabelText(/Сумма/); + const countInputElem = screen.getByLabelText(/Количество/); + const submitButtonElem = screen.getByRole('button', { name: /Сохранить/i }); + + return { + amountInputElem, + countInputElem, + dateInputElem, + descriptionInputElem, + formElem, + submitButtonElem, + titleInputElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +const fillForm = async ( + data: Omit, + options: { autoSubmit?: boolean } = {}, +) => { + const { + amount, + count, + date, + description, + title, + } = data; + const { autoSubmit = true } = options; + const { + amountInputElem, + countInputElem, + dateInputElem, + descriptionInputElem, + titleInputElem, + } = getFormElements(); + + await typeTextField(titleInputElem, title); + await typeTextField(descriptionInputElem, description); + await typeTextField(amountInputElem, amount.toString()); + await typeTextField(countInputElem, count.toString()); + await typeDateField(dateInputElem, date); + + if (autoSubmit) { + await submitForm(); + } +}; + +export const EditExpenseItemFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/expense/list/AddExpensesListButton.tsx b/frontend/tests/helpers/expense/list/AddExpensesListButton.tsx new file mode 100644 index 0000000..f66d49f --- /dev/null +++ b/frontend/tests/helpers/expense/list/AddExpensesListButton.tsx @@ -0,0 +1,58 @@ +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { AddExpenseListBody, AddExpenseListSuccessResponse } from '@/entity/expenses/list'; + +import { AddExpensesListButton } from '@/features/expenses/list/AddExpensesListButton'; +import { DataTestId } from '@/shared/constants'; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.ADD_EXPENSES_LIST_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.post< + never, + AddExpenseListBody, + AddExpenseListSuccessResponse +>( + '/expenses/list', + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json({ + createdAt: '2025-08-29T07:57:40.503Z', + id: '10cc093c-3875-4d33-a40a-df526266e262', + items: [], + title: 'Mock list', + updatedAt: '2025-08-29T07:57:40.503Z', + }); + }, +); + +export const AddExpensesListButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/expense/list/AddExpensesListForm.tsx b/frontend/tests/helpers/expense/list/AddExpensesListForm.tsx new file mode 100644 index 0000000..6856371 --- /dev/null +++ b/frontend/tests/helpers/expense/list/AddExpensesListForm.tsx @@ -0,0 +1,59 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { AddExpenseListFormProps, AddExpenseListFormValues } from '@/entity/expenses/list'; + +import { AddExpensesListForm } from '@/entity/expenses/list'; +import { DataTestId } from '@/shared/constants'; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render(); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.ADD_EXPENSES_LIST_FORM); + const titleInputElem = screen.getByLabelText(/Название/i); + const submitButtonElem = screen.getByTestId(DataTestId.ADD_EXPENSES_LIST_SUBMIT_BUTTON); + + return { + formElem, + submitButtonElem, + titleInputElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +const fillForm = async ( + data: AddExpenseListFormValues, + options: { autoSubmit?: boolean } = {}, +) => { + const { title } = data; + const { autoSubmit = true } = options; + const { titleInputElem } = getFormElements(); + + if (title) { + await userEvent.clear(titleInputElem); + await userEvent.type(titleInputElem, title); + } + + if (autoSubmit) { + await submitForm(); + } +}; + +export const AddExpensesListFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/expense/list/DeleteExpensesListButton.tsx b/frontend/tests/helpers/expense/list/DeleteExpensesListButton.tsx new file mode 100644 index 0000000..93d3ca8 --- /dev/null +++ b/frontend/tests/helpers/expense/list/DeleteExpensesListButton.tsx @@ -0,0 +1,51 @@ +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { DeleteExpenseListPath } from '@/entity/expenses/list'; + +import { DeleteExpensesListButton } from '@/features/expenses/list/DeleteExpensesListButton'; +import { DataTestId } from '@/shared/constants'; + +const expenseList = { + createdAt: '2025-08-29T07:57:40.503Z', + id: '10cc093c-3875-4d33-a40a-df526266e262', + items: [], + title: 'Mock list', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.DELETE_EXPENSES_LIST_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = http.delete( + `/expenses/list/${expenseList.id}`, + () => HttpResponse.json(expenseList), +); + +export const DeleteExpensesListButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/expense/list/DeleteExpensesListForm.tsx b/frontend/tests/helpers/expense/list/DeleteExpensesListForm.tsx new file mode 100644 index 0000000..d1a4d1a --- /dev/null +++ b/frontend/tests/helpers/expense/list/DeleteExpensesListForm.tsx @@ -0,0 +1,53 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { DeleteExpenseListFormProps } from '@/entity/expenses/list'; + +import { DeleteExpensesListForm } from '@/entity/expenses/list'; +import { DataTestId } from '@/shared/constants'; + +const expensesList = { + createdAt: '2025-08-29T07:57:40.503Z', + id: '10cc093c-3875-4d33-a40a-df526266e262', + items: [], + title: 'Mock list', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + ); + + return { + onSubmit, + }; +}; + +const getFormElems = () => { + const formElem = screen.getByTestId(DataTestId.DELETE_EXPENSES_LIST_FORM); + const submitButtonElem = screen.getByTestId(DataTestId.DELETE_EXPENSES_LIST_SUBMIT_BUTTON); + + return { + formElem, + submitButtonElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElems(); + await userEvent.click(submitButtonElem); +}; + +export const DeleteExpensesListFormHelper = { + expensesList, + getFormElems, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/expense/list/EditExpensesListButton.tsx b/frontend/tests/helpers/expense/list/EditExpensesListButton.tsx new file mode 100644 index 0000000..3a1e7bc --- /dev/null +++ b/frontend/tests/helpers/expense/list/EditExpensesListButton.tsx @@ -0,0 +1,60 @@ +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { EditExpenseListBody, EditExpenseListPath, EditExpenseListSuccessResponse } from '@/entity/expenses/list'; + +import { EditExpensesListButton } from '@/features/expenses/list/EditExpensesListButton'; +import { DataTestId } from '@/shared/constants'; + +const expenseList = { + createdAt: '2025-08-29T07:57:40.503Z', + id: '10cc093c-3875-4d33-a40a-df526266e262', + items: [], + title: 'Mock list', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.EDIT_EXPENSES_LIST_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.patch< + EditExpenseListPath, + EditExpenseListBody, + EditExpenseListSuccessResponse +>( + `/expenses/list/${expenseList.id}`, + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json(expenseList); + }, +); + +export const EditExpensesListButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/expense/list/EditExpensesListForm.tsx b/frontend/tests/helpers/expense/list/EditExpensesListForm.tsx new file mode 100644 index 0000000..fba0fea --- /dev/null +++ b/frontend/tests/helpers/expense/list/EditExpensesListForm.tsx @@ -0,0 +1,71 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { EditExpenseListFormValues, EditExpensesListFormProps } from '@/entity/expenses/list'; + +import { EditExpensesListForm } from '@/entity/expenses/list'; +import { DataTestId } from '@/shared/constants'; + +const expenseList = { + createdAt: '2025-08-29T07:57:40.503Z', + id: '10cc093c-3875-4d33-a40a-df526266e262', + items: [], + title: 'Mock list', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + ); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.EDIT_EXPENSES_LIST_FORM); + const titleInputElem = screen.getByLabelText(/Название/); + const submitButtonElem = screen.getByRole('button', { name: /Сохранить/ }); + + return { + formElem, + submitButtonElem, + titleInputElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +export const fillForm = async ( + data: EditExpenseListFormValues, + options: { autoSubmit?: boolean } = {}, +) => { + const { title } = data; + const { autoSubmit = true } = options; + const { titleInputElem } = getFormElements(); + + await userEvent.clear(titleInputElem); + await userEvent.type(titleInputElem, title); + + if (autoSubmit) { + await submitForm(); + } +}; + +export const EditExpensesListFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/income/item/AddIncomeItemButton.tsx b/frontend/tests/helpers/income/item/AddIncomeItemButton.tsx new file mode 100644 index 0000000..72f255a --- /dev/null +++ b/frontend/tests/helpers/income/item/AddIncomeItemButton.tsx @@ -0,0 +1,62 @@ +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { AddIncomeItemBody, AddIncomeItemPath, AddIncomeItemSuccessResponse } from '@/entity/incomes/item'; + +import { AddIncomeItemButton } from '@/features/incomes/items/AddIncomeItemButton'; +import { DataTestId } from '@/shared/constants'; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.ADD_INCOME_ITEM_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.post< + AddIncomeItemPath, + AddIncomeItemBody, + AddIncomeItemSuccessResponse +>( + '/incomes/list/123/items', + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json({ + amount: 12, + createdAt: '2025-08-29T07:57:40.503Z', + currency: 'RUB', + date: '2025-08-22T00:00:03.000Z', + description: 'Mock description', + id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', + incomeListId: '10cc093c-3875-4d33-a40a-df526266e262', + title: 'Mock item', + updatedAt: '2025-08-29T07:57:40.503Z', + }); + }, +); + +export const AddIncomeItemButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/income/item/AddIncomeItemForm.tsx b/frontend/tests/helpers/income/item/AddIncomeItemForm.tsx new file mode 100644 index 0000000..b6d3352 --- /dev/null +++ b/frontend/tests/helpers/income/item/AddIncomeItemForm.tsx @@ -0,0 +1,80 @@ +import { typeDateField, typeTextField } from '@mocks/jest/helpers'; +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { AddIncomeItemFormProps, AddIncomeItemFormValues } from '@/entity/incomes/item'; + +import { AddIncomeItemForm } from '@/entity/incomes/item'; +import { DataTestId } from '@/shared/constants'; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + { wrapper: createAppWrapper() }, + ); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.ADD_INCOME_ITEM_FORM); + const titleInputElem = screen.getByLabelText(/Название/i); + const descriptionInputElem = screen.getByLabelText(/Описание/); + const dateInputElem = screen.getAllByLabelText(/Дата/i)[1]; + const amountInputElem = screen.getByLabelText(/Сумма/i); + const submitButtonElem = screen.getByTestId(DataTestId.ADD_INCOME_ITEM_SUBMIT_BUTTON); + + return { + amountInputElem, + dateInputElem, + descriptionInputElem, + formElem, + submitButtonElem, + titleInputElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +const fillForm = async ( + data: Omit, + options: { autoSubmit?: boolean } = {}, +) => { + const { + amount, + date, + description, + title, + } = data; + const { autoSubmit = true } = options; + const { + amountInputElem, + dateInputElem, + descriptionInputElem, + titleInputElem, + } = getFormElements(); + + await typeTextField(titleInputElem, title); + await typeTextField(descriptionInputElem, description); + await typeDateField(dateInputElem, date); + await typeTextField(amountInputElem, amount.toString()); + + if (autoSubmit) { + await submitForm(); + } +}; + +export const AddIncomeItemFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/income/item/DeleteIncomeItemButton.tsx b/frontend/tests/helpers/income/item/DeleteIncomeItemButton.tsx new file mode 100644 index 0000000..42656e1 --- /dev/null +++ b/frontend/tests/helpers/income/item/DeleteIncomeItemButton.tsx @@ -0,0 +1,55 @@ +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { DeleteIncomeItemPath, IncomeListItem } from '@/entity/incomes/item'; + +import { DeleteIncomeItemButton } from '@/features/incomes/items/DeleteIncomeItemButton'; +import { DataTestId } from '@/shared/constants'; + +const iconmeItem: IncomeListItem = { + amount: 12, + createdAt: '2025-08-29T07:57:40.503Z', + currency: 'RUB', + date: '2025-08-22T00:00:03.000Z', + description: 'Mock description', + id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', + incomeListId: '10cc093c-3875-4d33-a40a-df526266e262', + title: 'Mock item', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.DELETE_INCOME_ITEM_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = http.delete( + `/incomes/list/${iconmeItem.incomeListId}/items/${iconmeItem.id}`, + () => HttpResponse.json(iconmeItem), +); + +export const DeleteIncomeItemButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/income/item/DeleteIncomeItemForm.tsx b/frontend/tests/helpers/income/item/DeleteIncomeItemForm.tsx new file mode 100644 index 0000000..7e68047 --- /dev/null +++ b/frontend/tests/helpers/income/item/DeleteIncomeItemForm.tsx @@ -0,0 +1,61 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { DeleteIncomeItemFormProps } from '@/entity/incomes/item'; + +import { DeleteIncomeItemForm } from '@/entity/incomes/item'; +import { DataTestId } from '@/shared/constants'; + +const incomeItem = { + amount: 12, + createdAt: '2025-08-29T07:57:40.503Z', + currency: 'RUB', + date: '2025-08-22T00:00:03.000Z', + description: 'Mock description', + id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', + incomeListId: '10cc093c-3875-4d33-a40a-df526266e262', + title: 'Mock item', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + ); + + const submitButtonElem = screen.getByText('Удалить'); + + return { + onSubmit, + submitButtonElem, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.DELETE_INCOME_ITEM_FORM); + const submitButtonElem = screen.getByTestId(DataTestId.DELETE_INCOME_ITEM_SUBMIT_BUTTON); + + return { + formElem, + submitButtonElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + + await userEvent.click(submitButtonElem); +}; + +export const DeleteIncomeItemFormHelper = { + getFormElements, + incomeItem, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/income/item/EditIncomeItemButton.tsx b/frontend/tests/helpers/income/item/EditIncomeItemButton.tsx new file mode 100644 index 0000000..d605caa --- /dev/null +++ b/frontend/tests/helpers/income/item/EditIncomeItemButton.tsx @@ -0,0 +1,69 @@ +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { + EditIncomeItemBody, + EditIncomeItemPath, + EditIncomeItemSuccessResponse, + IncomeListItem, +} from '@/entity/incomes/item'; + +import { EditIncomeItemButton } from '@/features/incomes/items/EditIncomeItemButton'; +import { DataTestId } from '@/shared/constants'; + +const incomeItem: IncomeListItem = { + amount: 12, + createdAt: '2025-08-29T07:57:40.503Z', + currency: 'RUB', + date: '2025-08-22T00:00:03.000Z', + description: 'Mock description', + id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', + incomeListId: '10cc093c-3875-4d33-a40a-df526266e262', + title: 'Mock item', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.EDIT_INCOME_ITEM_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.patch< + EditIncomeItemPath, + EditIncomeItemBody, + EditIncomeItemSuccessResponse +>( + `/incomes/list/${incomeItem.incomeListId}/items/${incomeItem.id}`, + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json(incomeItem); + }, +); + +export const EditIncomeItemButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/income/item/EditIncomeItemForm.tsx b/frontend/tests/helpers/income/item/EditIncomeItemForm.tsx new file mode 100644 index 0000000..dfe6fb8 --- /dev/null +++ b/frontend/tests/helpers/income/item/EditIncomeItemForm.tsx @@ -0,0 +1,97 @@ +import { typeDateField, typeTextField } from '@mocks/jest/helpers'; +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { EditIncomeItemFormProps, EditIncomeItemFormValues } from '@/entity/incomes/item'; + +import { EditIncomeItemForm } from '@/entity/incomes/item'; +import { DataTestId } from '@/shared/constants'; + +const incomeItem = { + amount: 12, + count: 12, + createdAt: '2025-08-29T07:57:40.503Z', + currency: 'RUB', + date: '2025-08-22T00:00:03.000Z', + description: 'Mock description', + id: '51a44dc0-597b-49b9-85bb-e0bcd42db3f6', + incomeListId: '10cc093c-3875-4d33-a40a-df526266e262', + title: 'Mock item', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + { wrapper: createAppWrapper() }, + ); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.EDIT_INCOME_ITEM_FORM); + const titleInputElem = screen.getByLabelText(/Название/i); + const descriptionInputElem = screen.getByLabelText(/Описание/i); + const dateInputElem = screen.getAllByLabelText(/Дата/i)[1]; + const amountInputElem = screen.getByLabelText(/Сумма/i); + const submitButtonElem = screen.getByTestId(DataTestId.EDIT_INCOME_ITEM_SUBMIT_BUTTON); + + return { + amountInputElem, + dateInputElem, + descriptionInputElem, + formElem, + submitButtonElem, + titleInputElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +const fillForm = async ( + data: Omit, + options: { autoSubmit?: boolean } = {}, +) => { + const { + amount, + date, + description, + title, + } = data; + const { autoSubmit = true } = options; + const { + amountInputElem, + dateInputElem, + descriptionInputElem, + titleInputElem, + } = getFormElements(); + + await typeTextField(titleInputElem, title); + await typeTextField(descriptionInputElem, description); + await typeDateField(dateInputElem, date); + await typeTextField(amountInputElem, amount.toString()); + + if (autoSubmit) { + await submitForm(); + } +}; + +export const EditIncomeItemFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/income/list/AddIncomeListButton.tsx b/frontend/tests/helpers/income/list/AddIncomeListButton.tsx new file mode 100644 index 0000000..77e0922 --- /dev/null +++ b/frontend/tests/helpers/income/list/AddIncomeListButton.tsx @@ -0,0 +1,58 @@ +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { AddExpenseListBody, AddExpenseListSuccessResponse } from '@/entity/expenses/list'; + +import { AddIncomeListButton } from '@/features/incomes/list/AddIncomeListButton'; +import { DataTestId } from '@/shared/constants'; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.ADD_INCOME_LIST_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.post< + never, + AddExpenseListBody, + AddExpenseListSuccessResponse +>( + '/incomes/list', + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json({ + createdAt: '2025-08-29T07:57:40.503Z', + id: '10cc093c-3875-4d33-a40a-df526266e262', + items: [], + title: 'Mock list', + updatedAt: '2025-08-29T07:57:40.503Z', + }); + }, +); + +export const AddIncomeListButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/income/list/AddIncomeListForm.tsx b/frontend/tests/helpers/income/list/AddIncomeListForm.tsx new file mode 100644 index 0000000..333bdf7 --- /dev/null +++ b/frontend/tests/helpers/income/list/AddIncomeListForm.tsx @@ -0,0 +1,57 @@ +import { typeTextField } from '@mocks/jest/helpers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { AddIncomeListFormProps, AddIncomeListFormValues } from '@/entity/incomes/list'; + +import { AddIncomeListForm } from '@/entity/incomes/list'; +import { DataTestId } from '@/shared/constants'; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render(); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.ADD_INCOME_LIST_FORM); + const titleInputElem = screen.getByLabelText(/Название/); + const submitButtonElem = screen.getByTestId(DataTestId.ADD_INCOME_LIST_SUBMIT_BUTTON); + + return { + formElem, + submitButtonElem, + titleInputElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +const fillForm = async ( + data: AddIncomeListFormValues, + options: { autoSubmit?: boolean } = {}, +) => { + const { title } = data; + const { autoSubmit = true } = options; + const { titleInputElem } = getFormElements(); + + await typeTextField(titleInputElem, title); + + if (autoSubmit) { + await submitForm(); + } +}; + +export const AddIncomeListFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/income/list/DeleteIncomeListButton.tsx b/frontend/tests/helpers/income/list/DeleteIncomeListButton.tsx new file mode 100644 index 0000000..a01a7e5 --- /dev/null +++ b/frontend/tests/helpers/income/list/DeleteIncomeListButton.tsx @@ -0,0 +1,52 @@ +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { DeleteExpenseListPath } from '@/entity/expenses/list'; +import type { IncomeList } from '@/entity/incomes/list'; + +import { DeleteIncomeListButton } from '@/features/incomes/list/DeleteIncomeListButton'; +import { DataTestId } from '@/shared/constants'; + +const incomeList: IncomeList = { + createdAt: '2025-08-29T07:57:40.503Z', + id: '10cc093c-3875-4d33-a40a-df526266e262', + items: [], + title: 'Mock list', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.DELETE_INCOME_LIST_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = http.delete( + `/incomes/list/${incomeList.id}`, + () => HttpResponse.json(incomeList), +); + +export const DeleteIncomeListButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/income/list/DeleteIncomeListForm.tsx b/frontend/tests/helpers/income/list/DeleteIncomeListForm.tsx new file mode 100644 index 0000000..035dbe9 --- /dev/null +++ b/frontend/tests/helpers/income/list/DeleteIncomeListForm.tsx @@ -0,0 +1,57 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { DeleteIncomeListFormProps } from '@/entity/incomes/list'; + +import { DeleteIncomeListForm } from '@/entity/incomes/list'; +import { DataTestId } from '@/shared/constants'; + +const incomeList = { + createdAt: '2025-08-29T07:57:40.503Z', + id: '10cc093c-3875-4d33-a40a-df526266e262', + items: [], + title: 'Mock list', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + ); + + const submitButtonElem = screen.getByText('Удалить'); + + return { + onSubmit, + submitButtonElem, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.DELETE_INCOME_LIST_FORM); + const submitButtonElem = screen.getByTestId(DataTestId.DELETE_INCOME_LIST_SUBMIT_BUTTON); + + return { + formElem, + submitButtonElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +export const DeleteIncomeListFormHelper = { + getFormElements, + incomeList, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/income/list/EditIncomeListButton.tsx b/frontend/tests/helpers/income/list/EditIncomeListButton.tsx new file mode 100644 index 0000000..9ff9765 --- /dev/null +++ b/frontend/tests/helpers/income/list/EditIncomeListButton.tsx @@ -0,0 +1,60 @@ +import { createAppWrapper } from '@mocks/jest/wrappers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse } from 'msw'; + +import type { EditExpenseListBody, EditExpenseListPath, EditExpenseListSuccessResponse } from '@/entity/expenses/list'; + +import { EditIncomeListButton } from '@/features/incomes/list/EditIncomeListButton'; +import { DataTestId } from '@/shared/constants'; + +const editingList = { + createdAt: '2025-08-29T07:57:40.503Z', + id: '10cc093c-3875-4d33-a40a-df526266e262', + items: null, + title: 'Mock list', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderButton = () => { + render( + , + { wrapper: createAppWrapper() }, + ); + + const buttonElem = screen.getByTestId(DataTestId.EDIT_INCOME_LIST_BUTTON); + + return { + buttonElem, + }; +}; + +const openModal = async () => { + const { buttonElem } = renderButton(); + await userEvent.click(buttonElem); + const modalElem = screen.getByRole('presentation'); + + return { + modalElem, + }; +}; + +const submitHandler = (requestSpy: jest.Mock) => http.patch< + EditExpenseListPath, + EditExpenseListBody, + EditExpenseListSuccessResponse +>( + `/incomes/list/${editingList.id}`, + async ({ request }) => { + const body = await request.json(); + requestSpy(body); + + return HttpResponse.json(editingList); + }, +); + +export const EditIncomeListButtonHelper = { + openModal, + renderButton, + submitHandler, +}; diff --git a/frontend/tests/helpers/income/list/EditIncomeListForm.tsx b/frontend/tests/helpers/income/list/EditIncomeListForm.tsx new file mode 100644 index 0000000..001570c --- /dev/null +++ b/frontend/tests/helpers/income/list/EditIncomeListForm.tsx @@ -0,0 +1,71 @@ +import { typeTextField } from '@mocks/jest/helpers'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import type { AddIncomeListFormValues, EditIncomeListFormProps } from '@/entity/incomes/list'; + +import { EditIncomeListForm } from '@/entity/incomes/list'; +import { DataTestId } from '@/shared/constants'; + +const editingList = { + createdAt: '2025-08-29T07:57:40.503Z', + id: '10cc093c-3875-4d33-a40a-df526266e262', + items: [], + title: 'Mock list', + updatedAt: '2025-08-29T07:57:40.503Z', +}; + +const renderForm = (props?: Partial) => { + const onSubmit = jest.fn(); + + render( + , + ); + + return { + onSubmit, + }; +}; + +const getFormElements = () => { + const formElem = screen.getByTestId(DataTestId.EDIT_INCOME_LIST_FORM); + const titleInputElem = screen.getByLabelText(/Название/); + const submitButtonElem = screen.getByTestId(DataTestId.EDIT_INCOME_LIST_SUBMIT_BUTTON); + + return { + formElem, + submitButtonElem, + titleInputElem, + }; +}; + +const submitForm = async () => { + const { submitButtonElem } = getFormElements(); + await userEvent.click(submitButtonElem); +}; + +const fillForm = async ( + data: AddIncomeListFormValues, + options: { autoSubmit?: boolean } = {}, +) => { + const { title } = data; + const { autoSubmit = true } = options; + const { titleInputElem } = getFormElements(); + + await typeTextField(titleInputElem, title); + + if (autoSubmit) { + await submitForm(); + } +}; + +export const EditIncomeListFormHelper = { + fillForm, + getFormElements, + renderForm, + submitForm, +}; diff --git a/frontend/tests/helpers/index.ts b/frontend/tests/helpers/index.ts new file mode 100644 index 0000000..82c41ff --- /dev/null +++ b/frontend/tests/helpers/index.ts @@ -0,0 +1,37 @@ +/* eslint-disable import/max-dependencies */ +export { AddBankCardButtonHelper } from './bank-card/AddBankCardButton'; +export { AddBankCardFormHelper } from './bank-card/AddBankCardForm'; +export { DeleteBankCardButtonHelper } from './bank-card/DeleteBankCardButton'; +export { DeleteBankCardFormHelper } from './bank-card/DeleteBankCardForm'; +export { EditBankCardButtonHelper } from './bank-card/EditBankCardButton'; +export { EditBankCardFormHelper } from './bank-card/EditBankCardForm'; +export { AddCashButtonHelper } from './cash/AddCashButton'; +export { AddCashFormHelper } from './cash/AddCashForm'; +export { DeleteCashButtonHelper } from './cash/DeleteCashButton'; +export { DeleteCashFormHelper } from './cash/DeleteCashForm'; +export { EditCashButtonHelper } from './cash/EditCashButton'; +export { EditCashFormHelper } from './cash/EditCashForm'; +export { AddExpenseItemButtonHelper } from './expense/item/AddExpenseItemButton'; +export { AddExpenseItemFormHelper } from './expense/item/AddExpenseItemForm'; +export { DeleteExpenseItemButtonHelper } from './expense/item/DeleteExpenseItemButton'; +export { DeleteExpenseItemFormHelper } from './expense/item/DeleteExpenseItemForm'; +export { EditExpenseItemButtonHelper } from './expense/item/EditExpenseItemButton'; +export { EditExpenseItemFormHelper } from './expense/item/EditExpenseItemForm'; +export { AddExpensesListButtonHelper } from './expense/list/AddExpensesListButton'; +export { AddExpensesListFormHelper } from './expense/list/AddExpensesListForm'; +export { DeleteExpensesListButtonHelper } from './expense/list/DeleteExpensesListButton'; +export { DeleteExpensesListFormHelper } from './expense/list/DeleteExpensesListForm'; +export { EditExpensesListButtonHelper } from './expense/list/EditExpensesListButton'; +export { EditExpensesListFormHelper } from './expense/list/EditExpensesListForm'; +export { AddIncomeItemButtonHelper } from './income/item/AddIncomeItemButton'; +export { AddIncomeItemFormHelper } from './income/item/AddIncomeItemForm'; +export { DeleteIncomeItemButtonHelper } from './income/item/DeleteIncomeItemButton'; +export { DeleteIncomeItemFormHelper } from './income/item/DeleteIncomeItemForm'; +export { EditIncomeItemButtonHelper } from './income/item/EditIncomeItemButton'; +export { EditIncomeItemFormHelper } from './income/item/EditIncomeItemForm'; +export { AddIncomeListButtonHelper } from './income/list/AddIncomeListButton'; +export { AddIncomeListFormHelper } from './income/list/AddIncomeListForm'; +export { DeleteIncomeListButtonHelper } from './income/list/DeleteIncomeListButton'; +export { DeleteIncomeListFormHelper } from './income/list/DeleteIncomeListForm'; +export { EditIncomeListButtonHelper } from './income/list/EditIncomeListButton'; +export { EditIncomeListFormHelper } from './income/list/EditIncomeListForm';