diff --git a/package-lock.json b/package-lock.json
index f2143866..6011ae37 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -40,6 +40,7 @@
"@swc/core": "^1.15.11",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^12.1.3",
+ "@testing-library/user-event": "^14.6.1",
"@testplane/storybook": "^1.7.4",
"@testplane/testing-library": "^1.0.2",
"@types/jest": "^30.0.0",
@@ -13533,6 +13534,23 @@
"ts-dedent": "^2.2.0"
}
},
+ "node_modules/@storybook/testing-library/node_modules/@testing-library/user-event": {
+ "version": "13.5.0",
+ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz",
+ "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.12.5"
+ },
+ "engines": {
+ "node": ">=10",
+ "npm": ">=6"
+ },
+ "peerDependencies": {
+ "@testing-library/dom": ">=7.21.4"
+ }
+ },
"node_modules/@storybook/theming": {
"version": "6.5.16",
"resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.5.16.tgz",
@@ -14218,15 +14236,13 @@
}
},
"node_modules/@testing-library/user-event": {
- "version": "13.5.0",
- "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz",
- "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==",
+ "version": "14.6.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
+ "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
"dev": true,
- "dependencies": {
- "@babel/runtime": "^7.12.5"
- },
+ "license": "MIT",
"engines": {
- "node": ">=10",
+ "node": ">=12",
"npm": ">=6"
},
"peerDependencies": {
@@ -38346,21 +38362,6 @@
}
}
},
- "node_modules/storybook/node_modules/@testing-library/user-event": {
- "version": "14.6.1",
- "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
- "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">=12",
- "npm": ">=6"
- },
- "peerDependencies": {
- "@testing-library/dom": ">=7.21.4"
- }
- },
"node_modules/storybook/node_modules/@vitest/spy": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
@@ -52998,6 +52999,17 @@
"@testing-library/dom": "^8.3.0",
"@testing-library/user-event": "^13.2.1",
"ts-dedent": "^2.2.0"
+ },
+ "dependencies": {
+ "@testing-library/user-event": {
+ "version": "13.5.0",
+ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz",
+ "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.12.5"
+ }
+ }
}
},
"@storybook/theming": {
@@ -53399,13 +53411,11 @@
}
},
"@testing-library/user-event": {
- "version": "13.5.0",
- "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz",
- "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==",
+ "version": "14.6.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
+ "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
"dev": true,
- "requires": {
- "@babel/runtime": "^7.12.5"
- }
+ "requires": {}
},
"@testplane/devtools": {
"version": "8.32.5",
@@ -71266,14 +71276,6 @@
"ws": "^8.18.0"
},
"dependencies": {
- "@testing-library/user-event": {
- "version": "14.6.1",
- "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
- "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
- "dev": true,
- "peer": true,
- "requires": {}
- },
"@vitest/spy": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
diff --git a/package.json b/package.json
index a1089e00..dbe74a45 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
"@swc/core": "^1.15.11",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^12.1.3",
+ "@testing-library/user-event": "^14.6.1",
"@testplane/storybook": "^1.7.4",
"@testplane/testing-library": "^1.0.2",
"@types/jest": "^30.0.0",
diff --git a/src/entities/Article/ui/ArticleList/ArticleList.tsx b/src/entities/Article/ui/ArticleList/ArticleList.tsx
index a88bab17..ab44511e 100644
--- a/src/entities/Article/ui/ArticleList/ArticleList.tsx
+++ b/src/entities/Article/ui/ArticleList/ArticleList.tsx
@@ -15,6 +15,7 @@ interface ArticleListProps {
isLoading?: boolean;
view?: ArticleView;
target?: HTMLAttributeAnchorTarget;
+ virtualized?: boolean;
}
const CARD_WIDTH = 250;
@@ -36,6 +37,7 @@ export const ArticleList = ({
isLoading,
view = ArticleView.LIST,
target,
+ virtualized,
}: ArticleListProps) => {
const { t } = useTranslation('article');
const [containerWidth, setContainerWidth] = useState(0);
@@ -132,18 +134,30 @@ export const ArticleList = ({
cls[view],
])}
>
-
+ {virtualized ? (
+
+ ) : (
+ articles.map((item) => (
+
+ ))
+ )}
{isLoading && getSkeletons(view)}
);
diff --git a/src/entities/Profile/ui/ProfileCard/ProfileCard.tsx b/src/entities/Profile/ui/ProfileCard/ProfileCard.tsx
index 0a7aea9e..2f6ba9c1 100644
--- a/src/entities/Profile/ui/ProfileCard/ProfileCard.tsx
+++ b/src/entities/Profile/ui/ProfileCard/ProfileCard.tsx
@@ -103,12 +103,14 @@ export const ProfileCard = ({
placeholder={t('Your name')}
readOnly={readonly}
onChange={onChangeFirstname}
+ data-testid="ProfileCard.firstname"
/>
({
- getArticleRecommendationsList: build.query({
+ getArticleRecommendationsList: build.query({
query: (limit) => ({
url: 'articles',
params: {
diff --git a/src/features/ArticleRecommendationsList/ui/ArticleRecommendationsList/ArticleRecommendationsList.tsx b/src/features/ArticleRecommendationsList/ui/ArticleRecommendationsList/ArticleRecommendationsList.tsx
index f32c27cd..2a5b4f00 100644
--- a/src/features/ArticleRecommendationsList/ui/ArticleRecommendationsList/ArticleRecommendationsList.tsx
+++ b/src/features/ArticleRecommendationsList/ui/ArticleRecommendationsList/ArticleRecommendationsList.tsx
@@ -26,11 +26,11 @@ export const ArticleRecommendationsList = memo(
} = getArticleRecommendationsList(getRandomNumber());
if (isLoading) {
- ;
+ return ;
}
- if (error) {
- ;
+ if (error || !articles) {
+ return ;
}
return (
@@ -41,6 +41,7 @@ export const ArticleRecommendationsList = memo(
articles={articles}
isLoading={isLoading}
target="_blank"
+ virtualized={false}
/>
);
diff --git a/src/features/EditableProfileCard/ui/EditableProfileCard/EditableProfileCard.test.tsx b/src/features/EditableProfileCard/ui/EditableProfileCard/EditableProfileCard.test.tsx
new file mode 100644
index 00000000..3459404b
--- /dev/null
+++ b/src/features/EditableProfileCard/ui/EditableProfileCard/EditableProfileCard.test.tsx
@@ -0,0 +1,130 @@
+import { componentRender } from 'shared/lib/tests/componentRouter/componentRouter';
+import { Profile } from 'entities/Profile';
+import { Currency } from 'entities/Currency';
+import { Country } from 'entities/Country';
+import { userEvent } from '@testing-library/user-event';
+import { screen } from '@testing-library/react';
+import { $api } from 'shared/api/api';
+import { profileReducer } from '../../model/slices/profileSlice';
+import { EditableProfileCard } from './EditableProfileCard';
+
+const profile: Profile = {
+ id: '1',
+ firstname: 'admin',
+ lastname: 'admin',
+ age: 22,
+ currency: Currency.EUR,
+ country: Country.Belarus,
+ city: 'Minsk',
+ username: 'adminchik',
+};
+
+const options = {
+ initialState: {
+ profile: {
+ readonly: true,
+ data: profile,
+ form: profile,
+ },
+ user: {
+ authData: {
+ id: '1',
+ username: 'admin',
+ },
+ },
+ },
+ asyncReducers: {
+ profile: profileReducer,
+ },
+};
+
+describe('features/EditableProfileCard', () => {
+ beforeEach(() => componentRender(, options));
+
+ test('Should be Edit mode form', async () => {
+ await userEvent.click(
+ screen.getByTestId('EditableProfileCardHeader.EditButton'),
+ );
+ expect(screen.getByTestId('EditableProfileCardHeader.CancelButton'));
+ });
+
+ test('Reset data in the form when click cancel button', async () => {
+ await userEvent.click(
+ screen.getByTestId('EditableProfileCardHeader.EditButton'),
+ );
+
+ // Clear the data in inputs
+ await userEvent.clear(screen.getByTestId('ProfileCard.firstname'));
+ await userEvent.clear(screen.getByTestId('ProfileCard.lastname'));
+ await userEvent.clear(screen.getByTestId('ProfileCard.age'));
+
+ // Type the another data in inputs
+ await userEvent.type(
+ screen.getByTestId('ProfileCard.firstname'),
+ 'user',
+ );
+ await userEvent.type(
+ screen.getByTestId('ProfileCard.lastname'),
+ 'user',
+ );
+ await userEvent.type(screen.getByTestId('ProfileCard.age'), '20');
+
+ // Check the another data in inputs
+ expect(screen.getByTestId('ProfileCard.firstname')).toHaveValue('user');
+ expect(screen.getByTestId('ProfileCard.lastname')).toHaveValue('user');
+ expect(screen.getByTestId('ProfileCard.age')).toHaveValue('20');
+
+ // Click the cancel button for reset another data in inputs
+ await userEvent.click(
+ screen.getByTestId('EditableProfileCardHeader.CancelButton'),
+ );
+
+ // And check the initialdatas in inputs
+ expect(screen.getByTestId('ProfileCard.firstname')).toHaveValue(
+ profile.firstname,
+ );
+ expect(screen.getByTestId('ProfileCard.lastname')).toHaveValue(
+ profile.lastname,
+ );
+ expect(screen.getByTestId('ProfileCard.age')).toHaveValue(
+ String(profile.age),
+ );
+ });
+
+ test('Should be display error', async () => {
+ await userEvent.click(
+ screen.getByTestId('EditableProfileCardHeader.EditButton'),
+ );
+
+ await userEvent.clear(screen.getByTestId('ProfileCard.firstname'));
+
+ expect(screen.getByTestId('ProfileCard.firstname')).toHaveValue('');
+
+ await userEvent.click(
+ screen.getByTestId('EditableProfileCardHeader.SaveButton'),
+ );
+
+ expect(
+ screen.getByTestId('EditableProfileCard.Error.Paragraph'),
+ ).toBeInTheDocument();
+ });
+
+ test('Put request method for success data', async () => {
+ const mockPutrequest = jest.spyOn($api, 'put');
+
+ await userEvent.click(
+ screen.getByTestId('EditableProfileCardHeader.EditButton'),
+ );
+
+ await userEvent.type(
+ screen.getByTestId('ProfileCard.firstname'),
+ 'user',
+ );
+
+ await userEvent.click(
+ screen.getByTestId('EditableProfileCardHeader.SaveButton'),
+ );
+
+ expect(mockPutrequest).toHaveBeenCalled();
+ });
+});
diff --git a/src/features/EditableProfileCard/ui/EditableProfileCard/EditableProfileCard.tsx b/src/features/EditableProfileCard/ui/EditableProfileCard/EditableProfileCard.tsx
index 62aadad9..48fe738d 100644
--- a/src/features/EditableProfileCard/ui/EditableProfileCard/EditableProfileCard.tsx
+++ b/src/features/EditableProfileCard/ui/EditableProfileCard/EditableProfileCard.tsx
@@ -140,6 +140,7 @@ export const EditableProfileCard = memo(
key={error}
theme={TextTheme.ERROR}
text={validateErrorTranslates[error]}
+ data-testid="EditableProfileCard.Error"
/>
))}
{t('Edit')}
@@ -59,12 +60,14 @@ export const EditableProfileCardHeader = ({
diff --git a/src/shared/lib/hooks/useInitialEffect/useInitialEffect.ts b/src/shared/lib/hooks/useInitialEffect/useInitialEffect.ts
index fa18ba5c..5576d966 100644
--- a/src/shared/lib/hooks/useInitialEffect/useInitialEffect.ts
+++ b/src/shared/lib/hooks/useInitialEffect/useInitialEffect.ts
@@ -8,7 +8,7 @@ import { useEffect } from 'react';
*/
export function useInitialEffect(callback: () => void) {
useEffect(() => {
- if (__PROJECT__ !== 'storybook') {
+ if (__PROJECT__ !== 'storybook' && __PROJECT__ !== 'jest') {
callback();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/src/shared/lib/tests/componentRouter/componentRouter.tsx b/src/shared/lib/tests/componentRouter/componentRouter.tsx
index c5f8f75d..9ac8b2fb 100644
--- a/src/shared/lib/tests/componentRouter/componentRouter.tsx
+++ b/src/shared/lib/tests/componentRouter/componentRouter.tsx
@@ -1,3 +1,4 @@
+import { ReducersMapObject } from '@reduxjs/toolkit';
import { render } from '@testing-library/react';
import { StateSchema, StoreProvider } from 'app/providers/StoreProvider';
import { ReactNode } from 'react';
@@ -8,16 +9,20 @@ import i18nForTests from 'shared/config/i18n/i18nForTests';
export interface componentRenderOptions {
route?: string;
initialState?: DeepPartial;
+ asyncReducers?: DeepPartial>;
}
export function componentRender(
component: ReactNode,
options: componentRenderOptions = {},
) {
- const { route = '/', initialState } = options;
+ const { route = '/', initialState, asyncReducers } = options;
return render(
-
+
{component}
diff --git a/src/shared/ui/Text/Text.tsx b/src/shared/ui/Text/Text.tsx
index f6fa8df8..b3cc5c03 100644
--- a/src/shared/ui/Text/Text.tsx
+++ b/src/shared/ui/Text/Text.tsx
@@ -26,6 +26,7 @@ interface TextProps {
theme?: TextTheme;
align?: TextAlign;
size?: TextSize;
+ 'data-testid'?: string;
}
type HeaderTagType = 'h2' | 'h3' | 'h4';
@@ -44,6 +45,7 @@ export const Text = memo(
theme = TextTheme.PRIMARY,
align = TextAlign.LEFT,
size = TextSize.M,
+ 'data-testid': dataTestId = 'Text',
}: TextProps) => {
const mods: Mods = {
[cls[theme]]: true,
@@ -55,8 +57,22 @@ export const Text = memo(
return (
- {title &&
{title}}
- {text &&
{text}
}
+ {title && (
+
+ {title}
+
+ )}
+ {text && (
+
+ {text}
+
+ )}
);
},
diff --git a/testplane/ArticleList.stories.tsx-screens/24c982a/linux-chrome/plain.png b/testplane/ArticleList.stories.tsx-screens/24c982a/linux-chrome/plain.png
index 6409c75f..55964ba5 100644
Binary files a/testplane/ArticleList.stories.tsx-screens/24c982a/linux-chrome/plain.png and b/testplane/ArticleList.stories.tsx-screens/24c982a/linux-chrome/plain.png differ
diff --git a/testplane/ArticleList.stories.tsx-screens/975fb7c/linux-chrome/plain.png b/testplane/ArticleList.stories.tsx-screens/975fb7c/linux-chrome/plain.png
index 0502c5d0..8d489795 100644
Binary files a/testplane/ArticleList.stories.tsx-screens/975fb7c/linux-chrome/plain.png and b/testplane/ArticleList.stories.tsx-screens/975fb7c/linux-chrome/plain.png differ
diff --git a/testplane/ArticleList.stories.tsx-screens/b39f666/linux-chrome/plain.png b/testplane/ArticleList.stories.tsx-screens/b39f666/linux-chrome/plain.png
index c3e2e849..9c5b645d 100644
Binary files a/testplane/ArticleList.stories.tsx-screens/b39f666/linux-chrome/plain.png and b/testplane/ArticleList.stories.tsx-screens/b39f666/linux-chrome/plain.png differ
diff --git a/testplane/ArticleList.stories.tsx-screens/c87465e/linux-chrome/plain.png b/testplane/ArticleList.stories.tsx-screens/c87465e/linux-chrome/plain.png
index 0502c5d0..9417b46d 100644
Binary files a/testplane/ArticleList.stories.tsx-screens/c87465e/linux-chrome/plain.png and b/testplane/ArticleList.stories.tsx-screens/c87465e/linux-chrome/plain.png differ
diff --git a/testplane/ArticleList.stories.tsx-screens/d909d51/linux-chrome/plain.png b/testplane/ArticleList.stories.tsx-screens/d909d51/linux-chrome/plain.png
index 6409c75f..1ce3bd53 100644
Binary files a/testplane/ArticleList.stories.tsx-screens/d909d51/linux-chrome/plain.png and b/testplane/ArticleList.stories.tsx-screens/d909d51/linux-chrome/plain.png differ
diff --git a/testplane/Avatar.stories.tsx-screens/d76533d/linux-chrome/plain.png b/testplane/Avatar.stories.tsx-screens/d76533d/linux-chrome/plain.png
index 29b6b18f..e0cb84e4 100644
Binary files a/testplane/Avatar.stories.tsx-screens/d76533d/linux-chrome/plain.png and b/testplane/Avatar.stories.tsx-screens/d76533d/linux-chrome/plain.png differ