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
Original file line number Diff line number Diff line change
@@ -1,33 +1,83 @@
import { render } from '@testing-library/react';
import { Button } from '@patternfly/react-core';
import { screen } from '@testing-library/react';
import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils';
import { AccessDenied, EmptyBox, ConsoleEmptyState } from '..';

const TestIcon = () => 'TestIcon';

describe('EmptyBox', () => {
it('should render without label', () => {
const { getByText } = render(<EmptyBox />);
getByText('Not found');
it('renders default "Not found" message without label', () => {
renderWithProviders(<EmptyBox />);
expect(screen.getByText('Not found')).toBeVisible();
});

it('should render with label', () => {
const { getByText } = render(<EmptyBox label="test-label" />);
getByText('No test-label found');
it('renders message with label when provided', () => {
renderWithProviders(<EmptyBox label="resources" />);
expect(screen.getByText('No resources found')).toBeVisible();
});
});

describe('MsgBox', () => {
it('should render title', () => {
const { getByText } = render(<ConsoleEmptyState title="test-title" />);
getByText('test-title');
describe('ConsoleEmptyState', () => {
it('renders title and children in body', () => {
renderWithProviders(
<ConsoleEmptyState title="Empty State Title">Body content</ConsoleEmptyState>,
);
expect(screen.getByText('Empty State Title')).toBeVisible();
expect(screen.getByText('Body content')).toBeVisible();
});

it('renders Icon when provided', () => {
renderWithProviders(<ConsoleEmptyState Icon={TestIcon} title="With Icon" />);
expect(screen.getByText('TestIcon')).toBeVisible();
});

it('should render children', () => {
const { getByText } = render(<ConsoleEmptyState>test-child</ConsoleEmptyState>);
getByText('test-child');
it('renders primary and secondary actions when provided', () => {
const primaryActions = [<Button key="create">Create Resource</Button>];
const secondaryActions = [
<Button key="learn" variant="link">
Learn more
</Button>,
];
renderWithProviders(
<ConsoleEmptyState
title="Test"
primaryActions={primaryActions}
secondaryActions={secondaryActions}
/>,
);
expect(screen.getByRole('button', { name: 'Create Resource' })).toBeVisible();
expect(screen.getByRole('button', { name: 'Learn more' })).toBeVisible();
});

it('does not render body or footer when not provided', () => {
renderWithProviders(<ConsoleEmptyState title="No Body" />);
expect(screen.queryByTestId('console-empty-state-body')).not.toBeInTheDocument();
expect(screen.queryByTestId('console-empty-state-footer')).not.toBeInTheDocument();
});
});

describe('AccessDenied', () => {
it('should render message', () => {
const { getByText } = render(<AccessDenied>test-message</AccessDenied>);
getByText('test-message');
it('renders restricted access title and message', () => {
renderWithProviders(<AccessDenied />);
expect(screen.getByText('Restricted access')).toBeVisible();
expect(
screen.getByText("You don't have access to this section due to cluster policy"),
).toBeVisible();
});

it('renders error details alert when children provided', () => {
renderWithProviders(<AccessDenied>Permission denied for resource xyz</AccessDenied>);
expect(screen.getByText('Error details')).toBeVisible();
expect(screen.getByText('Permission denied for resource xyz')).toBeVisible();
});

it('does not render error alert when no children provided', () => {
renderWithProviders(<AccessDenied />);
expect(screen.queryByText('Error details')).not.toBeInTheDocument();
});

it('renders restricted sign icon', () => {
renderWithProviders(<AccessDenied />);
expect(screen.getByAltText('Restricted access')).toBeVisible();
});
});
Original file line number Diff line number Diff line change
@@ -1,47 +1,69 @@
import { screen, configure, fireEvent, act, waitFor } from '@testing-library/react';
import { screen, configure, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { mockFormikRenderer } from '../../../test-utils/unit-test-utils';
import NumberSpinnerField from '../NumberSpinnerField';

const getInput = () => screen.getByTestId('number-spinner-field') as HTMLInputElement;

configure({ testIdAttribute: 'data-test-id' });

describe('Number Spinner Field', () => {
it('should render input field with type number', () => {
mockFormikRenderer(<NumberSpinnerField name="spinnerField" />);
const input = getInput();
expect(input.type).toEqual('number');
const getInput = () => screen.getByRole('spinbutton') as HTMLInputElement;

describe('NumberSpinnerField', () => {
it('renders input with label, help text, and increment/decrement buttons', () => {
mockFormikRenderer(
<NumberSpinnerField name="spinnerField" label="Replica Count" helpText="Enter a number" />,
);
expect(getInput()).toHaveAttribute('type', 'number');
expect(screen.getByText('Replica Count')).toBeVisible();
expect(screen.getByText('Enter a number')).toBeVisible();
expect(screen.getByRole('button', { name: /increment/i })).toBeVisible();
expect(screen.getByRole('button', { name: /decrement/i })).toBeVisible();
});

it('should render input with increment decrement button', () => {
mockFormikRenderer(<NumberSpinnerField name="spinnerField" />);
screen.getByTestId('Increment');
screen.getByTestId('Decrement');
it('displays initial value from formik', () => {
mockFormikRenderer(<NumberSpinnerField name="spinnerField" />, { spinnerField: 5 });
expect(getInput()).toHaveValue(5);
});

it('should only put numbers in input field', async () => {
it('displays 0 when initial value is empty', () => {
mockFormikRenderer(<NumberSpinnerField name="spinnerField" />, { spinnerField: '' });
expect(getInput()).toHaveValue(0);
});

expect(getInput().value).toEqual('0');
it('updates value when typing in input field', async () => {
const user = userEvent.setup();
mockFormikRenderer(<NumberSpinnerField name="spinnerField" />, { spinnerField: '' });

await act(async () => {
await fireEvent.change(getInput(), {
currentTarget: { value: '12' },
target: { value: '12' },
});
});
await user.clear(getInput());
await user.type(getInput(), '42');

await waitFor(() => expect(getInput().value).toEqual('12'));
await waitFor(() => expect(getInput()).toHaveValue(42));
});

it('should increment or decrement value based on clicked button', () => {
mockFormikRenderer(<NumberSpinnerField name="spinnerField" />, { spinnerField: '' });
it('increments value when increment button is clicked', async () => {
const user = userEvent.setup();
mockFormikRenderer(<NumberSpinnerField name="spinnerField" />, { spinnerField: 0 });

await user.click(screen.getByRole('button', { name: /increment/i }));

expect(getInput()).toHaveValue(1);
});

it('decrements value when decrement button is clicked', async () => {
const user = userEvent.setup();
mockFormikRenderer(<NumberSpinnerField name="spinnerField" />, { spinnerField: 5 });

await user.click(screen.getByRole('button', { name: /decrement/i }));

expect(getInput()).toHaveValue(4);
});

it('associates help text with input via aria-describedby', () => {
mockFormikRenderer(<NumberSpinnerField name="spinnerField" helpText="Helpful description" />);
expect(getInput()).toHaveAttribute('aria-describedby');
});

expect(getInput().value).toEqual('0');
fireEvent.click(screen.getByTestId('Increment'));
fireEvent.click(screen.getByTestId('Increment'));
expect(getInput().value).toEqual('2');
fireEvent.click(screen.getByTestId('Decrement'));
expect(getInput().value).toEqual('1');
it('input is accessible by spinbutton role', () => {
mockFormikRenderer(<NumberSpinnerField name="spinnerField" label="Replicas" />);
expect(screen.getByRole('spinbutton')).toBeVisible();
});
});
Original file line number Diff line number Diff line change
@@ -1,91 +1,113 @@
import { screen, fireEvent } from '@testing-library/react';
import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils';
import type { GettingStartedCardProps } from '../GettingStartedCard';
import { GettingStartedCard } from '../GettingStartedCard';

jest.mock('@console/shared/src/hooks/useTelemetry', () => ({
useTelemetry: () => jest.fn(),
}));

jest.mock('@console/dynamic-plugin-sdk/src/perspective/useActivePerspective', () => ({
default: jest.fn(() => ['admin', jest.fn()]),
}));

jest.mock('@patternfly/react-icons', () => ({
ArrowRightIcon: () => 'ArrowRightIcon',
ExternalLinkAltIcon: () => 'ExternalLinkAltIcon',
}));

describe('GettingStartedCard', () => {
const defaultProps: GettingStartedCardProps = {
id: 'test-card',
title: 'Test Card',
description: 'This is a test card.',
links: [
{
id: 'link-1',
title: 'Internal Link',
href: '/internal',
},
{
id: 'link-2',
title: 'External Link',
href: 'https://example.com',
external: true,
},
{ id: 'link-1', title: 'Internal Link', href: '/internal' },
{ id: 'link-2', title: 'External Link', href: 'https://example.com', external: true },
],
moreLink: {
id: 'more-link',
title: 'More Info',
href: '/more',
},
moreLink: { id: 'more-link', title: 'More Info', href: '/more' },
};

it('renders title and description', () => {
it('renders card title, description, and icon', async () => {
const TestIcon = () => <div>TestIcon</div>;
renderWithProviders(<GettingStartedCard {...defaultProps} icon={<TestIcon />} />);
await waitFor(() => {
expect(screen.getByRole('heading', { name: /Test Card/i, level: 3 })).toBeVisible();
});
expect(screen.getByText('This is a test card.')).toBeVisible();
expect(screen.getByText('TestIcon')).toBeVisible();
});

it('renders internal links with arrow icon', async () => {
renderWithProviders(<GettingStartedCard {...defaultProps} />);
const internalLink = await screen.findByTestId('item link-1');
expect(within(internalLink).getByText('Internal Link')).toBeVisible();
expect(within(internalLink).getByText('ArrowRightIcon')).toBeVisible();
});

it('renders external links with external link icon and target blank', async () => {
renderWithProviders(<GettingStartedCard {...defaultProps} />);
expect(screen.getByText('Test Card')).toBeInTheDocument();
expect(screen.getByText('This is a test card.')).toBeInTheDocument();
const externalLink = await screen.findByTestId('item link-2');
expect(within(externalLink).getByText('External Link')).toBeVisible();
expect(within(externalLink).getByText('ExternalLinkAltIcon')).toBeVisible();
expect(externalLink).toHaveAttribute('target', '_blank');
expect(externalLink).toHaveAttribute('rel', 'noopener noreferrer');
});

it('renders all links', () => {
it('renders moreLink as internal link or button based on props', async () => {
renderWithProviders(<GettingStartedCard {...defaultProps} />);
expect(screen.getByTestId('item link-1')).toBeInTheDocument();
expect(screen.getByTestId('item link-2')).toBeInTheDocument();
expect(screen.getByTestId('item more-link')).toBeInTheDocument();
expect(await screen.findByRole('link', { name: 'More Info' })).toHaveAttribute('href', '/more');
});

it('calls onClick for internal link', () => {
it('renders moreLink as button when onClick is provided and calls handler', async () => {
const user = userEvent.setup();
const onClick = jest.fn();
const props = {
...defaultProps,
links: [
{
id: 'link-1',
title: 'Internal Link',
href: '/internal',
onClick,
},
],
};
const props = { ...defaultProps, moreLink: { id: 'more-link', title: 'More Info', onClick } };
renderWithProviders(<GettingStartedCard {...props} />);
fireEvent.click(screen.getByTestId('item link-1'));
expect(onClick).toHaveBeenCalled();
await user.click(await screen.findByRole('button', { name: 'More Info' }));
expect(onClick).toHaveBeenCalledTimes(1);
});

it('calls onClick for moreLink', () => {
it('calls onClick for internal link when clicked', async () => {
const user = userEvent.setup();
const onClick = jest.fn();
const props = {
...defaultProps,
moreLink: {
id: 'more-link',
title: 'More Info',
href: '/more',
onClick,
},
links: [{ id: 'link-1', title: 'Internal Link', href: '/internal', onClick }],
};
renderWithProviders(<GettingStartedCard {...props} />);
fireEvent.click(screen.getByTestId('item more-link'));
expect(onClick).toHaveBeenCalled();
await user.click(await screen.findByTestId('item link-1'));
expect(onClick).toHaveBeenCalledTimes(1);
});

it('renders skeleton for loading links', () => {
const props = {
...defaultProps,
links: [
{
id: 'loading-link',
loading: true,
},
],
};
it('renders skeleton for loading links', async () => {
const props = { ...defaultProps, links: [{ id: 'loading-link', loading: true }] };
renderWithProviders(<GettingStartedCard {...props} />);
expect(await screen.findByTestId('getting-started-skeleton')).toBeVisible();
});

it('does not render links section when links array is empty', async () => {
const props = { ...defaultProps, links: [] };
renderWithProviders(<GettingStartedCard {...props} />);
await waitFor(() => {
expect(screen.queryByRole('list')).not.toBeInTheDocument();
});
});

it('does not render moreLink when not provided', async () => {
const props = { ...defaultProps, moreLink: undefined };
renderWithProviders(<GettingStartedCard {...props} />);
await waitFor(() => {
expect(screen.queryByRole('link', { name: 'More Info' })).not.toBeInTheDocument();
});
});

it('applies custom title color when provided', async () => {
const props = { ...defaultProps, titleColor: '#ff0000' };
renderWithProviders(<GettingStartedCard {...props} />);
expect(screen.getByTestId('getting-started-skeleton')).toBeInTheDocument();
expect(await screen.findByRole('heading', { name: 'Test Card' })).toHaveStyle({
color: '#ff0000',
});
});
});
Loading