feat(frontend): add setup tests

This commit is contained in:
Sergey Krylov 2025-10-09 05:39:24 +03:00
parent 6ed6cf8782
commit 35291a103a
18 changed files with 1847 additions and 825 deletions

2
frontend/.gitignore vendored
View File

@ -14,3 +14,5 @@ dist/
.vscode/* .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
.idea .idea
coverage

View File

@ -0,0 +1,3 @@
export { selectOption } from './selectOption';
export { typeDateField } from './typeDateField';
export { typeTextField } from './typeTextField';

View File

@ -0,0 +1,16 @@
import { screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
export const selectOption = async (selectElement: HTMLElement | null, value: string) => {
if (!selectElement) {
throw new Error('Не найден элемент выпадающего списка');
}
const bankcardDropdown = within(selectElement).getByRole('combobox');
await userEvent.click(bankcardDropdown);
const option = screen.getAllByRole('option').find((el) => el.dataset.value === value);
if (!option) {
throw new Error(`Не найдена опция с value:${value}`);
}
await userEvent.click(option);
};

View File

@ -0,0 +1,17 @@
import userEvent from '@testing-library/user-event';
export const typeDateField = async (dateElement: HTMLElement | null, value?: null | string) => {
if (!dateElement) {
throw new Error('Не найдено поле ввода');
}
if (value === null) {
await userEvent.clear(dateElement);
}
if (value) {
await userEvent.click(dateElement);
await userEvent.clear(dateElement);
await userEvent.type(dateElement, value);
}
};

View File

@ -0,0 +1,16 @@
import userEvent from '@testing-library/user-event';
export const typeTextField = async (inputElement: HTMLElement | null, value?: null | string) => {
if (!inputElement) {
throw new Error('Не найдено поле ввода');
}
if (value === null) {
await userEvent.clear(inputElement);
}
if (value) {
await userEvent.clear(inputElement);
await userEvent.type(inputElement, value);
}
};

View File

@ -0,0 +1,17 @@
import type { PropsWithChildren } from 'react';
import { createLocalizationWrapper } from './LocalizationWrapper';
import { createReactQueryWrapper } from './ReactQueryWrapper';
export const createAppWrapper = () => ({ children }: PropsWithChildren) => {
const ReactQueryWrapper = createReactQueryWrapper();
const LocalizationWrapper = createLocalizationWrapper();
return (
<ReactQueryWrapper>
<LocalizationWrapper>
{children}
</LocalizationWrapper>
</ReactQueryWrapper>
);
};

View File

@ -0,0 +1,11 @@
import type { PropsWithChildren } from 'react';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { ru } from 'date-fns/locale/ru';
export const createLocalizationWrapper = () => ({ children }: PropsWithChildren) => (
<LocalizationProvider adapterLocale={ru} dateAdapter={AdapterDateFns}>
{children}
</LocalizationProvider>
);

View File

@ -1 +1,3 @@
export { createAppWrapper } from './AppWrapper';
export { createLocalizationWrapper } from './LocalizationWrapper';
export { createReactQueryWrapper } from './ReactQueryWrapper'; export { createReactQueryWrapper } from './ReactQueryWrapper';

View File

@ -0,0 +1 @@
process.env.TZ = 'Europe/Moscow';

View File

@ -1,6 +1,8 @@
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import { server } from '@mocks/jest/server'; import { server } from '@mocks/jest/server';
import './setupEnv';
beforeAll(() => { beforeAll(() => {
server.listen(); server.listen();
}); });

View File

@ -4,6 +4,7 @@ export default {
moduleNameMapper: { moduleNameMapper: {
'@/(.*)': '<rootDir>/src/$1', '@/(.*)': '<rootDir>/src/$1',
'@mocks/(.*)': '<rootDir>/__mocks__/$1', '@mocks/(.*)': '<rootDir>/__mocks__/$1',
'@tests/(.*)': '<rootDir>/tests/$1',
'\\.(css|scss|sass|less)$': 'identity-obj-proxy', '\\.(css|scss|sass|less)$': 'identity-obj-proxy',
'\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/jest/fileMock.js', '\\.(jpg|jpeg|png|gif|webp|svg)$': '<rootDir>/__mocks__/jest/fileMock.js',
}, },
@ -20,4 +21,5 @@ export default {
}, },
}], }],
}, },
transformIgnorePatterns: ['node_modules/(?!(until-async)/)'],
} as Config; } as Config;

File diff suppressed because it is too large Load Diff

View File

@ -9,42 +9,50 @@
"dev:mock": "rsbuild dev --env-mode mock", "dev:mock": "rsbuild dev --env-mode mock",
"preview": "rsbuild preview", "preview": "rsbuild preview",
"lint": "eslint --concurrency=auto", "lint": "eslint --concurrency=auto",
"test:unit": "jest", "test:unit": "jest --verbose --silent",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"openapi:gen": "openapi-typescript http://localhost:3000/docs/swagger.json -o src/shared/api/schema.ts" "openapi:gen": "openapi-typescript http://localhost:3000/docs/swagger.json -o src/shared/api/schema.ts"
}, },
"dependencies": { "dependencies": {
"@tanstack/react-query": "5.89.0", "@emotion/react": "11.14.0",
"@emotion/styled": "11.14.1",
"@fontsource/roboto": "5.2.8",
"@mui/icons-material": "7.3.4",
"@mui/material": "7.3.4",
"@mui/x-date-pickers": "8.12.0",
"@tanstack/react-query": "5.90.2",
"axios": "1.12.2", "axios": "1.12.2",
"react": "19.1.1", "date-fns": "4.1.0",
"react-dom": "19.1.1", "date-fns-tz": "3.2.0",
"react-router": "7.9.1" "react": "19.2.0",
"react-dom": "19.2.0",
"react-router": "7.9.4"
}, },
"devDependencies": { "devDependencies": {
"@rsbuild/core": "1.5.7", "@rsbuild/core": "1.5.14",
"@rsbuild/plugin-eslint": "1.1.2", "@rsbuild/plugin-eslint": "1.1.2",
"@rsbuild/plugin-react": "1.4.0", "@rsbuild/plugin-react": "1.4.1",
"@rsbuild/plugin-type-check": "1.2.4", "@rsbuild/plugin-type-check": "1.2.4",
"@swc/core": "1.13.5", "@swc/core": "1.13.5",
"@swc/jest": "0.2.39", "@swc/jest": "0.2.39",
"@tanstack/eslint-plugin-query": "5.89.0", "@tanstack/eslint-plugin-query": "5.91.0",
"@testing-library/dom": "10.4.1", "@testing-library/dom": "10.4.1",
"@testing-library/jest-dom": "6.8.0", "@testing-library/jest-dom": "6.9.1",
"@testing-library/react": "16.3.0", "@testing-library/react": "16.3.0",
"@testing-library/user-event": "14.6.1", "@testing-library/user-event": "14.6.1",
"@types/jest": "30.0.0", "@types/jest": "30.0.0",
"@types/react": "19.1.13", "@types/react": "19.2.2",
"@types/react-dom": "19.1.9", "@types/react-dom": "19.2.1",
"eslint": "9.35.0", "eslint": "9.37.0",
"eslint-config-ksv741": "0.3.0", "eslint-config-ksv741": "0.3.0",
"eslint-plugin-perfectionist": "^4.15.0", "eslint-plugin-perfectionist": "4.15.1",
"identity-obj-proxy": "3.0.0", "identity-obj-proxy": "3.0.0",
"jest": "30.1.3", "jest": "30.2.0",
"jest-environment-jsdom": "30.1.2", "jest-environment-jsdom": "30.2.0",
"msw": "2.11.2", "msw": "2.11.3",
"openapi-typescript": "7.9.1", "openapi-typescript": "7.9.1",
"ts-node": "10.9.2", "ts-node": "10.9.2",
"typescript": "5.9.2" "typescript": "5.9.3"
}, },
"msw": { "msw": {
"workerDirectory": [ "workerDirectory": [

View File

@ -7,7 +7,7 @@
* - Please do NOT modify this file. * - Please do NOT modify this file.
*/ */
const PACKAGE_VERSION = '2.11.2' const PACKAGE_VERSION = '2.11.3'
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set() const activeClientIds = new Set()

