Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions src/shared/ui/Stub/Stub.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { fireEvent, screen } from '@testing-library/react';
import { ComponentProps } from 'react';

import { useScreenSize } from '@/shared/libs';
import { renderComponent } from '@/shared/libs/jest/renderComponent/renderComponent';
import { stubTestIds } from '@/shared/ui/Stub/constants';
import { Stub } from '@/shared/ui/Stub/Stub';

jest.mock('@/shared/libs', () => ({
useScreenSize: jest.fn(),
}));

const mockedUseScreenSize = useScreenSize as jest.Mock;

const mockScreenSize = [
{ name: 'Desktop', isMobile: false, titleClass: 'body4' },
{ name: 'Mobile', isMobile: true, titleClass: 'body3-strong' },
];

const mockScreen = (isMobile: boolean) => {
mockedUseScreenSize.mockReturnValue({ isMobile });
};

const renderStub = (props: ComponentProps<typeof Stub>) => renderComponent(<Stub {...props} />);

describe('Stub Component', () => {
describe.each(mockScreenSize)('$name layout', ({ isMobile, titleClass }) => {
beforeEach(() => {
jest.clearAllMocks();
mockScreen(isMobile);
});

describe('Title', () => {
test('renders custom title', () => {
renderStub({ type: 'empty', title: 'title' });

const title = screen.getByTestId(stubTestIds.title);

expect(title).toHaveTextContent('title');
expect(title).toHaveClass(titleClass);
});

test('renders default title', () => {
renderStub({ type: 'empty' });

const title = screen.getByTestId(stubTestIds.title);

expect(title).toHaveTextContent('stub.empty.title');
expect(title).toHaveClass(titleClass);
});
});

describe('Subtitle', () => {
test('renders custom subtitle', () => {
renderStub({ type: 'empty', subtitle: 'subtitle' });

const subtitle = screen.getByTestId(stubTestIds.subtitle);

expect(subtitle).toHaveTextContent('subtitle');
expect(subtitle).toHaveClass('body3');
});

test('renders default subtitle', () => {
renderStub({ type: 'empty' });

const subtitle = screen.getByTestId(stubTestIds.subtitle);

expect(subtitle).toHaveTextContent('stub.empty.subtitle');
expect(subtitle).toHaveClass('body3');
});
});

test('does not render text container when title and subtitle are empty', () => {
renderStub({ type: 'empty', title: '', subtitle: '' });

expect(screen.queryByTestId(stubTestIds.container)).not.toBeInTheDocument();
});

describe('Button', () => {
test('does not render button when buttonText is empty', () => {
renderStub({ type: 'empty' });

expect(screen.queryByTestId(stubTestIds.button)).not.toBeInTheDocument();
});

test('renders button with custom text', () => {
renderStub({ type: 'empty', buttonText: 'button' });

expect(screen.getByTestId(stubTestIds.button)).toHaveTextContent('button');
});

test('button is disabled when onClick is not provided', () => {
renderStub({ type: 'error' });

expect(screen.getByTestId(stubTestIds.button)).toBeDisabled();
});

test('button is enabled and calls onClick', () => {
const onClick = jest.fn();

renderStub({ type: 'error', onClick });

const button = screen.getByTestId(stubTestIds.button);

expect(button).not.toBeDisabled();
fireEvent.click(button);
expect(onClick).toHaveBeenCalledTimes(1);
});

test('renders outline button for filter-empty type', () => {
renderStub({ type: 'filter-empty', onClick: jest.fn() });

const button = screen.getByTestId(stubTestIds.button);

expect(button).toHaveClass('button-outline');
});
});

test('applies custom className', () => {
renderStub({ type: 'empty', className: 'custom-class' });

const wrapper = screen.getByTestId(stubTestIds.container).closest('.custom-class');

expect(wrapper).toBeInTheDocument();
});
});
});
16 changes: 13 additions & 3 deletions src/shared/ui/Stub/Stub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useScreenSize } from '@/shared/libs';
import { Button } from '@/shared/ui/Button';
import { Card } from '@/shared/ui/Card';
import { Flex } from '@/shared/ui/Flex';
import { stubTestIds } from '@/shared/ui/Stub/constants';
import { Text } from '@/shared/ui/Text';

import styles from './Stub.module.css';
Expand Down Expand Up @@ -86,9 +87,17 @@ export const Stub = ({ type, title, subtitle, buttonText, onClick, className }:
<img src={imgByType[type]} alt="" loading="lazy" className={styles.img} />

{(resolvedTitle || resolvedSubtitle) && (
<Flex gap="6" align="center" direction="column">
{Boolean(resolvedTitle) && <Text variant={titleVariant}>{resolvedTitle}</Text>}
{Boolean(resolvedSubtitle) && <Text variant="body3">{resolvedSubtitle}</Text>}
<Flex dataTestId={stubTestIds.container} gap="6" align="center" direction="column">
{Boolean(resolvedTitle) && (
<Text dataTestId={stubTestIds.title} variant={titleVariant}>
{resolvedTitle}
</Text>
)}
{Boolean(resolvedSubtitle) && (
<Text dataTestId={stubTestIds.subtitle} variant="body3">
{resolvedSubtitle}
</Text>
)}
</Flex>
)}

Expand All @@ -99,6 +108,7 @@ export const Stub = ({ type, title, subtitle, buttonText, onClick, className }:
onClick={onClick}
disabled={!onClick}
className={styles.button}
dataTestId={stubTestIds.button}
>
{resolvedButtonText}
</Button>
Expand Down
6 changes: 6 additions & 0 deletions src/shared/ui/Stub/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const stubTestIds = {
container: 'container',
title: 'title',
subtitle: 'subtitle',
button: 'button',
};