From 8d62615c38e8448edc2491601b9c66b72ccffa56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20=D0=9F=D0=B0=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Mon, 4 Dec 2023 20:11:19 +0700 Subject: [PATCH] add test add env --- .eslintrc.js | 1 + config/build/buildPlugins.ts | 1 + config/build/types/config.ts | 1 + config/jest/jest.config.ts | 1 + config/storybook/webpack.config.ts | 1 + src/app/types/global.d.ts | 2 + .../fetchProfileData/fetchProfileData.test.ts | 37 +++++++++ .../fetchProfileData/fetchProfileData.ts | 4 + .../updateProfileData.test.ts | 76 +++++++++++++++++++ .../updateProfileData/updateProfileData.ts | 4 + .../validateProfileData.test.ts | 49 ++++++++++++ src/shared/const/isFrontend.ts | 1 + src/shared/const/isJest.ts | 1 + src/shared/const/isStorybook.ts | 1 + .../lib/tests/TestAsyncFunc/TestAsyncFunc.ts | 9 ++- webpack.config.ts | 1 + 16 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 src/entities/Profile/model/services/fetchProfileData/fetchProfileData.test.ts create mode 100644 src/entities/Profile/model/services/updateProfileData/updateProfileData.test.ts create mode 100644 src/entities/Profile/model/services/validateProfileData/validateProfileData.test.ts create mode 100644 src/shared/const/isFrontend.ts create mode 100644 src/shared/const/isJest.ts create mode 100644 src/shared/const/isStorybook.ts diff --git a/.eslintrc.js b/.eslintrc.js index c35c7a4..3d547f5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -92,6 +92,7 @@ module.exports = { globals: { __IS_DEV__: true, __API__: true, + __PROJECT__: true, }, overrides: [ { diff --git a/config/build/buildPlugins.ts b/config/build/buildPlugins.ts index 6ae1569..5ddda93 100644 --- a/config/build/buildPlugins.ts +++ b/config/build/buildPlugins.ts @@ -18,6 +18,7 @@ export const buildPlugins = (options: BuildOptions): webpack.WebpackPluginInstan new webpack.DefinePlugin({ __IS_DEV__: JSON.stringify(options.isDev), __API__: JSON.stringify(options.apiUrl), + __PROJECT___: JSON.stringify(options.project), }), new ReactRefreshWebpackPlugin(), ]; diff --git a/config/build/types/config.ts b/config/build/types/config.ts index 2efd7b3..ec749f5 100644 --- a/config/build/types/config.ts +++ b/config/build/types/config.ts @@ -21,4 +21,5 @@ export interface BuildOptions { isDev: BuildIsDev, port: BuildPort, apiUrl: string, + project: 'storybook' | 'frontend' | 'jest'; } diff --git a/config/jest/jest.config.ts b/config/jest/jest.config.ts index fc7f696..5e9bd97 100644 --- a/config/jest/jest.config.ts +++ b/config/jest/jest.config.ts @@ -67,6 +67,7 @@ export default { globals: { __IS_DEV__: true, __API__: '', + __PROJECT__: 'jest', }, // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. diff --git a/config/storybook/webpack.config.ts b/config/storybook/webpack.config.ts index 0d80dae..6c0285d 100644 --- a/config/storybook/webpack.config.ts +++ b/config/storybook/webpack.config.ts @@ -32,6 +32,7 @@ export default ({ config }: { config: webpack.Configuration }) => { config.plugins.push(new DefinePlugin({ __IS_DEV__: JSON.stringify(true), __API__: JSON.stringify(''), + __PROJECT__: JSON.stringify('storybook'), })); return config; diff --git a/src/app/types/global.d.ts b/src/app/types/global.d.ts index dd99b9c..4b1ed6f 100644 --- a/src/app/types/global.d.ts +++ b/src/app/types/global.d.ts @@ -17,3 +17,5 @@ declare module '*.svg' { declare const __IS_DEV__: boolean; // eslint-disable-next-line declare const __API__: string; +// eslint-disable-next-line +declare const __PROJECT__: string; diff --git a/src/entities/Profile/model/services/fetchProfileData/fetchProfileData.test.ts b/src/entities/Profile/model/services/fetchProfileData/fetchProfileData.test.ts new file mode 100644 index 0000000..b9671e4 --- /dev/null +++ b/src/entities/Profile/model/services/fetchProfileData/fetchProfileData.test.ts @@ -0,0 +1,37 @@ +import { TestAsyncFunc } from 'shared/lib/tests/TestAsyncFunc/TestAsyncFunc'; +import { fetchProfileData } from './fetchProfileData'; +import { Profile } from '../../types/profile'; + +jest.mock('axios'); + +const profileData: Profile = { + age: 99, + city: 'Any city', + country: 'Any country', + email: 'Any email', + firstName: 'Any firstName', + secondName: 'Any secondName', + username: 'username', +}; + +describe('fetchProfileData.test', () => { + test('success', async () => { + const thunk = new TestAsyncFunc(fetchProfileData); + thunk.api.get.mockReturnValue(Promise.resolve({ data: profileData })); + + const result = await thunk.callThunc(); + + expect(thunk.api.get).toHaveBeenCalled(); + expect(result.meta.requestStatus).toBe('fulfilled'); + expect(result.payload).toBe(profileData); + }); + + test('error', async () => { + const thunk = new TestAsyncFunc(fetchProfileData); + thunk.api.get.mockReturnValue(Promise.resolve({ status: 403 })); + + const result = await thunk.callThunc(); + + expect(result.meta.requestStatus).toBe('rejected'); + }); +}); diff --git a/src/entities/Profile/model/services/fetchProfileData/fetchProfileData.ts b/src/entities/Profile/model/services/fetchProfileData/fetchProfileData.ts index 33f96d9..969a40c 100644 --- a/src/entities/Profile/model/services/fetchProfileData/fetchProfileData.ts +++ b/src/entities/Profile/model/services/fetchProfileData/fetchProfileData.ts @@ -14,6 +14,10 @@ ThunkConfig try { const response = await extra.api.get('/profile'); + if (!response.data) { + throw new Error(); + } + return response.data; } catch (e) { return rejectWithValue('error'); diff --git a/src/entities/Profile/model/services/updateProfileData/updateProfileData.test.ts b/src/entities/Profile/model/services/updateProfileData/updateProfileData.test.ts new file mode 100644 index 0000000..7be19a5 --- /dev/null +++ b/src/entities/Profile/model/services/updateProfileData/updateProfileData.test.ts @@ -0,0 +1,76 @@ +import { TestAsyncFunc } from 'shared/lib/tests/TestAsyncFunc/TestAsyncFunc'; +import { updateProfileData } from './updateProfileData'; +import { Profile, ValidateProfileError } from '../../types/profile'; + +jest.mock('axios'); + +const profileData: Profile = { + age: 99, + city: 'Any city', + country: 'Any country', + email: 'Any email', + firstName: 'Any firstName', + secondName: 'Any secondName', + username: 'username', +}; + +describe('fetchProfileData.test', () => { + test('success', async () => { + const thunk = new TestAsyncFunc( + updateProfileData, + { + profile: { + form: profileData, + }, + }, + ); + thunk.api.put.mockReturnValue(Promise.resolve({ data: profileData })); + + const result = await thunk.callThunc(); + + expect(thunk.api.put).toHaveBeenCalled(); + expect(result.meta.requestStatus).toBe('fulfilled'); + expect(result.payload).toBe(profileData); + }); + + test('error', async () => { + const thunk = new TestAsyncFunc( + updateProfileData, + { + profile: { + form: profileData, + }, + }, + ); + thunk.api.put.mockReturnValue(Promise.resolve({ status: 403 })); + + const result = await thunk.callThunc(); + + expect(result.meta.requestStatus).toBe('rejected'); + expect(result.payload).toEqual([ + ValidateProfileError.SERVER_ERROR, + ]); + }); + + test('validate error', async () => { + const thunk = new TestAsyncFunc( + updateProfileData, + { + profile: { + form: { + ...profileData, + age: undefined, + }, + }, + }, + ); + thunk.api.put.mockReturnValue(Promise.resolve({ status: 403 })); + + const result = await thunk.callThunc(); + + expect(result.meta.requestStatus).toBe('rejected'); + expect(result.payload).toEqual([ + ValidateProfileError.INCORRECT_AGE, + ]); + }); +}); diff --git a/src/entities/Profile/model/services/updateProfileData/updateProfileData.ts b/src/entities/Profile/model/services/updateProfileData/updateProfileData.ts index ccd8f39..566207c 100644 --- a/src/entities/Profile/model/services/updateProfileData/updateProfileData.ts +++ b/src/entities/Profile/model/services/updateProfileData/updateProfileData.ts @@ -24,6 +24,10 @@ ThunkConfig try { const response = await extra.api.put('/profile', formData); + if (!response.data) { + throw new Error(); + } + return response.data; } catch (e) { return rejectWithValue([ValidateProfileError.SERVER_ERROR]); diff --git a/src/entities/Profile/model/services/validateProfileData/validateProfileData.test.ts b/src/entities/Profile/model/services/validateProfileData/validateProfileData.test.ts new file mode 100644 index 0000000..d77fbfa --- /dev/null +++ b/src/entities/Profile/model/services/validateProfileData/validateProfileData.test.ts @@ -0,0 +1,49 @@ +import { validateProfileData } from './validateProfileData'; +import { Profile, ValidateProfileError } from '../../types/profile'; + +jest.mock('axios'); + +const profileData: Profile = { + age: 99, + city: 'Any city', + country: 'Any country', + email: 'Any email', + firstName: 'Any firstName', + secondName: 'Any secondName', + username: 'username', +}; + +describe('validateProfileData.test', () => { + test('success', async () => { + const result = validateProfileData(profileData); + + expect(result).toEqual([]); + }); + + test('without secondname', async () => { + const result = validateProfileData({ + ...profileData, + secondName: '', + }); + + expect(result).toEqual([ValidateProfileError.INCORRECT_USER_DATA]); + }); + + test('without age', async () => { + const result = validateProfileData({ + ...profileData, + age: undefined, + }); + + expect(result).toEqual([ValidateProfileError.INCORRECT_AGE]); + }); + + test('with nothing', async () => { + const result = validateProfileData({}); + expect(result).toEqual([ + ValidateProfileError.INCORRECT_USER_DATA, + ValidateProfileError.INCORRECT_COUNTRY, + ValidateProfileError.INCORRECT_AGE, + ]); + }); +}); diff --git a/src/shared/const/isFrontend.ts b/src/shared/const/isFrontend.ts new file mode 100644 index 0000000..f2d85ba --- /dev/null +++ b/src/shared/const/isFrontend.ts @@ -0,0 +1 @@ +export const isFrontend = __PROJECT__ === 'frontend'; diff --git a/src/shared/const/isJest.ts b/src/shared/const/isJest.ts new file mode 100644 index 0000000..19b12f7 --- /dev/null +++ b/src/shared/const/isJest.ts @@ -0,0 +1 @@ +export const isJest = __PROJECT__ === 'jest'; diff --git a/src/shared/const/isStorybook.ts b/src/shared/const/isStorybook.ts new file mode 100644 index 0000000..4d2d527 --- /dev/null +++ b/src/shared/const/isStorybook.ts @@ -0,0 +1 @@ +export const isStorybook = __PROJECT__ === 'storybook'; diff --git a/src/shared/lib/tests/TestAsyncFunc/TestAsyncFunc.ts b/src/shared/lib/tests/TestAsyncFunc/TestAsyncFunc.ts index 904a997..5c73924 100644 --- a/src/shared/lib/tests/TestAsyncFunc/TestAsyncFunc.ts +++ b/src/shared/lib/tests/TestAsyncFunc/TestAsyncFunc.ts @@ -1,4 +1,4 @@ -import { AsyncThunkAction } from '@reduxjs/toolkit'; +import { AsyncThunkAction, DeepPartial } from '@reduxjs/toolkit'; import { StateSchema } from 'app/providers/StoreProvider'; import axios, { AxiosStatic } from 'axios'; @@ -23,10 +23,13 @@ export class TestAsyncFunc { navigate: jest.MockedFn; - constructor(actionCreator: ActionCreatorType) { + constructor( + actionCreator: ActionCreatorType, + state?: DeepPartial, + ) { this.actionCreator = actionCreator; this.dispatch = jest.fn(); - this.getState = jest.fn(); + this.getState = jest.fn(() => state as StateSchema); this.navigate = jest.fn(); this.api = mockedAxios; diff --git a/webpack.config.ts b/webpack.config.ts index 50d24c9..b595a4a 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -22,6 +22,7 @@ export default (env: BuildEnv) => { isDev, port, apiUrl, + project: 'frontend', }); return config;