View File

@ -12,6 +12,8 @@ export default defineConfig({
pluginEslint({ pluginEslint({
eslintPluginOptions: { eslintPluginOptions: {
configType: 'flat', configType: 'flat',
emitError: false,
emitWarning: false,
failOnError: false, failOnError: false,
failOnWarning: false, failOnWarning: false,
}, },

View File

@ -0,0 +1 @@
export { DataTestId } from './tests/data-testids';

View File

@ -0,0 +1,57 @@
// eslint-disable-next-line @typescript-eslint/naming-convention
export enum DataTestId {
ADD_BANK_CARD_BUTTON = 'add-bank-card-button',
ADD_BANK_CARD_FORM = 'add-bank-card-form',
ADD_BANK_CARD_SUBMIT_BUTTON = 'add-bank-card-submit-button',
ADD_CASH_BUTTON = 'add-cash-button',
ADD_CASH_FORM = 'add-cash-form',
ADD_CASH_SUBMIT_BUTTON = 'add-cash-submit-button',
ADD_EXPENSE_ITEM_BUTTON = 'add-expense-item-button',
ADD_EXPENSE_ITEM_FORM = 'add-expense-item-form',
ADD_EXPENSE_ITEM_SUBMIT_BUTTON = 'add-expense-item-submit-button',
ADD_EXPENSES_LIST_BUTTON = 'add-expenses-list-button',
ADD_EXPENSES_LIST_FORM = 'add-expenses-list-form',
ADD_EXPENSES_LIST_SUBMIT_BUTTON = 'add-expenses-list-submit-button',
ADD_INCOME_ITEM_BUTTON = 'add-income-item-button',
ADD_INCOME_ITEM_FORM = 'add-income-item-form',
ADD_INCOME_ITEM_SUBMIT_BUTTON = 'add-income-item-submit-button',
ADD_INCOME_LIST_BUTTON = 'add-income-list-button',
ADD_INCOME_LIST_FORM = 'add-income-list-form',
ADD_INCOME_LIST_SUBMIT_BUTTON = 'add-income-list-submit-button',
DELETE_BANK_CARD_BUTTON = 'delete-bank-card-button',
DELETE_BANK_CARD_FORM = 'delete-bank-card-form',
DELETE_BANK_CARD_SUBMIT_BUTTON = 'delete-bank-card-submit-button',
DELETE_CASH_BUTTON = 'delete-cash-button',
DELETE_CASH_FORM = 'delete-cash-form',
DELETE_CASH_SUBMIT_BUTTON = 'delete-cash-submit-button',
DELETE_EXPENSE_ITEM_BUTTON = 'delete-expense-item-button',
DELETE_EXPENSE_ITEM_FORM = 'delete-expense-item-form',
DELETE_EXPENSE_ITEM_SUBMIT_BUTTON = 'delete-expense-item-submit-button',
DELETE_EXPENSES_LIST_BUTTON = 'delete-expenses-list-button',
DELETE_EXPENSES_LIST_FORM = 'delete-expenses-list-form',
DELETE_EXPENSES_LIST_SUBMIT_BUTTON = 'delete-expenses-list-submit-button',
DELETE_INCOME_ITEM_BUTTON = 'delete-income-item-button',
DELETE_INCOME_ITEM_FORM = 'delete-income-item-form',
DELETE_INCOME_ITEM_SUBMIT_BUTTON = 'delete-income-item-submit-button',
DELETE_INCOME_LIST_BUTTON = 'delete-income-list-button',
DELETE_INCOME_LIST_FORM = 'delete-income-list-form',
DELETE_INCOME_LIST_SUBMIT_BUTTON = 'delete-income-list-submit-button',
EDIT_BANK_CARD_BUTTON = 'edit-bank-card-button',
EDIT_BANK_CARD_FORM = 'edit-bank-card-form',
EDIT_BANK_CARD_SUBMIT_BUTTON = 'edit-bank-card-submit-button',
EDIT_CASH_BUTTON = 'edit-cash-button',
EDIT_CASH_FORM = 'edit-cash-form',
EDIT_CASH_SUBMIT_BUTTON = 'edit-cash-submit-button',
EDIT_EXPENSE_ITEM_BUTTON = 'edit-expense-item-button',
EDIT_EXPENSE_ITEM_FORM = 'edit-expense-item-form',
EDIT_EXPENSE_ITEM_SUBMIT_BUTTON = 'edit-expense-item-submit-button',
EDIT_EXPENSES_LIST_BUTTON = 'edit-expenses-list-button',
EDIT_EXPENSES_LIST_FORM = 'edit-expenses-list-form',
EDIT_EXPENSES_LIST_SUBMIT_BUTTON = 'edit-expenses-list-submit-button',
EDIT_INCOME_ITEM_BUTTON = 'edit-income-item-button',
EDIT_INCOME_ITEM_FORM = 'edit-income-item-form',
EDIT_INCOME_ITEM_SUBMIT_BUTTON = 'edit-income-item-submit-button',
EDIT_INCOME_LIST_BUTTON = 'edit-income-list-button',
EDIT_INCOME_LIST_FORM = 'edit-income-list-form',
EDIT_INCOME_LIST_SUBMIT_BUTTON = 'edit-income-list-submit-button',
}

View File

@ -22,13 +22,15 @@
"noUnusedParameters": true, "noUnusedParameters": true,
"paths": { "paths": {
"@/*": ["./src/*"], "@/*": ["./src/*"],
"@mocks/*": ["./__mocks__/*"] "@mocks/*": ["./__mocks__/*"],
"@tests/*": ["./tests/*"],
}, },
"types": ["jest", "@testing-library/jest-dom"] "types": ["jest", "@testing-library/jest-dom"]
}, },
"include": [ "include": [
"src", "src",
"__mocks__", "__mocks__",
"tests",
"configs", "configs",
"eslint.config.ts", "eslint.config.ts",
"rsbuild.config.ts", "rsbuild.config.ts",