From 19c0ef28821ffe782a7be00392ed0183111863ef Mon Sep 17 00:00:00 2001 From: grinikita Date: Sat, 18 Jan 2025 12:24:04 +0300 Subject: [PATCH] jest mocking --- jest.config.js | 9 ++--- package.json | 1 - src/__tests__/mocks/api/list/get-list.ts | 18 ++++++++++ src/__tests__/mocks/brojs-cli.ts | 25 ++++++++++++++ src/__tests__/setup.ts | 4 ++- src/__tests__/test-wrapper.tsx | 7 ++++ .../heading/__tests__/index.test.tsx | 2 +- src/container/list/__tests__/index.test.tsx | 33 +++++++++++++++++++ src/container/main/index.tsx | 4 ++- src/store/index.ts | 30 +++++++++-------- tsconfig.json | 11 +++++-- 11 files changed, 118 insertions(+), 26 deletions(-) create mode 100644 src/__tests__/mocks/api/list/get-list.ts create mode 100644 src/__tests__/mocks/brojs-cli.ts create mode 100644 src/__tests__/test-wrapper.tsx create mode 100644 src/container/list/__tests__/index.test.tsx diff --git a/jest.config.js b/jest.config.js index 33773e7..f821715 100644 --- a/jest.config.js +++ b/jest.config.js @@ -21,7 +21,7 @@ const config = { collectCoverage: true, // An array of glob patterns indicating a set of files for which coverage information should be collected - collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}'], + collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!**/__tests__/**/*'], // The directory where Jest should output its coverage files coverageDirectory: 'coverage', @@ -152,7 +152,7 @@ const config = { // testLocationInResults: false, // The glob patterns Jest uses to detect test files - testMatch: ['**/?(*.)+(test).[tj]s?(x)'] + testMatch: ['**/?(*.)+(test).[tj]s?(x)'], // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped // testPathIgnorePatterns: [ @@ -172,10 +172,7 @@ const config = { // transform: undefined, // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "\\\\node_modules\\\\", - // "\\.pnp\\.[^\\\\]+$" - // ], + transformIgnorePatterns: ['\\\\node_modules\\\\(?!(@brojs)/)', '\\.pnp\\.[^\\\\]+$'] // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them // unmockedModulePathPatterns: undefined, diff --git a/package.json b/package.json index e0369b7..3d59ecd 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.1.0", - "@types/jest": "^29.5.14", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", "@types/webpack-env": "^1.18.5", diff --git a/src/__tests__/mocks/api/list/get-list.ts b/src/__tests__/mocks/api/list/get-list.ts new file mode 100644 index 0000000..20365da --- /dev/null +++ b/src/__tests__/mocks/api/list/get-list.ts @@ -0,0 +1,18 @@ +import { jest } from '@jest/globals'; +import { listService } from '../../../../service/list'; +import { GetListResponse } from '../../../../service/list/types'; + +export const spyedGetList = jest.spyOn(listService, 'getList'); + +export const mockGetList = (data?: GetListResponse) => { + spyedGetList.mockResolvedValueOnce( + data ?? [ + { id: 1, title: 'title', description: 'description' }, + { id: 2, title: 'title', description: 'description' }, + { id: 3, title: 'title', description: 'description' }, + { id: 4, title: 'title', description: 'description' }, + { id: 5, title: 'title', description: 'description' } + ] + ); + return spyedGetList; +}; diff --git a/src/__tests__/mocks/brojs-cli.ts b/src/__tests__/mocks/brojs-cli.ts new file mode 100644 index 0000000..ff7078b --- /dev/null +++ b/src/__tests__/mocks/brojs-cli.ts @@ -0,0 +1,25 @@ +import { jest } from '@jest/globals'; + +jest.mock('@brojs/cli', () => { + global.System = { + get: () => ({ + getConfig: jest.fn(), + getConfigValue: jest.fn(), + getNavigations: jest.fn(), + getNavigationsValue: jest.fn(), + getAllFeatures: jest.fn(), + getFeatures: jest.fn(), + getHistory: jest.fn(), + getNavigation: jest.fn(), + getNavigationValue: jest.fn() + }) + }; + const originalBroJsCli = jest.requireActual('@brojs/cli'); + + return { + ...originalBroJsCli, + getConfigValue: () => { + return 'mocked_value'; + } + }; +}); diff --git a/src/__tests__/setup.ts b/src/__tests__/setup.ts index 7b0828b..e1d8763 100644 --- a/src/__tests__/setup.ts +++ b/src/__tests__/setup.ts @@ -1 +1,3 @@ -import '@testing-library/jest-dom'; +import '@testing-library/jest-dom/jest-globals'; + +import './mocks/brojs-cli'; diff --git a/src/__tests__/test-wrapper.tsx b/src/__tests__/test-wrapper.tsx new file mode 100644 index 0000000..9c02606 --- /dev/null +++ b/src/__tests__/test-wrapper.tsx @@ -0,0 +1,7 @@ +import React from 'react'; +import { setupStore } from '../store'; +import { Provider } from 'react-redux'; + +export const TestWrapper = ({ children }: { children: React.ReactNode }) => { + return {children}; +}; diff --git a/src/components/heading/__tests__/index.test.tsx b/src/components/heading/__tests__/index.test.tsx index 30e6d13..8118a6c 100644 --- a/src/components/heading/__tests__/index.test.tsx +++ b/src/components/heading/__tests__/index.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import { describe, test } from '@jest/globals'; +import { describe, test, expect } from '@jest/globals'; import Heading from '../index'; import { HeadingVariant } from '../types'; diff --git a/src/container/list/__tests__/index.test.tsx b/src/container/list/__tests__/index.test.tsx new file mode 100644 index 0000000..2c9f38e --- /dev/null +++ b/src/container/list/__tests__/index.test.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { describe, expect, test } from '@jest/globals'; +import { render, screen } from '@testing-library/react'; +import ListPage from '../index'; +import { TestWrapper } from '../../../__tests__/test-wrapper'; +import { mockGetList, spyedGetList } from '../../../__tests__/mocks/api/list/get-list'; + +describe('ListPage', () => { + test('renders', async () => { + const mockedGetList = mockGetList(); + + render(, { + wrapper: TestWrapper + }); + + expect(screen.getByText('List Page New')).toBeInTheDocument(); + + expect(await screen.findByText('1: title - description')).toBeInTheDocument(); + expect(mockedGetList).toHaveBeenCalled(); + }); + + test('Отображается ошибка', async () => { + spyedGetList.mockRejectedValueOnce({ + message: 'В доступе отказано' + }); + + render(, { + wrapper: TestWrapper + }); + + expect(await screen.findByText('Произошла ошибка')).toBeInTheDocument(); + }); +}); diff --git a/src/container/main/index.tsx b/src/container/main/index.tsx index fede5a8..fd8729a 100644 --- a/src/container/main/index.tsx +++ b/src/container/main/index.tsx @@ -1,10 +1,12 @@ import React from 'react'; import { RouterProvider } from 'react-router-dom'; import { router } from './router'; -import { store } from '../../store'; +import { setupStore } from '../../store'; import { Provider } from 'react-redux'; import { useKeycloak } from './keycloak'; +const store = setupStore(); + const Main = (): React.ReactElement | string => { const { isLoading } = useKeycloak(); diff --git a/src/store/index.ts b/src/store/index.ts index 8a3c187..6cb2c72 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,20 +1,24 @@ -import { configureStore } from '@reduxjs/toolkit'; +import { combineReducers, configureStore } from '@reduxjs/toolkit'; import { api } from './api'; import { setupListeners } from '@reduxjs/toolkit/query'; -export const store = configureStore({ - reducer: { - // Add the generated reducer as a specific top-level slice - [api.reducerPath]: api.reducer - }, - // Adding the api middleware enables caching, invalidation, polling, - // and other useful features of `rtk-query`. - middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(api.middleware) +const rootReducer = combineReducers({ + [api.reducerPath]: api.reducer }); -setupListeners(store.dispatch); +export function setupStore(preloadedState?: Partial) { + const store = configureStore({ + reducer: rootReducer, + preloadedState: preloadedState, + // Adding the api middleware enables caching, invalidation, polling, + // and other useful features of `rtk-query`. + middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(api.middleware) + }); + setupListeners(store.dispatch); + return store; +} // Infer the `RootState` and `AppDispatch` types from the store itself -export type RootState = ReturnType; -// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} -export type AppDispatch = typeof store.dispatch; +export type RootState = ReturnType; +export type AppStore = ReturnType; +export type AppDispatch = AppStore['dispatch']; diff --git a/tsconfig.json b/tsconfig.json index b88df44..835bbe8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "es2017" ], "outDir": "./dist/", + "skipLibCheck": true, "sourceMap": true, "esModuleInterop": true, "noImplicitAny": false, @@ -12,13 +13,17 @@ "moduleResolution": "Bundler", "target": "es6", "jsx": "react", - "typeRoots": ["node_modules/@types", "src/typings"], + "typeRoots": ["node_modules/@types", "@types"], "resolveJsonModule": true }, + "include": [ + "src", + "@types/**/*" + ], "exclude": [ "node_modules", - "**/*.test.ts", - "**/*.test.tsx", +// "**/*.test.ts", +// "**/*.test.tsx", "node_modules/@types/jest" ] } \ No newline at end of file