Issues/215 topics page tests (#1948)
* Add Tests to Topics Lists Page for TopicsTableCells * Delete obsolete ListItems and its test suites from topics. * Add tests suites for the new Topic creation * Add tests suites for TopicForm styled components * Minor code modifications in the CustomParamField test file * Minor code modifications in the CustomParams test file * Minor code modifications in the Topic folder * Add test suites for Topic Edit Page + minor modifications in the DangerZone test suite * Add tests suites for Topic Edit page * Add tests suites for Topic Edit page * Add tests suites for Topic Edit page * Add tests suites for DangerZone and validation * Add tests suites for DangerZone * Add tests suites for DangerZone * Add tests suites to SendMessage * increase the tests coverage for validateMessage * minor changes in the SendMessage and validateMessage function * add alert message suggestion in the SendMessage * add alert message suggestion in the SendMessage * Total Coverage of Overview test suites * increase tests suite coverage in the Filters styles * increase tests suite coverage in the Filters styles + Messages Page * improve the test coverage of the Message Component * improve the test coverage of the Message Component * improve the test coverage of the MessagesTable Component * improve the test coverage of the MessagesTable Component * improve the test coverage of the MessagesTable Component * Add Tests for Topic Page * Change to react testing library from enzyme Topics list * optimizing List elements Tests suites * delete necessary file * minor bug fix in messages due to the rebase * minor semantic changes in the Test suites
This commit is contained in:
parent
a94697c6af
commit
e597f14b8d
25 changed files with 1454 additions and 693 deletions
|
@ -1,168 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
ClusterName,
|
|
||||||
TopicName,
|
|
||||||
TopicWithDetailedInfo,
|
|
||||||
} from 'redux/interfaces';
|
|
||||||
import DropdownItem from 'components/common/Dropdown/DropdownItem';
|
|
||||||
import Dropdown from 'components/common/Dropdown/Dropdown';
|
|
||||||
import ConfirmationModal from 'components/common/ConfirmationModal/ConfirmationModal';
|
|
||||||
import ClusterContext from 'components/contexts/ClusterContext';
|
|
||||||
import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
|
|
||||||
import { Tag } from 'components/common/Tag/Tag.styled';
|
|
||||||
import VerticalElipsisIcon from 'components/common/Icons/VerticalElipsisIcon';
|
|
||||||
import { TableKeyLink } from 'components/common/table/Table/TableKeyLink.styled';
|
|
||||||
|
|
||||||
import * as S from './List.styled';
|
|
||||||
|
|
||||||
export interface ListItemProps {
|
|
||||||
topic: TopicWithDetailedInfo;
|
|
||||||
selected: boolean;
|
|
||||||
toggleTopicSelected(topicName: TopicName): void;
|
|
||||||
deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
|
||||||
recreateTopic: (clusterName: ClusterName, topicName: TopicName) => void;
|
|
||||||
clusterName: ClusterName;
|
|
||||||
clearTopicMessages(topicName: TopicName, clusterName: ClusterName): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ListItem: React.FC<ListItemProps> = ({
|
|
||||||
topic: {
|
|
||||||
name,
|
|
||||||
internal,
|
|
||||||
partitions,
|
|
||||||
segmentSize,
|
|
||||||
replicationFactor,
|
|
||||||
cleanUpPolicy,
|
|
||||||
},
|
|
||||||
selected,
|
|
||||||
toggleTopicSelected,
|
|
||||||
deleteTopic,
|
|
||||||
recreateTopic,
|
|
||||||
clusterName,
|
|
||||||
clearTopicMessages,
|
|
||||||
}) => {
|
|
||||||
const { isReadOnly, isTopicDeletionAllowed } =
|
|
||||||
React.useContext(ClusterContext);
|
|
||||||
|
|
||||||
const [isDeleteTopicConfirmationVisible, setDeleteTopicConfirmationVisible] =
|
|
||||||
React.useState(false);
|
|
||||||
|
|
||||||
const [
|
|
||||||
isRecreateTopicConfirmationVisible,
|
|
||||||
setRecreateTopicConfirmationVisible,
|
|
||||||
] = React.useState(false);
|
|
||||||
|
|
||||||
const { outOfSyncReplicas, numberOfMessages } = React.useMemo(() => {
|
|
||||||
if (partitions === undefined || partitions.length === 0) {
|
|
||||||
return {
|
|
||||||
outOfSyncReplicas: 0,
|
|
||||||
numberOfMessages: 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return partitions.reduce(
|
|
||||||
(memo, { replicas, offsetMax, offsetMin }) => {
|
|
||||||
const outOfSync = replicas?.filter(({ inSync }) => !inSync);
|
|
||||||
return {
|
|
||||||
outOfSyncReplicas: memo.outOfSyncReplicas + (outOfSync?.length || 0),
|
|
||||||
numberOfMessages: memo.numberOfMessages + (offsetMax - offsetMin),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{
|
|
||||||
outOfSyncReplicas: 0,
|
|
||||||
numberOfMessages: 0,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}, [partitions]);
|
|
||||||
|
|
||||||
const deleteTopicHandler = React.useCallback(() => {
|
|
||||||
deleteTopic(clusterName, name);
|
|
||||||
}, [clusterName, deleteTopic, name]);
|
|
||||||
|
|
||||||
const recreateTopicHandler = React.useCallback(() => {
|
|
||||||
recreateTopic(clusterName, name);
|
|
||||||
setRecreateTopicConfirmationVisible(false);
|
|
||||||
}, [recreateTopic, clusterName, name]);
|
|
||||||
|
|
||||||
const clearTopicMessagesHandler = React.useCallback(() => {
|
|
||||||
clearTopicMessages(clusterName, name);
|
|
||||||
}, [clearTopicMessages, clusterName, name]);
|
|
||||||
const [vElipsisVisble, setVElipsisVisble] = React.useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<tr
|
|
||||||
onMouseEnter={() => setVElipsisVisble(true)}
|
|
||||||
onMouseLeave={() => setVElipsisVisble(false)}
|
|
||||||
>
|
|
||||||
{!isReadOnly && (
|
|
||||||
<td>
|
|
||||||
{!internal && (
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={selected}
|
|
||||||
onChange={() => {
|
|
||||||
toggleTopicSelected(name);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
)}
|
|
||||||
<TableKeyLink style={{ width: '44%' }}>
|
|
||||||
{internal && <Tag color="gray">IN</Tag>}
|
|
||||||
<S.Link exact to={`topics/${name}`} $isInternal={internal}>
|
|
||||||
{name}
|
|
||||||
</S.Link>
|
|
||||||
</TableKeyLink>
|
|
||||||
<td>{partitions?.length}</td>
|
|
||||||
<td>{outOfSyncReplicas}</td>
|
|
||||||
<td>{replicationFactor}</td>
|
|
||||||
<td>{numberOfMessages}</td>
|
|
||||||
<td>
|
|
||||||
<BytesFormatted value={segmentSize} />
|
|
||||||
</td>
|
|
||||||
<td className="topic-action-block" style={{ width: '4%' }}>
|
|
||||||
{!internal && !isReadOnly && vElipsisVisble ? (
|
|
||||||
<div className="has-text-right">
|
|
||||||
<Dropdown label={<VerticalElipsisIcon />} right>
|
|
||||||
{cleanUpPolicy === 'DELETE' && (
|
|
||||||
<DropdownItem onClick={clearTopicMessagesHandler} danger>
|
|
||||||
Clear Messages
|
|
||||||
</DropdownItem>
|
|
||||||
)}
|
|
||||||
{isTopicDeletionAllowed && (
|
|
||||||
<DropdownItem
|
|
||||||
onClick={() => setDeleteTopicConfirmationVisible(true)}
|
|
||||||
danger
|
|
||||||
>
|
|
||||||
Remove Topic
|
|
||||||
</DropdownItem>
|
|
||||||
)}
|
|
||||||
<DropdownItem
|
|
||||||
onClick={() => setRecreateTopicConfirmationVisible(true)}
|
|
||||||
danger
|
|
||||||
>
|
|
||||||
Recreate Topic
|
|
||||||
</DropdownItem>
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<ConfirmationModal
|
|
||||||
isOpen={isDeleteTopicConfirmationVisible}
|
|
||||||
onCancel={() => setDeleteTopicConfirmationVisible(false)}
|
|
||||||
onConfirm={deleteTopicHandler}
|
|
||||||
>
|
|
||||||
Are you sure want to remove <b>{name}</b> topic?
|
|
||||||
</ConfirmationModal>
|
|
||||||
<ConfirmationModal
|
|
||||||
isOpen={isRecreateTopicConfirmationVisible}
|
|
||||||
onCancel={() => setRecreateTopicConfirmationVisible(false)}
|
|
||||||
onConfirm={recreateTopicHandler}
|
|
||||||
>
|
|
||||||
Are you sure to recreate <b>{name}</b> topic?
|
|
||||||
</ConfirmationModal>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ListItem;
|
|
|
@ -1,56 +1,45 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mount, ReactWrapper } from 'enzyme';
|
import { render } from 'lib/testHelpers';
|
||||||
import { Route, Router } from 'react-router-dom';
|
import { screen, waitFor, within } from '@testing-library/react';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { Route, Router, StaticRouter } from 'react-router';
|
||||||
import ClusterContext, {
|
import ClusterContext, {
|
||||||
ContextProps,
|
ContextProps,
|
||||||
} from 'components/contexts/ClusterContext';
|
} from 'components/contexts/ClusterContext';
|
||||||
import List, { TopicsListProps } from 'components/Topics/List/List';
|
import List, { TopicsListProps } from 'components/Topics/List/List';
|
||||||
import { createMemoryHistory } from 'history';
|
import { createMemoryHistory } from 'history';
|
||||||
import { StaticRouter } from 'react-router';
|
|
||||||
import Search from 'components/common/Search/Search';
|
|
||||||
import { externalTopicPayload } from 'redux/reducers/topics/__test__/fixtures';
|
import { externalTopicPayload } from 'redux/reducers/topics/__test__/fixtures';
|
||||||
import { ConfirmationModalProps } from 'components/common/ConfirmationModal/ConfirmationModal';
|
import { CleanUpPolicy, SortOrder } from 'generated-sources';
|
||||||
import theme from 'theme/theme';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { ThemeProvider } from 'styled-components';
|
|
||||||
import { SortOrder } from 'generated-sources';
|
|
||||||
|
|
||||||
jest.mock(
|
|
||||||
'components/common/ConfirmationModal/ConfirmationModal',
|
|
||||||
() => 'mock-ConfirmationModal'
|
|
||||||
);
|
|
||||||
|
|
||||||
describe('List', () => {
|
describe('List', () => {
|
||||||
const setupComponent = (props: Partial<TopicsListProps> = {}) => (
|
const setupComponent = (props: Partial<TopicsListProps> = {}) => (
|
||||||
<ThemeProvider theme={theme}>
|
<List
|
||||||
<List
|
areTopicsFetching={false}
|
||||||
areTopicsFetching={false}
|
topics={[]}
|
||||||
topics={[]}
|
totalPages={1}
|
||||||
totalPages={1}
|
fetchTopicsList={jest.fn()}
|
||||||
fetchTopicsList={jest.fn()}
|
deleteTopic={jest.fn()}
|
||||||
deleteTopic={jest.fn()}
|
deleteTopics={jest.fn()}
|
||||||
deleteTopics={jest.fn()}
|
clearTopicsMessages={jest.fn()}
|
||||||
clearTopicsMessages={jest.fn()}
|
clearTopicMessages={jest.fn()}
|
||||||
clearTopicMessages={jest.fn()}
|
recreateTopic={jest.fn()}
|
||||||
recreateTopic={jest.fn()}
|
search=""
|
||||||
search=""
|
orderBy={null}
|
||||||
orderBy={null}
|
sortOrder={SortOrder.ASC}
|
||||||
sortOrder={SortOrder.ASC}
|
setTopicsSearch={jest.fn()}
|
||||||
setTopicsSearch={jest.fn()}
|
setTopicsOrderBy={jest.fn()}
|
||||||
setTopicsOrderBy={jest.fn()}
|
{...props}
|
||||||
{...props}
|
/>
|
||||||
/>
|
|
||||||
</ThemeProvider>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const historyMock = createMemoryHistory();
|
const historyMock = createMemoryHistory();
|
||||||
|
|
||||||
const mountComponentWithProviders = (
|
const renderComponentWithProviders = (
|
||||||
contextProps: Partial<ContextProps> = {},
|
contextProps: Partial<ContextProps> = {},
|
||||||
props: Partial<TopicsListProps> = {},
|
props: Partial<TopicsListProps> = {},
|
||||||
history = historyMock
|
history = historyMock
|
||||||
) =>
|
) =>
|
||||||
mount(
|
render(
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<ClusterContext.Provider
|
<ClusterContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
@ -68,84 +57,79 @@ describe('List', () => {
|
||||||
|
|
||||||
describe('when it has readonly flag', () => {
|
describe('when it has readonly flag', () => {
|
||||||
it('does not render the Add a Topic button', () => {
|
it('does not render the Add a Topic button', () => {
|
||||||
const component = mountComponentWithProviders();
|
renderComponentWithProviders();
|
||||||
expect(component.exists('Link')).toBeFalsy();
|
expect(screen.queryByText(/add a topic/)).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when it does not have readonly flag', () => {
|
describe('when it does not have readonly flag', () => {
|
||||||
let fetchTopicsList = jest.fn();
|
const fetchTopicsList = jest.fn();
|
||||||
let component: ReactWrapper;
|
|
||||||
const internalTopicsSwitchName = 'input[name="ShowInternalTopics"]';
|
|
||||||
|
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
|
|
||||||
beforeEach(() => {
|
afterEach(() => {
|
||||||
fetchTopicsList = jest.fn();
|
fetchTopicsList.mockClear();
|
||||||
|
|
||||||
component = mountComponentWithProviders(
|
|
||||||
{ isReadOnly: false },
|
|
||||||
{ fetchTopicsList }
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the Add a Topic button', () => {
|
it('renders the Add a Topic button', () => {
|
||||||
expect(component.exists('Link')).toBeTruthy();
|
renderComponentWithProviders({ isReadOnly: false }, { fetchTopicsList });
|
||||||
|
expect(screen.getByText(/add a topic/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls setTopicsSearch on input', () => {
|
it('calls setTopicsSearch on input', async () => {
|
||||||
const setTopicsSearch = jest.fn();
|
const setTopicsSearch = jest.fn();
|
||||||
component = mountComponentWithProviders({}, { setTopicsSearch });
|
renderComponentWithProviders({}, { setTopicsSearch });
|
||||||
const query = 'topic';
|
const query = 'topic';
|
||||||
const input = component.find(Search);
|
const searchElement = screen.getByPlaceholderText('Search by Topic Name');
|
||||||
input.props().handleSearch(query);
|
userEvent.type(searchElement, query);
|
||||||
expect(setTopicsSearch).toHaveBeenCalledWith(query);
|
await waitFor(() => {
|
||||||
|
expect(setTopicsSearch).toHaveBeenCalledWith(query);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show internal toggle state should be true if user has not used it yet', () => {
|
it('show internal toggle state should be true if user has not used it yet', () => {
|
||||||
const toggle = component.find(internalTopicsSwitchName);
|
renderComponentWithProviders({ isReadOnly: false }, { fetchTopicsList });
|
||||||
const { checked } = toggle.props();
|
const internalCheckBox = screen.getByRole('checkbox');
|
||||||
|
|
||||||
expect(checked).toEqual(true);
|
expect(internalCheckBox).toBeChecked();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show internal toggle state should match user preference', () => {
|
it('show internal toggle state should match user preference', () => {
|
||||||
localStorage.setItem('hideInternalTopics', 'true');
|
localStorage.setItem('hideInternalTopics', 'true');
|
||||||
component = mountComponentWithProviders(
|
renderComponentWithProviders({ isReadOnly: false }, { fetchTopicsList });
|
||||||
{ isReadOnly: false },
|
|
||||||
{ fetchTopicsList }
|
|
||||||
);
|
|
||||||
|
|
||||||
const toggle = component.find(internalTopicsSwitchName);
|
const internalCheckBox = screen.getByRole('checkbox');
|
||||||
const { checked } = toggle.props();
|
|
||||||
|
|
||||||
expect(checked).toEqual(false);
|
expect(internalCheckBox).not.toBeChecked();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should refetch topics on show internal toggle change', () => {
|
it('should re-fetch topics on show internal toggle change', async () => {
|
||||||
jest.clearAllMocks();
|
renderComponentWithProviders({ isReadOnly: false }, { fetchTopicsList });
|
||||||
const toggle = component.find(internalTopicsSwitchName);
|
const internalCheckBox: HTMLInputElement = screen.getByRole('checkbox');
|
||||||
const { checked } = toggle.props();
|
|
||||||
toggle.simulate('change');
|
|
||||||
|
|
||||||
expect(fetchTopicsList).toHaveBeenLastCalledWith({
|
userEvent.click(internalCheckBox);
|
||||||
search: '',
|
const { value } = internalCheckBox;
|
||||||
showInternal: !checked,
|
|
||||||
sortOrder: SortOrder.ASC,
|
await waitFor(() => {
|
||||||
|
expect(fetchTopicsList).toHaveBeenLastCalledWith({
|
||||||
|
search: '',
|
||||||
|
showInternal: value === 'on',
|
||||||
|
sortOrder: SortOrder.ASC,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reset page query param on show internal toggle change', () => {
|
it('should reset page query param on show internal toggle change', () => {
|
||||||
const mockedHistory = createMemoryHistory();
|
const mockedHistory = createMemoryHistory();
|
||||||
jest.spyOn(mockedHistory, 'push');
|
jest.spyOn(mockedHistory, 'push');
|
||||||
component = mountComponentWithProviders(
|
renderComponentWithProviders(
|
||||||
{ isReadOnly: false },
|
{ isReadOnly: false },
|
||||||
{ fetchTopicsList },
|
{ fetchTopicsList },
|
||||||
mockedHistory
|
mockedHistory
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggle = component.find(internalTopicsSwitchName);
|
const internalCheckBox: HTMLInputElement = screen.getByRole('checkbox');
|
||||||
toggle.simulate('change');
|
userEvent.click(internalCheckBox);
|
||||||
|
|
||||||
expect(mockedHistory.push).toHaveBeenCalledWith('/?page=1&perPage=25');
|
expect(mockedHistory.push).toHaveBeenCalledWith('/?page=1&perPage=25');
|
||||||
});
|
});
|
||||||
|
@ -153,127 +137,158 @@ describe('List', () => {
|
||||||
it('should set cached page query param on show internal toggle change', async () => {
|
it('should set cached page query param on show internal toggle change', async () => {
|
||||||
const mockedHistory = createMemoryHistory();
|
const mockedHistory = createMemoryHistory();
|
||||||
jest.spyOn(mockedHistory, 'push');
|
jest.spyOn(mockedHistory, 'push');
|
||||||
component = mountComponentWithProviders(
|
|
||||||
|
const cachedPage = 5;
|
||||||
|
mockedHistory.push(`/?page=${cachedPage}&perPage=25`);
|
||||||
|
|
||||||
|
renderComponentWithProviders(
|
||||||
{ isReadOnly: false },
|
{ isReadOnly: false },
|
||||||
{ fetchTopicsList, totalPages: 10 },
|
{ fetchTopicsList, totalPages: 10 },
|
||||||
mockedHistory
|
mockedHistory
|
||||||
);
|
);
|
||||||
|
|
||||||
const cachedPage = 5;
|
const searchInput = screen.getByPlaceholderText('Search by Topic Name');
|
||||||
|
userEvent.type(searchInput, 'nonEmptyString');
|
||||||
|
|
||||||
mockedHistory.push(`/?page=${cachedPage}&perPage=25`);
|
await waitFor(() => {
|
||||||
|
expect(mockedHistory.push).toHaveBeenCalledWith('/?page=1&perPage=25');
|
||||||
|
});
|
||||||
|
|
||||||
const input = component.find(Search);
|
userEvent.clear(searchInput);
|
||||||
input.props().handleSearch('nonEmptyString');
|
|
||||||
|
|
||||||
expect(mockedHistory.push).toHaveBeenCalledWith('/?page=1&perPage=25');
|
await waitFor(() => {
|
||||||
|
expect(mockedHistory.push).toHaveBeenCalledWith(
|
||||||
input.props().handleSearch('');
|
`/?page=${cachedPage}&perPage=25`
|
||||||
|
);
|
||||||
expect(mockedHistory.push).toHaveBeenCalledWith(
|
});
|
||||||
`/?page=${cachedPage}&perPage=25`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when some list items are selected', () => {
|
describe('when some list items are selected', () => {
|
||||||
const mockDeleteTopics = jest.fn();
|
const mockDeleteTopics = jest.fn();
|
||||||
|
const mockDeleteTopic = jest.fn();
|
||||||
|
const mockClearTopic = jest.fn();
|
||||||
const mockClearTopicsMessages = jest.fn();
|
const mockClearTopicsMessages = jest.fn();
|
||||||
|
const mockRecreate = jest.fn();
|
||||||
|
const fetchTopicsList = jest.fn();
|
||||||
|
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
const pathname = '/ui/clusters/local/topics';
|
const pathname = '/ui/clusters/local/topics';
|
||||||
const component = mount(
|
|
||||||
<StaticRouter location={{ pathname }}>
|
|
||||||
<Route path="/ui/clusters/:clusterName">
|
|
||||||
<ClusterContext.Provider
|
|
||||||
value={{
|
|
||||||
isReadOnly: false,
|
|
||||||
hasKafkaConnectConfigured: true,
|
|
||||||
hasSchemaRegistryConfigured: true,
|
|
||||||
isTopicDeletionAllowed: true,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{setupComponent({
|
|
||||||
topics: [
|
|
||||||
externalTopicPayload,
|
|
||||||
{ ...externalTopicPayload, name: 'external.topic2' },
|
|
||||||
],
|
|
||||||
deleteTopics: mockDeleteTopics,
|
|
||||||
clearTopicsMessages: mockClearTopicsMessages,
|
|
||||||
})}
|
|
||||||
</ClusterContext.Provider>
|
|
||||||
</Route>
|
|
||||||
</StaticRouter>
|
|
||||||
);
|
|
||||||
const getCheckboxInput = (at: number) =>
|
|
||||||
component.find('TableRow').at(at).find('input[type="checkbox"]').at(0);
|
|
||||||
|
|
||||||
const getConfirmationModal = () =>
|
beforeEach(() => {
|
||||||
component.find('mock-ConfirmationModal').at(0);
|
render(
|
||||||
|
<StaticRouter location={{ pathname }}>
|
||||||
it('renders delete/purge buttons', () => {
|
<Route path="/ui/clusters/:clusterName">
|
||||||
expect(getCheckboxInput(0).props().checked).toBeFalsy();
|
<ClusterContext.Provider
|
||||||
expect(getCheckboxInput(1).props().checked).toBeFalsy();
|
value={{
|
||||||
expect(component.find('.buttons').length).toEqual(0);
|
isReadOnly: false,
|
||||||
|
hasKafkaConnectConfigured: true,
|
||||||
// check first item
|
hasSchemaRegistryConfigured: true,
|
||||||
getCheckboxInput(0).simulate('change', { target: { checked: true } });
|
isTopicDeletionAllowed: true,
|
||||||
expect(getCheckboxInput(0).props().checked).toBeTruthy();
|
}}
|
||||||
expect(getCheckboxInput(1).props().checked).toBeFalsy();
|
>
|
||||||
|
{setupComponent({
|
||||||
// check second item
|
topics: [
|
||||||
getCheckboxInput(1).simulate('change', { target: { checked: true } });
|
{
|
||||||
expect(getCheckboxInput(0).props().checked).toBeTruthy();
|
...externalTopicPayload,
|
||||||
expect(getCheckboxInput(1).props().checked).toBeTruthy();
|
cleanUpPolicy: CleanUpPolicy.DELETE,
|
||||||
expect(
|
},
|
||||||
component.find('div[data-testid="delete-buttons"]').length
|
{ ...externalTopicPayload, name: 'external.topic2' },
|
||||||
).toEqual(1);
|
],
|
||||||
|
deleteTopics: mockDeleteTopics,
|
||||||
// uncheck second item
|
clearTopicsMessages: mockClearTopicsMessages,
|
||||||
getCheckboxInput(1).simulate('change', { target: { checked: false } });
|
recreateTopic: mockRecreate,
|
||||||
expect(getCheckboxInput(0).props().checked).toBeTruthy();
|
deleteTopic: mockDeleteTopic,
|
||||||
expect(getCheckboxInput(1).props().checked).toBeFalsy();
|
clearTopicMessages: mockClearTopic,
|
||||||
expect(
|
fetchTopicsList,
|
||||||
component.find('div[data-testid="delete-buttons"]').length
|
})}
|
||||||
).toEqual(1);
|
</ClusterContext.Provider>
|
||||||
|
</Route>
|
||||||
// uncheck first item
|
</StaticRouter>
|
||||||
getCheckboxInput(0).simulate('change', { target: { checked: false } });
|
);
|
||||||
expect(getCheckboxInput(0).props().checked).toBeFalsy();
|
|
||||||
expect(getCheckboxInput(1).props().checked).toBeFalsy();
|
|
||||||
expect(
|
|
||||||
component.find('div[data-testid="delete-buttons"]').length
|
|
||||||
).toEqual(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const checkActionButtonClick = async (action: string) => {
|
afterEach(() => {
|
||||||
|
mockDeleteTopics.mockClear();
|
||||||
|
mockClearTopicsMessages.mockClear();
|
||||||
|
mockRecreate.mockClear();
|
||||||
|
mockDeleteTopic.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
const getCheckboxInput = (at: number) => {
|
||||||
|
const rows = screen.getAllByRole('row');
|
||||||
|
return within(rows[at + 1]).getByRole('checkbox');
|
||||||
|
};
|
||||||
|
|
||||||
|
it('renders delete/purge buttons', () => {
|
||||||
|
const firstCheckbox = getCheckboxInput(0);
|
||||||
|
const secondCheckbox = getCheckboxInput(1);
|
||||||
|
expect(firstCheckbox).not.toBeChecked();
|
||||||
|
expect(secondCheckbox).not.toBeChecked();
|
||||||
|
// expect(component.find('.buttons').length).toEqual(0);
|
||||||
|
|
||||||
|
// check first item
|
||||||
|
userEvent.click(firstCheckbox);
|
||||||
|
expect(firstCheckbox).toBeChecked();
|
||||||
|
expect(secondCheckbox).not.toBeChecked();
|
||||||
|
|
||||||
|
expect(screen.getByTestId('delete-buttons')).toBeInTheDocument();
|
||||||
|
|
||||||
|
// check second item
|
||||||
|
userEvent.click(secondCheckbox);
|
||||||
|
expect(firstCheckbox).toBeChecked();
|
||||||
|
expect(secondCheckbox).toBeChecked();
|
||||||
|
|
||||||
|
expect(screen.getByTestId('delete-buttons')).toBeInTheDocument();
|
||||||
|
|
||||||
|
// uncheck second item
|
||||||
|
userEvent.click(secondCheckbox);
|
||||||
|
expect(firstCheckbox).toBeChecked();
|
||||||
|
expect(secondCheckbox).not.toBeChecked();
|
||||||
|
|
||||||
|
expect(screen.getByTestId('delete-buttons')).toBeInTheDocument();
|
||||||
|
|
||||||
|
// uncheck first item
|
||||||
|
userEvent.click(firstCheckbox);
|
||||||
|
expect(firstCheckbox).not.toBeChecked();
|
||||||
|
expect(secondCheckbox).not.toBeChecked();
|
||||||
|
|
||||||
|
expect(screen.queryByTestId('delete-buttons')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
const checkActionButtonClick = async (
|
||||||
|
action: 'deleteTopics' | 'clearTopicsMessages'
|
||||||
|
) => {
|
||||||
const buttonIndex = action === 'deleteTopics' ? 0 : 1;
|
const buttonIndex = action === 'deleteTopics' ? 0 : 1;
|
||||||
|
|
||||||
const confirmationText =
|
const confirmationText =
|
||||||
action === 'deleteTopics'
|
action === 'deleteTopics'
|
||||||
? 'Are you sure you want to remove selected topics?'
|
? 'Are you sure you want to remove selected topics?'
|
||||||
: 'Are you sure you want to purge messages of selected topics?';
|
: 'Are you sure you want to purge messages of selected topics?';
|
||||||
const mockFn =
|
const mockFn =
|
||||||
action === 'deleteTopics' ? mockDeleteTopics : mockClearTopicsMessages;
|
action === 'deleteTopics' ? mockDeleteTopics : mockClearTopicsMessages;
|
||||||
getCheckboxInput(0).simulate('change', { target: { checked: true } });
|
|
||||||
getCheckboxInput(1).simulate('change', { target: { checked: true } });
|
const firstCheckbox = getCheckboxInput(0);
|
||||||
let modal = getConfirmationModal();
|
const secondCheckbox = getCheckboxInput(1);
|
||||||
expect(modal.prop('isOpen')).toBeFalsy();
|
userEvent.click(firstCheckbox);
|
||||||
component
|
userEvent.click(secondCheckbox);
|
||||||
.find('div[data-testid="delete-buttons"]')
|
|
||||||
.find('button')
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||||
.at(buttonIndex)
|
|
||||||
.simulate('click');
|
const deleteButtonContainer = screen.getByTestId('delete-buttons');
|
||||||
expect(modal.text()).toEqual(confirmationText);
|
const buttonClickedElement = within(deleteButtonContainer).getAllByRole(
|
||||||
modal = getConfirmationModal();
|
'button'
|
||||||
expect(modal.prop('isOpen')).toBeTruthy();
|
)[buttonIndex];
|
||||||
await act(async () => {
|
userEvent.click(buttonClickedElement);
|
||||||
(modal.props() as ConfirmationModalProps).onConfirm();
|
|
||||||
|
const modal = screen.getByRole('dialog');
|
||||||
|
expect(within(modal).getByText(confirmationText)).toBeInTheDocument();
|
||||||
|
userEvent.click(within(modal).getByRole('button', { name: 'Submit' }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByTestId('delete-buttons')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
component.update();
|
|
||||||
expect(getCheckboxInput(0).props().checked).toBeFalsy();
|
|
||||||
expect(getCheckboxInput(1).props().checked).toBeFalsy();
|
|
||||||
expect(
|
|
||||||
component.find('div[data-testid="delete-buttons"]').length
|
|
||||||
).toEqual(0);
|
|
||||||
expect(mockFn).toBeCalledTimes(1);
|
expect(mockFn).toBeCalledTimes(1);
|
||||||
expect(mockFn).toBeCalledWith('local', [
|
expect(mockFn).toBeCalledWith('local', [
|
||||||
externalTopicPayload.name,
|
externalTopicPayload.name,
|
||||||
|
@ -290,28 +305,84 @@ describe('List', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('closes ConfirmationModal when clicked on the cancel button', async () => {
|
it('closes ConfirmationModal when clicked on the cancel button', async () => {
|
||||||
getCheckboxInput(0).simulate('change', { target: { checked: true } });
|
const firstCheckbox = getCheckboxInput(0);
|
||||||
getCheckboxInput(1).simulate('change', { target: { checked: true } });
|
const secondCheckbox = getCheckboxInput(1);
|
||||||
let modal = getConfirmationModal();
|
|
||||||
expect(modal.prop('isOpen')).toBeFalsy();
|
userEvent.click(firstCheckbox);
|
||||||
component
|
userEvent.click(secondCheckbox);
|
||||||
.find('div[data-testid="delete-buttons"]')
|
|
||||||
.find('button')
|
const deleteButton = screen.getByText('Delete selected topics');
|
||||||
.at(0)
|
|
||||||
.simulate('click');
|
userEvent.click(deleteButton);
|
||||||
modal = getConfirmationModal();
|
|
||||||
expect(modal.prop('isOpen')).toBeTruthy();
|
const modal = screen.getByRole('dialog');
|
||||||
await act(async () => {
|
userEvent.click(within(modal).getByText('Cancel'));
|
||||||
(modal.props() as ConfirmationModalProps).onCancel();
|
|
||||||
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||||
|
expect(firstCheckbox).toBeChecked();
|
||||||
|
expect(secondCheckbox).toBeChecked();
|
||||||
|
|
||||||
|
expect(screen.getByTestId('delete-buttons')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(mockDeleteTopics).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableRowActionClickAndCheck = async (
|
||||||
|
action: 'deleteTopics' | 'clearTopicsMessages' | 'recreate'
|
||||||
|
) => {
|
||||||
|
const row = screen.getAllByRole('row')[1];
|
||||||
|
userEvent.hover(row);
|
||||||
|
const actionBtn = within(row).getByRole('menu', { hidden: true });
|
||||||
|
|
||||||
|
userEvent.click(actionBtn);
|
||||||
|
|
||||||
|
let textBtn;
|
||||||
|
let mock: jest.Mock;
|
||||||
|
|
||||||
|
if (action === 'clearTopicsMessages') {
|
||||||
|
textBtn = 'Clear Messages';
|
||||||
|
mock = mockClearTopic;
|
||||||
|
} else if (action === 'deleteTopics') {
|
||||||
|
textBtn = 'Remove Topic';
|
||||||
|
mock = mockDeleteTopic;
|
||||||
|
} else {
|
||||||
|
textBtn = 'Recreate Topic';
|
||||||
|
mock = mockRecreate;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ourAction = screen.getByText(textBtn);
|
||||||
|
|
||||||
|
userEvent.click(ourAction);
|
||||||
|
|
||||||
|
let dialog = screen.getByRole('dialog');
|
||||||
|
expect(dialog).toBeInTheDocument();
|
||||||
|
userEvent.click(within(dialog).getByRole('button', { name: 'Submit' }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mock).toHaveBeenCalled();
|
||||||
|
if (action === 'clearTopicsMessages') {
|
||||||
|
expect(fetchTopicsList).toHaveBeenCalled();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
component.update();
|
|
||||||
expect(getConfirmationModal().prop('isOpen')).toBeFalsy();
|
userEvent.click(ourAction);
|
||||||
expect(getCheckboxInput(0).props().checked).toBeTruthy();
|
dialog = screen.getByRole('dialog');
|
||||||
expect(getCheckboxInput(1).props().checked).toBeTruthy();
|
expect(dialog).toBeInTheDocument();
|
||||||
expect(
|
userEvent.click(within(dialog).getByRole('button', { name: 'Cancel' }));
|
||||||
component.find('div[data-testid="delete-buttons"]').length
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||||
).toEqual(1);
|
expect(mock).toHaveBeenCalledTimes(1);
|
||||||
expect(mockDeleteTopics).toBeCalledTimes(0);
|
};
|
||||||
|
|
||||||
|
it('should test the actions of the row and their modal and fetching for removing', async () => {
|
||||||
|
await tableRowActionClickAndCheck('deleteTopics');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should test the actions of the row and their modal and fetching for clear', async () => {
|
||||||
|
await tableRowActionClickAndCheck('clearTopicsMessages');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should test the actions of the row and their modal and fetching for recreate', async () => {
|
||||||
|
await tableRowActionClickAndCheck('recreate');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
externalTopicPayload,
|
|
||||||
internalTopicPayload,
|
|
||||||
} from 'redux/reducers/topics/__test__/fixtures';
|
|
||||||
import ListItem, { ListItemProps } from 'components/Topics/List/ListItem';
|
|
||||||
import { screen } from '@testing-library/react';
|
|
||||||
import userEvent from '@testing-library/user-event';
|
|
||||||
import { render } from 'lib/testHelpers';
|
|
||||||
|
|
||||||
const mockDelete = jest.fn();
|
|
||||||
const clusterName = 'local';
|
|
||||||
const mockDeleteMessages = jest.fn();
|
|
||||||
const mockToggleTopicSelected = jest.fn();
|
|
||||||
const mockRecreateTopic = jest.fn();
|
|
||||||
jest.mock(
|
|
||||||
'components/common/ConfirmationModal/ConfirmationModal',
|
|
||||||
() => 'mock-ConfirmationModal'
|
|
||||||
);
|
|
||||||
|
|
||||||
jest.mock('react-redux', () => ({
|
|
||||||
...jest.requireActual('react-redux'),
|
|
||||||
useSelector: () => ['TOPIC_DELETION'],
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe('ListItem', () => {
|
|
||||||
const setupComponent = (props: Partial<ListItemProps> = {}) => (
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<ListItem
|
|
||||||
topic={internalTopicPayload}
|
|
||||||
deleteTopic={mockDelete}
|
|
||||||
clusterName={clusterName}
|
|
||||||
clearTopicMessages={mockDeleteMessages}
|
|
||||||
recreateTopic={mockRecreateTopic}
|
|
||||||
selected={false}
|
|
||||||
toggleTopicSelected={mockToggleTopicSelected}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
|
|
||||||
const getCheckbox = () => screen.getByRole('checkbox');
|
|
||||||
|
|
||||||
it('renders without checkbox for internal topic', () => {
|
|
||||||
render(setupComponent({ topic: internalTopicPayload }));
|
|
||||||
expect(screen.queryByRole('checkbox')).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders with checkbox for external topic', () => {
|
|
||||||
render(setupComponent({ topic: externalTopicPayload }));
|
|
||||||
|
|
||||||
expect(getCheckbox()).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('triggers the toggleTopicSelected when clicked on the checkbox input', () => {
|
|
||||||
render(setupComponent({ topic: externalTopicPayload }));
|
|
||||||
expect(getCheckbox()).toBeInTheDocument();
|
|
||||||
userEvent.click(getCheckbox());
|
|
||||||
expect(mockToggleTopicSelected).toBeCalledTimes(1);
|
|
||||||
expect(mockToggleTopicSelected).toBeCalledWith(externalTopicPayload.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders correct out of sync replicas number', () => {
|
|
||||||
render(
|
|
||||||
setupComponent({
|
|
||||||
topic: { ...externalTopicPayload, partitions: undefined },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(screen.getAllByRole('cell', { name: '0' }).length).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { render } from 'lib/testHelpers';
|
||||||
|
import {
|
||||||
|
MessagesCell,
|
||||||
|
OutOfSyncReplicasCell,
|
||||||
|
TitleCell,
|
||||||
|
} from 'components/Topics/List/TopicsTableCells';
|
||||||
|
import { TableState } from 'lib/hooks/useTableState';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import { Topic } from 'generated-sources';
|
||||||
|
import { topicsPayload } from 'redux/reducers/topics/__test__/fixtures';
|
||||||
|
|
||||||
|
describe('TopicsTableCells Components', () => {
|
||||||
|
const mockTableState: TableState<Topic, string, never> = {
|
||||||
|
data: topicsPayload,
|
||||||
|
selectedIds: new Set([]),
|
||||||
|
idSelector: jest.fn(),
|
||||||
|
isRowSelectable: jest.fn(),
|
||||||
|
selectedCount: 0,
|
||||||
|
setRowsSelection: jest.fn(),
|
||||||
|
toggleSelection: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('TitleCell Component', () => {
|
||||||
|
it('should check the TitleCell component Render without the internal option', () => {
|
||||||
|
const currentData = topicsPayload[1];
|
||||||
|
render(
|
||||||
|
<TitleCell
|
||||||
|
rowIndex={1}
|
||||||
|
dataItem={currentData}
|
||||||
|
tableState={mockTableState}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(screen.queryByText('IN')).not.toBeInTheDocument();
|
||||||
|
expect(screen.getByText(currentData.name)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the TitleCell component Render without the internal option', () => {
|
||||||
|
const currentData = topicsPayload[0];
|
||||||
|
render(
|
||||||
|
<TitleCell
|
||||||
|
rowIndex={1}
|
||||||
|
dataItem={currentData}
|
||||||
|
tableState={mockTableState}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(screen.getByText('IN')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(currentData.name)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('OutOfSyncReplicasCell Component', () => {
|
||||||
|
it('should check the content of the OutOfSyncReplicasCell to return 0 if no partition is empty array', () => {
|
||||||
|
const currentData = topicsPayload[0];
|
||||||
|
currentData.partitions = [];
|
||||||
|
render(
|
||||||
|
<OutOfSyncReplicasCell
|
||||||
|
rowIndex={1}
|
||||||
|
dataItem={currentData}
|
||||||
|
tableState={mockTableState}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(screen.getByText('0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the content of the OutOfSyncReplicasCell to return 0 if no partition is found', () => {
|
||||||
|
const currentData = topicsPayload[1];
|
||||||
|
currentData.partitions = undefined;
|
||||||
|
render(
|
||||||
|
<OutOfSyncReplicasCell
|
||||||
|
rowIndex={1}
|
||||||
|
dataItem={currentData}
|
||||||
|
tableState={mockTableState}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(screen.getByText('0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the content of the OutOfSyncReplicasCell with the correct partition number', () => {
|
||||||
|
const currentData = topicsPayload[0];
|
||||||
|
const partitionNumber = currentData.partitions?.reduce(
|
||||||
|
(memo, { replicas }) => {
|
||||||
|
const outOfSync = replicas?.filter(({ inSync }) => !inSync);
|
||||||
|
return memo + (outOfSync?.length || 0);
|
||||||
|
},
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
render(
|
||||||
|
<OutOfSyncReplicasCell
|
||||||
|
rowIndex={1}
|
||||||
|
dataItem={currentData}
|
||||||
|
tableState={mockTableState}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
screen.getByText(partitionNumber ? partitionNumber.toString() : '0')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('MessagesCell Component', () => {
|
||||||
|
it('should check the content of the MessagesCell to return 0 if no partition is empty array ', () => {
|
||||||
|
const currentData = topicsPayload[0];
|
||||||
|
currentData.partitions = [];
|
||||||
|
render(
|
||||||
|
<MessagesCell
|
||||||
|
rowIndex={1}
|
||||||
|
dataItem={topicsPayload[0]}
|
||||||
|
tableState={mockTableState}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(screen.getByText('0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the content of the MessagesCell to return 0 if no partition is found', () => {
|
||||||
|
const currentData = topicsPayload[0];
|
||||||
|
currentData.partitions = undefined;
|
||||||
|
render(
|
||||||
|
<MessagesCell
|
||||||
|
rowIndex={1}
|
||||||
|
dataItem={topicsPayload[0]}
|
||||||
|
tableState={mockTableState}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(screen.getByText('0')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the content of the MessagesCell with the correct partition number', () => {
|
||||||
|
const currentData = topicsPayload[0];
|
||||||
|
const partitionNumber = currentData.partitions?.reduce(
|
||||||
|
(memo, { offsetMax, offsetMin }) => {
|
||||||
|
return memo + (offsetMax - offsetMin);
|
||||||
|
},
|
||||||
|
0
|
||||||
|
);
|
||||||
|
render(
|
||||||
|
<MessagesCell
|
||||||
|
rowIndex={1}
|
||||||
|
dataItem={topicsPayload[0]}
|
||||||
|
tableState={mockTableState}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
screen.getByText(partitionNumber ? partitionNumber.toString() : '0')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -122,4 +122,29 @@ describe('New', () => {
|
||||||
expect(mockedHistory.push).toBeCalledTimes(1);
|
expect(mockedHistory.push).toBeCalledTimes(1);
|
||||||
expect(createTopicAPIPathMock.called()).toBeTruthy();
|
expect(createTopicAPIPathMock.called()).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('submits valid form that result in an error', async () => {
|
||||||
|
const createTopicAPIPathMock = fetchMock.postOnce(
|
||||||
|
createTopicAPIPath,
|
||||||
|
{ throws: new Error('Something went wrong') },
|
||||||
|
{
|
||||||
|
body: createTopicPayload,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const mocked = createMemoryHistory({
|
||||||
|
initialEntries: [clusterTopicNewPath(clusterName)],
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.spyOn(mocked, 'push');
|
||||||
|
renderComponent(mocked);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.type(screen.getByPlaceholderText('Topic Name'), topicName);
|
||||||
|
userEvent.click(screen.getByText(/submit/i));
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createTopicAPIPathMock.called()).toBeTruthy();
|
||||||
|
expect(mocked.push).toBeCalledTimes(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -257,7 +257,9 @@ export const ConfirmDeletionText = styled.h3`
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const MessageLoading = styled.div<MessageLoadingProps>`
|
export const MessageLoading = styled.div.attrs({
|
||||||
|
role: 'contentLoader',
|
||||||
|
})<MessageLoadingProps>`
|
||||||
color: ${({ theme }) => theme.heading.h3.color};
|
color: ${({ theme }) => theme.heading.h3.color};
|
||||||
font-size: ${({ theme }) => theme.heading.h3.fontSize};
|
font-size: ${({ theme }) => theme.heading.h3.fontSize};
|
||||||
display: ${(props) => (props.isLive ? 'flex' : 'none')};
|
display: ${(props) => (props.isLive ? 'flex' : 'none')};
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { render } from 'lib/testHelpers';
|
||||||
|
import * as S from 'components/Topics/Topic/Details/Messages/Filters/Filters.styled';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import theme from 'theme/theme';
|
||||||
|
|
||||||
|
describe('Filters Styled components', () => {
|
||||||
|
describe('MessageLoading component', () => {
|
||||||
|
it('should check the styling during live', () => {
|
||||||
|
render(<S.MessageLoading isLive />);
|
||||||
|
expect(screen.getByRole('contentLoader')).toHaveStyle({
|
||||||
|
color: theme.heading.h3.color,
|
||||||
|
'font-size': theme.heading.h3.fontSize,
|
||||||
|
display: 'flex',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the styling during not live', () => {
|
||||||
|
render(<S.MessageLoading isLive={false} />);
|
||||||
|
expect(screen.getByRole('contentLoader', { hidden: true })).toHaveStyle({
|
||||||
|
color: theme.heading.h3.color,
|
||||||
|
'font-size': theme.heading.h3.fontSize,
|
||||||
|
display: 'none',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -20,7 +20,11 @@ const StyledDataCell = styled.td`
|
||||||
min-width: 350px;
|
min-width: 350px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Message: React.FC<{ message: TopicMessage }> = ({
|
export interface Props {
|
||||||
|
message: TopicMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Message: React.FC<Props> = ({
|
||||||
message: {
|
message: {
|
||||||
timestamp,
|
timestamp,
|
||||||
timestampType,
|
timestampType,
|
||||||
|
|
|
@ -1,29 +1,92 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { TopicMessageTimestampTypeEnum } from 'generated-sources';
|
import { TopicMessage, TopicMessageTimestampTypeEnum } from 'generated-sources';
|
||||||
import Message from 'components/Topics/Topic/Details/Messages/Message';
|
import Message, {
|
||||||
|
Props,
|
||||||
|
} from 'components/Topics/Topic/Details/Messages/Message';
|
||||||
import { screen } from '@testing-library/react';
|
import { screen } from '@testing-library/react';
|
||||||
import { render } from 'lib/testHelpers';
|
import { render } from 'lib/testHelpers';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
|
const messageContentText = 'messageContentText';
|
||||||
|
|
||||||
|
jest.mock(
|
||||||
|
'components/Topics/Topic/Details/Messages/MessageContent/MessageContent',
|
||||||
|
() => () =>
|
||||||
|
(
|
||||||
|
<tr>
|
||||||
|
<td>{messageContentText}</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
describe('Message component', () => {
|
describe('Message component', () => {
|
||||||
it('shows the data in the table row', async () => {
|
const mockMessage: TopicMessage = {
|
||||||
render(
|
timestamp: new Date(),
|
||||||
|
timestampType: TopicMessageTimestampTypeEnum.CREATE_TIME,
|
||||||
|
offset: 0,
|
||||||
|
key: 'test-key',
|
||||||
|
partition: 6,
|
||||||
|
content: '{"data": "test"}',
|
||||||
|
headers: { header: 'test' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderComponent = (
|
||||||
|
props: Partial<Props> = {
|
||||||
|
message: mockMessage,
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
return render(
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<Message
|
<Message message={props.message || mockMessage} />
|
||||||
message={{
|
|
||||||
timestamp: new Date(0),
|
|
||||||
timestampType: TopicMessageTimestampTypeEnum.CREATE_TIME,
|
|
||||||
offset: 0,
|
|
||||||
key: 'test-key',
|
|
||||||
partition: 0,
|
|
||||||
content: '{"data": "test"}',
|
|
||||||
headers: { header: 'test' },
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
);
|
);
|
||||||
expect(screen.getByText('{"data": "test"}')).toBeInTheDocument();
|
};
|
||||||
expect(screen.getByText('test-key')).toBeInTheDocument();
|
|
||||||
|
it('shows the data in the table row', () => {
|
||||||
|
renderComponent();
|
||||||
|
expect(screen.getByText(mockMessage.content as string)).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(mockMessage.key as string)).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText(
|
||||||
|
dayjs(mockMessage.timestamp).format('MM.DD.YYYY HH:mm:ss')
|
||||||
|
)
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(screen.getByText(mockMessage.offset.toString())).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText(mockMessage.partition.toString())
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('check the useDataSaver functionality', () => {
|
||||||
|
const props = { message: { ...mockMessage } };
|
||||||
|
delete props.message.content;
|
||||||
|
renderComponent(props);
|
||||||
|
expect(
|
||||||
|
screen.queryByText(mockMessage.content as string)
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the dropdown being visible during hover', () => {
|
||||||
|
renderComponent();
|
||||||
|
const text = 'Save as a file';
|
||||||
|
const trElement = screen.getByRole('row');
|
||||||
|
expect(screen.queryByText(text)).not.toBeInTheDocument();
|
||||||
|
|
||||||
|
userEvent.hover(trElement);
|
||||||
|
expect(screen.getByText(text)).toBeInTheDocument();
|
||||||
|
|
||||||
|
userEvent.unhover(trElement);
|
||||||
|
expect(screen.queryByText(text)).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check open Message Content functionality', () => {
|
||||||
|
renderComponent();
|
||||||
|
const messageToggleIcon = screen.getByRole('button', { hidden: true });
|
||||||
|
expect(screen.queryByText(messageContentText)).not.toBeInTheDocument();
|
||||||
|
userEvent.click(messageToggleIcon);
|
||||||
|
expect(screen.getByText(messageContentText)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { screen } from '@testing-library/react';
|
import { screen, waitFor } from '@testing-library/react';
|
||||||
import { render, EventSourceMock } from 'lib/testHelpers';
|
import { render, EventSourceMock } from 'lib/testHelpers';
|
||||||
import Messages, {
|
import Messages, {
|
||||||
SeekDirectionOptions,
|
SeekDirectionOptions,
|
||||||
|
@ -40,7 +40,7 @@ describe('Messages', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check the SeekDirection select changes', () => {
|
it('should check the SeekDirection select changes with live option', async () => {
|
||||||
const seekDirectionSelect = screen.getAllByRole('listbox')[1];
|
const seekDirectionSelect = screen.getAllByRole('listbox')[1];
|
||||||
const seekDirectionOption = screen.getAllByRole('option')[1];
|
const seekDirectionOption = screen.getAllByRole('option')[1];
|
||||||
|
|
||||||
|
@ -50,17 +50,28 @@ describe('Messages', () => {
|
||||||
|
|
||||||
const labelValue1 = SeekDirectionOptions[1].label;
|
const labelValue1 = SeekDirectionOptions[1].label;
|
||||||
userEvent.click(seekDirectionSelect);
|
userEvent.click(seekDirectionSelect);
|
||||||
userEvent.selectOptions(seekDirectionSelect, [
|
userEvent.selectOptions(seekDirectionSelect, [labelValue1]);
|
||||||
SeekDirectionOptions[1].label,
|
|
||||||
]);
|
|
||||||
expect(seekDirectionOption).toHaveTextContent(labelValue1);
|
expect(seekDirectionOption).toHaveTextContent(labelValue1);
|
||||||
|
|
||||||
const labelValue0 = SeekDirectionOptions[0].label;
|
const labelValue0 = SeekDirectionOptions[0].label;
|
||||||
userEvent.click(seekDirectionSelect);
|
userEvent.click(seekDirectionSelect);
|
||||||
userEvent.selectOptions(seekDirectionSelect, [
|
userEvent.selectOptions(seekDirectionSelect, [labelValue0]);
|
||||||
SeekDirectionOptions[0].label,
|
|
||||||
]);
|
|
||||||
expect(seekDirectionOption).toHaveTextContent(labelValue0);
|
expect(seekDirectionOption).toHaveTextContent(labelValue0);
|
||||||
|
|
||||||
|
const liveOptionConf = SeekDirectionOptions[2];
|
||||||
|
const labelValue2 = liveOptionConf.label;
|
||||||
|
userEvent.click(seekDirectionSelect);
|
||||||
|
const liveModeLi = screen.getByRole(
|
||||||
|
(role, element) =>
|
||||||
|
role === 'option' &&
|
||||||
|
element?.getAttribute('value') === liveOptionConf.value
|
||||||
|
);
|
||||||
|
userEvent.selectOptions(seekDirectionSelect, [liveModeLi]);
|
||||||
|
expect(seekDirectionOption).toHaveTextContent(labelValue2);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole('contentLoader')).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,23 @@ import { screen } from '@testing-library/react';
|
||||||
import { render } from 'lib/testHelpers';
|
import { render } from 'lib/testHelpers';
|
||||||
import MessagesTable from 'components/Topics/Topic/Details/Messages/MessagesTable';
|
import MessagesTable from 'components/Topics/Topic/Details/Messages/MessagesTable';
|
||||||
import { Router } from 'react-router';
|
import { Router } from 'react-router';
|
||||||
import { createMemoryHistory } from 'history';
|
import { createMemoryHistory, MemoryHistory } from 'history';
|
||||||
import { SeekDirection, SeekType } from 'generated-sources';
|
import { SeekDirection, SeekType, TopicMessage } from 'generated-sources';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import TopicMessagesContext, {
|
import TopicMessagesContext, {
|
||||||
ContextProps,
|
ContextProps,
|
||||||
} from 'components/contexts/TopicMessagesContext';
|
} from 'components/contexts/TopicMessagesContext';
|
||||||
|
import {
|
||||||
|
topicMessagePayload,
|
||||||
|
topicMessagesMetaPayload,
|
||||||
|
} from 'redux/reducers/topicMessages/__test__/fixtures';
|
||||||
|
|
||||||
|
const mockTopicsMessages: TopicMessage[] = [{ ...topicMessagePayload }];
|
||||||
|
|
||||||
describe('MessagesTable', () => {
|
describe('MessagesTable', () => {
|
||||||
const searchParams = new URLSearchParams(
|
const seekToResult = '&seekTo=0::9';
|
||||||
`?filterQueryType=STRING_CONTAINS&attempt=0&limit=100&seekDirection=${SeekDirection.FORWARD}&seekType=${SeekType.OFFSET}&seekTo=0::9`
|
const searchParamsValue = `?filterQueryType=STRING_CONTAINS&attempt=0&limit=100&seekDirection=${SeekDirection.FORWARD}&seekType=${SeekType.OFFSET}${seekToResult}`;
|
||||||
);
|
const searchParams = new URLSearchParams(searchParamsValue);
|
||||||
const contextValue: ContextProps = {
|
const contextValue: ContextProps = {
|
||||||
isLive: false,
|
isLive: false,
|
||||||
seekDirection: SeekDirection.FORWARD,
|
seekDirection: SeekDirection.FORWARD,
|
||||||
|
@ -23,18 +29,32 @@ describe('MessagesTable', () => {
|
||||||
|
|
||||||
const setUpComponent = (
|
const setUpComponent = (
|
||||||
params: URLSearchParams = searchParams,
|
params: URLSearchParams = searchParams,
|
||||||
ctx: ContextProps = contextValue
|
ctx: ContextProps = contextValue,
|
||||||
|
messages: TopicMessage[] = [],
|
||||||
|
customHistory?: MemoryHistory
|
||||||
) => {
|
) => {
|
||||||
const history = createMemoryHistory();
|
const history =
|
||||||
history.push({
|
customHistory ||
|
||||||
search: params.toString(),
|
createMemoryHistory({
|
||||||
});
|
initialEntries: [params.toString()],
|
||||||
|
});
|
||||||
return render(
|
return render(
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<TopicMessagesContext.Provider value={ctx}>
|
<TopicMessagesContext.Provider value={ctx}>
|
||||||
<MessagesTable />
|
<MessagesTable />
|
||||||
</TopicMessagesContext.Provider>
|
</TopicMessagesContext.Provider>
|
||||||
</Router>
|
</Router>,
|
||||||
|
{
|
||||||
|
preloadedState: {
|
||||||
|
topicMessages: {
|
||||||
|
messages,
|
||||||
|
meta: {
|
||||||
|
...topicMessagesMetaPayload,
|
||||||
|
},
|
||||||
|
isFetching: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,5 +89,55 @@ describe('MessagesTable', () => {
|
||||||
setUpComponent(searchParams, { ...contextValue, isLive: true });
|
setUpComponent(searchParams, { ...contextValue, isLive: true });
|
||||||
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should check the seekTo parameter in the url if no seekTo is found should noy change the history', () => {
|
||||||
|
const customSearchParam = new URLSearchParams(searchParamsValue);
|
||||||
|
|
||||||
|
const mockedHistory = createMemoryHistory({
|
||||||
|
initialEntries: [customSearchParam.toString()],
|
||||||
|
});
|
||||||
|
jest.spyOn(mockedHistory, 'push');
|
||||||
|
|
||||||
|
setUpComponent(customSearchParam, contextValue, [], mockedHistory);
|
||||||
|
|
||||||
|
userEvent.click(screen.getByRole('button', { name: 'Next' }));
|
||||||
|
expect(mockedHistory.push).toHaveBeenCalledWith({
|
||||||
|
search: searchParamsValue.replace(seekToResult, '&seekTo=0%3A%3A1'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the seekTo parameter in the url if no seekTo is found should change the history', () => {
|
||||||
|
const customSearchParam = new URLSearchParams(
|
||||||
|
searchParamsValue.replace(seekToResult, '')
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockedHistory = createMemoryHistory({
|
||||||
|
initialEntries: [customSearchParam.toString()],
|
||||||
|
});
|
||||||
|
jest.spyOn(mockedHistory, 'push');
|
||||||
|
|
||||||
|
setUpComponent(
|
||||||
|
customSearchParam,
|
||||||
|
{ ...contextValue, searchParams: customSearchParam },
|
||||||
|
[],
|
||||||
|
mockedHistory
|
||||||
|
);
|
||||||
|
|
||||||
|
userEvent.click(screen.getByRole('button', { name: 'Next' }));
|
||||||
|
expect(mockedHistory.push).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should render Messages table with data', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setUpComponent(searchParams, { ...contextValue }, mockTopicsMessages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the rendering of the messages', () => {
|
||||||
|
expect(screen.queryByText(/No messages found/i)).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText(mockTopicsMessages[0].content as string)
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -49,8 +49,8 @@ describe('Overview', () => {
|
||||||
underReplicatedPartitions?: number,
|
underReplicatedPartitions?: number,
|
||||||
inSyncReplicas?: number,
|
inSyncReplicas?: number,
|
||||||
replicas?: number
|
replicas?: number
|
||||||
) =>
|
) => {
|
||||||
render(
|
return render(
|
||||||
<ClusterContext.Provider value={contextValues}>
|
<ClusterContext.Provider value={contextValues}>
|
||||||
<Overview
|
<Overview
|
||||||
underReplicatedPartitions={underReplicatedPartitions}
|
underReplicatedPartitions={underReplicatedPartitions}
|
||||||
|
@ -60,6 +60,11 @@ describe('Overview', () => {
|
||||||
/>
|
/>
|
||||||
</ClusterContext.Provider>
|
</ClusterContext.Provider>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
mockClearTopicMessages.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
describe('when it has internal flag', () => {
|
describe('when it has internal flag', () => {
|
||||||
it('does not render the Action button a Topic', () => {
|
it('does not render the Action button a Topic', () => {
|
||||||
|
@ -116,4 +121,49 @@ describe('Overview', () => {
|
||||||
|
|
||||||
expect(mockClearTopicMessages).toHaveBeenCalledTimes(1);
|
expect(mockClearTopicMessages).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when the table partition dropdown appearance', () => {
|
||||||
|
it('should check if the dropdown is not present when it is readOnly', () => {
|
||||||
|
setupComponent(
|
||||||
|
{
|
||||||
|
...defaultProps,
|
||||||
|
partitions: mockPartitions,
|
||||||
|
internal: true,
|
||||||
|
cleanUpPolicy: CleanUpPolicy.DELETE,
|
||||||
|
},
|
||||||
|
{ ...defaultContextValues, isReadOnly: true }
|
||||||
|
);
|
||||||
|
expect(screen.queryByText('Clear Messages')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if the dropdown is not present when it is internal', () => {
|
||||||
|
setupComponent({
|
||||||
|
...defaultProps,
|
||||||
|
partitions: mockPartitions,
|
||||||
|
internal: true,
|
||||||
|
cleanUpPolicy: CleanUpPolicy.DELETE,
|
||||||
|
});
|
||||||
|
expect(screen.queryByText('Clear Messages')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if the dropdown is not present when cleanUpPolicy is not DELETE', () => {
|
||||||
|
setupComponent({
|
||||||
|
...defaultProps,
|
||||||
|
partitions: mockPartitions,
|
||||||
|
internal: false,
|
||||||
|
cleanUpPolicy: CleanUpPolicy.COMPACT,
|
||||||
|
});
|
||||||
|
expect(screen.queryByText('Clear Messages')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if the dropdown action to be in visible', () => {
|
||||||
|
setupComponent({
|
||||||
|
...defaultProps,
|
||||||
|
partitions: mockPartitions,
|
||||||
|
internal: false,
|
||||||
|
cleanUpPolicy: CleanUpPolicy.DELETE,
|
||||||
|
});
|
||||||
|
expect(screen.getByText('Clear Messages')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import { render } from 'lib/testHelpers';
|
||||||
|
import { ConfigItemCell } from 'components/Topics/Topic/Details/Settings/Settings.styled';
|
||||||
|
|
||||||
|
describe('Settings styled Components', () => {
|
||||||
|
describe('ConfigItemCell Component', () => {
|
||||||
|
const renderComponent = (
|
||||||
|
props: Partial<{ $hasCustomValue: boolean }> = {}
|
||||||
|
) => {
|
||||||
|
return render(
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<ConfigItemCell
|
||||||
|
$hasCustomValue={
|
||||||
|
'$hasCustomValue' in props ? !!props.$hasCustomValue : false
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
it('should check the true rendering ConfigItemList', () => {
|
||||||
|
renderComponent({ $hasCustomValue: true });
|
||||||
|
expect(screen.getByRole('cell')).toHaveStyleRule(
|
||||||
|
'font-weight',
|
||||||
|
'500 !important'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the true rendering ConfigItemList', () => {
|
||||||
|
renderComponent();
|
||||||
|
expect(screen.getByRole('cell')).toHaveStyleRule(
|
||||||
|
'font-weight',
|
||||||
|
'400 !important'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,287 @@
|
||||||
|
import React from 'react';
|
||||||
|
import DangerZone, {
|
||||||
|
Props,
|
||||||
|
} from 'components/Topics/Topic/Edit/DangerZone/DangerZone';
|
||||||
|
import { screen, waitFor, within } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { render } from 'lib/testHelpers';
|
||||||
|
import {
|
||||||
|
topicName,
|
||||||
|
clusterName,
|
||||||
|
} from 'components/Topics/Topic/Edit/__test__/fixtures';
|
||||||
|
|
||||||
|
const defaultPartitions = 3;
|
||||||
|
const defaultReplicationFactor = 3;
|
||||||
|
|
||||||
|
const renderComponent = (props?: Partial<Props>) => {
|
||||||
|
return render(
|
||||||
|
<DangerZone
|
||||||
|
clusterName={clusterName}
|
||||||
|
topicName={topicName}
|
||||||
|
defaultPartitions={defaultPartitions}
|
||||||
|
defaultReplicationFactor={defaultReplicationFactor}
|
||||||
|
partitionsCountIncreased={false}
|
||||||
|
replicationFactorUpdated={false}
|
||||||
|
updateTopicPartitionsCount={jest.fn()}
|
||||||
|
updateTopicReplicationFactor={jest.fn()}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickOnDialogSubmitButton = () => {
|
||||||
|
userEvent.click(
|
||||||
|
within(screen.getByRole('dialog')).getByRole('button', {
|
||||||
|
name: 'Submit',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkDialogThenPressCancel = async () => {
|
||||||
|
const dialog = screen.getByRole('dialog');
|
||||||
|
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.click(within(dialog).getByText(/cancel/i));
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('DangerZone', () => {
|
||||||
|
it('renders the component', () => {
|
||||||
|
renderComponent();
|
||||||
|
|
||||||
|
const numberOfPartitionsEditForm = screen.getByRole('form', {
|
||||||
|
name: 'Edit number of partitions',
|
||||||
|
});
|
||||||
|
expect(numberOfPartitionsEditForm).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
within(numberOfPartitionsEditForm).getByRole('spinbutton', {
|
||||||
|
name: 'Number of partitions *',
|
||||||
|
})
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
within(numberOfPartitionsEditForm).getByRole('button', { name: 'Submit' })
|
||||||
|
).toBeInTheDocument();
|
||||||
|
|
||||||
|
const replicationFactorEditForm = screen.getByRole('form', {
|
||||||
|
name: 'Edit replication factor',
|
||||||
|
});
|
||||||
|
expect(replicationFactorEditForm).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
within(replicationFactorEditForm).getByRole('spinbutton', {
|
||||||
|
name: 'Replication Factor *',
|
||||||
|
})
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
within(replicationFactorEditForm).getByRole('button', { name: 'Submit' })
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls updateTopicPartitionsCount', async () => {
|
||||||
|
const mockUpdateTopicPartitionsCount = jest.fn();
|
||||||
|
renderComponent({
|
||||||
|
updateTopicPartitionsCount: mockUpdateTopicPartitionsCount,
|
||||||
|
});
|
||||||
|
const numberOfPartitionsEditForm = screen.getByRole('form', {
|
||||||
|
name: 'Edit number of partitions',
|
||||||
|
});
|
||||||
|
|
||||||
|
userEvent.type(
|
||||||
|
within(numberOfPartitionsEditForm).getByRole('spinbutton'),
|
||||||
|
'4'
|
||||||
|
);
|
||||||
|
userEvent.click(within(numberOfPartitionsEditForm).getByRole('button'));
|
||||||
|
|
||||||
|
await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument());
|
||||||
|
await waitFor(() => clickOnDialogSubmitButton());
|
||||||
|
|
||||||
|
expect(mockUpdateTopicPartitionsCount).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls updateTopicReplicationFactor', async () => {
|
||||||
|
const mockUpdateTopicReplicationFactor = jest.fn();
|
||||||
|
renderComponent({
|
||||||
|
updateTopicReplicationFactor: mockUpdateTopicReplicationFactor,
|
||||||
|
});
|
||||||
|
|
||||||
|
const replicationFactorEditForm = screen.getByRole('form', {
|
||||||
|
name: 'Edit replication factor',
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
within(replicationFactorEditForm).getByRole('spinbutton', {
|
||||||
|
name: 'Replication Factor *',
|
||||||
|
})
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
within(replicationFactorEditForm).getByRole('button', { name: 'Submit' })
|
||||||
|
).toBeInTheDocument();
|
||||||
|
|
||||||
|
userEvent.type(
|
||||||
|
within(replicationFactorEditForm).getByRole('spinbutton'),
|
||||||
|
'4'
|
||||||
|
);
|
||||||
|
userEvent.click(within(replicationFactorEditForm).getByRole('button'));
|
||||||
|
|
||||||
|
await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument());
|
||||||
|
await waitFor(() => clickOnDialogSubmitButton());
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockUpdateTopicReplicationFactor).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should view the validation error when partition value is lower than the default passed or empty', async () => {
|
||||||
|
renderComponent();
|
||||||
|
const partitionInput = screen.getByPlaceholderText('Number of partitions');
|
||||||
|
const partitionInputSubmitBtn = screen.getAllByText(/submit/i)[0];
|
||||||
|
const value = (defaultPartitions - 4).toString();
|
||||||
|
|
||||||
|
expect(partitionInputSubmitBtn).toBeDisabled();
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.clear(partitionInput);
|
||||||
|
userEvent.type(partitionInput, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(partitionInput).toHaveValue(+value);
|
||||||
|
expect(partitionInputSubmitBtn).toBeEnabled();
|
||||||
|
userEvent.click(partitionInputSubmitBtn);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
screen.getByText(/You can only increase the number of partitions!/i)
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.clear(partitionInput);
|
||||||
|
});
|
||||||
|
expect(screen.getByText(/are required/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should view the validation error when Replication Facto value is lower than the default passed or empty', async () => {
|
||||||
|
renderComponent();
|
||||||
|
const replicatorFactorInput =
|
||||||
|
screen.getByPlaceholderText('Replication Factor');
|
||||||
|
const replicatorFactorInputSubmitBtn = screen.getAllByText(/submit/i)[1];
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.clear(replicatorFactorInput);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(replicatorFactorInputSubmitBtn).toBeEnabled();
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.click(replicatorFactorInputSubmitBtn);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(screen.getByText(/are required/i)).toBeInTheDocument();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.type(replicatorFactorInput, '1');
|
||||||
|
});
|
||||||
|
expect(screen.queryByText(/are required/i)).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close any popup if the partitionsCount is Increased ', () => {
|
||||||
|
renderComponent({ partitionsCountIncreased: true });
|
||||||
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close any popup if the replicationFactor is Updated', () => {
|
||||||
|
renderComponent({ replicationFactorUpdated: true });
|
||||||
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should already opened Confirmation popup if partitionsCount is Increased', async () => {
|
||||||
|
const { rerender } = renderComponent();
|
||||||
|
const partitionInput = screen.getByPlaceholderText('Number of partitions');
|
||||||
|
const partitionInputSubmitBtn = screen.getAllByText(/submit/i)[0];
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.type(partitionInput, '5');
|
||||||
|
});
|
||||||
|
|
||||||
|
userEvent.click(partitionInputSubmitBtn);
|
||||||
|
await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument());
|
||||||
|
rerender(
|
||||||
|
<DangerZone
|
||||||
|
clusterName={clusterName}
|
||||||
|
topicName={topicName}
|
||||||
|
defaultPartitions={defaultPartitions}
|
||||||
|
defaultReplicationFactor={defaultReplicationFactor}
|
||||||
|
partitionsCountIncreased
|
||||||
|
replicationFactorUpdated={false}
|
||||||
|
updateTopicPartitionsCount={jest.fn()}
|
||||||
|
updateTopicReplicationFactor={jest.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should already opened Confirmation popup if replicationFactor is Increased', async () => {
|
||||||
|
const { rerender } = renderComponent();
|
||||||
|
const replicatorFactorInput =
|
||||||
|
screen.getByPlaceholderText('Replication Factor');
|
||||||
|
const replicatorFactorInputSubmitBtn = screen.getAllByText(/submit/i)[1];
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.type(replicatorFactorInput, '5');
|
||||||
|
});
|
||||||
|
|
||||||
|
userEvent.click(replicatorFactorInputSubmitBtn);
|
||||||
|
await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument());
|
||||||
|
rerender(
|
||||||
|
<DangerZone
|
||||||
|
clusterName={clusterName}
|
||||||
|
topicName={topicName}
|
||||||
|
defaultPartitions={defaultPartitions}
|
||||||
|
defaultReplicationFactor={defaultReplicationFactor}
|
||||||
|
partitionsCountIncreased={false}
|
||||||
|
replicationFactorUpdated
|
||||||
|
updateTopicPartitionsCount={jest.fn()}
|
||||||
|
updateTopicReplicationFactor={jest.fn()}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close the partitions dialog if he cancel button is pressed', async () => {
|
||||||
|
renderComponent();
|
||||||
|
const partitionInput = screen.getByPlaceholderText('Number of partitions');
|
||||||
|
const partitionInputSubmitBtn = screen.getAllByText(/submit/i)[0];
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.type(partitionInput, '5');
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.click(partitionInputSubmitBtn);
|
||||||
|
});
|
||||||
|
|
||||||
|
await checkDialogThenPressCancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should close the replicator dialog if he cancel button is pressed', async () => {
|
||||||
|
renderComponent();
|
||||||
|
const replicatorFactorInput =
|
||||||
|
screen.getByPlaceholderText('Replication Factor');
|
||||||
|
const replicatorFactorInputSubmitBtn = screen.getAllByText(/submit/i)[1];
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.type(replicatorFactorInput, '5');
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.click(replicatorFactorInputSubmitBtn);
|
||||||
|
});
|
||||||
|
|
||||||
|
await checkDialogThenPressCancel();
|
||||||
|
});
|
||||||
|
});
|
|
@ -46,7 +46,7 @@ const EditWrapperStyled = styled.div`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const DEFAULTS = {
|
export const DEFAULTS = {
|
||||||
partitions: 1,
|
partitions: 1,
|
||||||
replicationFactor: 1,
|
replicationFactor: 1,
|
||||||
minInSyncReplicas: 1,
|
minInSyncReplicas: 1,
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import DangerZone, {
|
|
||||||
Props,
|
|
||||||
} from 'components/Topics/Topic/Edit/DangerZone/DangerZone';
|
|
||||||
import { screen, waitFor, within } from '@testing-library/react';
|
|
||||||
import userEvent from '@testing-library/user-event';
|
|
||||||
import { render } from 'lib/testHelpers';
|
|
||||||
|
|
||||||
import { topicName, clusterName } from './fixtures';
|
|
||||||
|
|
||||||
const renderComponent = (props?: Partial<Props>) =>
|
|
||||||
render(
|
|
||||||
<DangerZone
|
|
||||||
clusterName={clusterName}
|
|
||||||
topicName={topicName}
|
|
||||||
defaultPartitions={3}
|
|
||||||
defaultReplicationFactor={3}
|
|
||||||
partitionsCountIncreased={false}
|
|
||||||
replicationFactorUpdated={false}
|
|
||||||
updateTopicPartitionsCount={jest.fn()}
|
|
||||||
updateTopicReplicationFactor={jest.fn()}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const clickOnDialogSubmitButton = () => {
|
|
||||||
userEvent.click(
|
|
||||||
within(screen.getByRole('dialog')).getByRole('button', {
|
|
||||||
name: 'Submit',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
describe('DangerZone', () => {
|
|
||||||
it('renders', () => {
|
|
||||||
renderComponent();
|
|
||||||
|
|
||||||
const numberOfPartitionsEditForm = screen.getByRole('form', {
|
|
||||||
name: 'Edit number of partitions',
|
|
||||||
});
|
|
||||||
expect(numberOfPartitionsEditForm).toBeInTheDocument();
|
|
||||||
expect(
|
|
||||||
within(numberOfPartitionsEditForm).getByRole('spinbutton', {
|
|
||||||
name: 'Number of partitions *',
|
|
||||||
})
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(
|
|
||||||
within(numberOfPartitionsEditForm).getByRole('button', { name: 'Submit' })
|
|
||||||
).toBeInTheDocument();
|
|
||||||
|
|
||||||
const replicationFactorEditForm = screen.getByRole('form', {
|
|
||||||
name: 'Edit replication factor',
|
|
||||||
});
|
|
||||||
expect(replicationFactorEditForm).toBeInTheDocument();
|
|
||||||
expect(
|
|
||||||
within(replicationFactorEditForm).getByRole('spinbutton', {
|
|
||||||
name: 'Replication Factor *',
|
|
||||||
})
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(
|
|
||||||
within(replicationFactorEditForm).getByRole('button', { name: 'Submit' })
|
|
||||||
).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls updateTopicPartitionsCount', async () => {
|
|
||||||
const mockUpdateTopicPartitionsCount = jest.fn();
|
|
||||||
renderComponent({
|
|
||||||
updateTopicPartitionsCount: mockUpdateTopicPartitionsCount,
|
|
||||||
});
|
|
||||||
const numberOfPartitionsEditForm = screen.getByRole('form', {
|
|
||||||
name: 'Edit number of partitions',
|
|
||||||
});
|
|
||||||
|
|
||||||
userEvent.type(
|
|
||||||
within(numberOfPartitionsEditForm).getByRole('spinbutton'),
|
|
||||||
'4'
|
|
||||||
);
|
|
||||||
userEvent.click(within(numberOfPartitionsEditForm).getByRole('button'));
|
|
||||||
|
|
||||||
await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument());
|
|
||||||
await waitFor(() => clickOnDialogSubmitButton());
|
|
||||||
|
|
||||||
expect(mockUpdateTopicPartitionsCount).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls updateTopicReplicationFactor', async () => {
|
|
||||||
const mockUpdateTopicReplicationFactor = jest.fn();
|
|
||||||
renderComponent({
|
|
||||||
updateTopicReplicationFactor: mockUpdateTopicReplicationFactor,
|
|
||||||
});
|
|
||||||
|
|
||||||
const replicationFactorEditForm = screen.getByRole('form', {
|
|
||||||
name: 'Edit replication factor',
|
|
||||||
});
|
|
||||||
expect(
|
|
||||||
within(replicationFactorEditForm).getByRole('spinbutton', {
|
|
||||||
name: 'Replication Factor *',
|
|
||||||
})
|
|
||||||
).toBeInTheDocument();
|
|
||||||
expect(
|
|
||||||
within(replicationFactorEditForm).getByRole('button', { name: 'Submit' })
|
|
||||||
).toBeInTheDocument();
|
|
||||||
|
|
||||||
userEvent.type(
|
|
||||||
within(replicationFactorEditForm).getByRole('spinbutton'),
|
|
||||||
'4'
|
|
||||||
);
|
|
||||||
userEvent.click(within(replicationFactorEditForm).getByRole('button'));
|
|
||||||
|
|
||||||
await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument());
|
|
||||||
await waitFor(() => clickOnDialogSubmitButton());
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(mockUpdateTopicReplicationFactor).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,27 +1,39 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Edit, { Props } from 'components/Topics/Topic/Edit/Edit';
|
import Edit, { DEFAULTS, Props } from 'components/Topics/Topic/Edit/Edit';
|
||||||
import { screen } from '@testing-library/react';
|
import { screen, waitFor } from '@testing-library/react';
|
||||||
import { render } from 'lib/testHelpers';
|
import { render } from 'lib/testHelpers';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { Router } from 'react-router-dom';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
|
import { clusterTopicPath, clusterTopicsPath } from 'lib/paths';
|
||||||
|
|
||||||
import { topicName, clusterName, topicWithInfo } from './fixtures';
|
import { topicName, clusterName, topicWithInfo } from './fixtures';
|
||||||
|
|
||||||
const renderComponent = (props?: Partial<Props>) =>
|
const historyMock = createMemoryHistory();
|
||||||
|
|
||||||
|
const renderComponent = (props: Partial<Props> = {}, history = historyMock) =>
|
||||||
render(
|
render(
|
||||||
<Edit
|
<Router history={history}>
|
||||||
clusterName={clusterName}
|
<Edit
|
||||||
topicName={topicName}
|
clusterName={props.clusterName || clusterName}
|
||||||
topic={topicWithInfo}
|
topicName={props.topicName || topicName}
|
||||||
isFetched
|
topic={'topic' in props ? props.topic : topicWithInfo}
|
||||||
isTopicUpdated={false}
|
isFetched={'isFetched' in props ? !!props.isFetched : true}
|
||||||
fetchTopicConfig={jest.fn()}
|
isTopicUpdated={
|
||||||
updateTopic={jest.fn()}
|
'isTopicUpdated' in props ? !!props.isTopicUpdated : false
|
||||||
updateTopicPartitionsCount={jest.fn()}
|
}
|
||||||
{...props}
|
fetchTopicConfig={jest.fn()}
|
||||||
/>
|
updateTopic={props.updateTopic || jest.fn()}
|
||||||
|
updateTopicPartitionsCount={
|
||||||
|
props.updateTopicPartitionsCount || jest.fn()
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</Router>
|
||||||
);
|
);
|
||||||
|
|
||||||
describe('DangerZone', () => {
|
describe('Edit Component', () => {
|
||||||
it('renders', () => {
|
it('renders the Edit Component', () => {
|
||||||
renderComponent();
|
renderComponent();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -31,4 +43,112 @@ describe('DangerZone', () => {
|
||||||
screen.getByRole('heading', { name: `Danger Zone` })
|
screen.getByRole('heading', { name: `Danger Zone` })
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should check Edit component renders null is not rendered when topic is not passed', () => {
|
||||||
|
renderComponent({ topic: undefined });
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('heading', { name: `Edit ${topicName}` })
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('heading', { name: `Danger Zone` })
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check Edit component renders null is not isFetched is false', () => {
|
||||||
|
renderComponent({ isFetched: false });
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('heading', { name: `Edit ${topicName}` })
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('heading', { name: `Danger Zone` })
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check Edit component renders null is not topic config is not passed is false', () => {
|
||||||
|
const modifiedTopic = { ...topicWithInfo };
|
||||||
|
modifiedTopic.config = undefined;
|
||||||
|
renderComponent({ topic: modifiedTopic });
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('heading', { name: `Edit ${topicName}` })
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('heading', { name: `Danger Zone` })
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Edit Component with its topic default and modified values', () => {
|
||||||
|
it('should check the default partitions value in the DangerZone', () => {
|
||||||
|
renderComponent({
|
||||||
|
topic: { ...topicWithInfo, partitionCount: undefined },
|
||||||
|
});
|
||||||
|
expect(screen.getByPlaceholderText('Number of partitions')).toHaveValue(
|
||||||
|
DEFAULTS.partitions
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the default partitions value in the DangerZone', () => {
|
||||||
|
renderComponent({
|
||||||
|
topic: { ...topicWithInfo, replicationFactor: undefined },
|
||||||
|
});
|
||||||
|
expect(screen.getByPlaceholderText('Replication Factor')).toHaveValue(
|
||||||
|
DEFAULTS.replicationFactor
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Submit Case of the Edit Component', () => {
|
||||||
|
it('should check the submit functionality when topic updated is false', async () => {
|
||||||
|
const updateTopicMock = jest.fn();
|
||||||
|
const mocked = createMemoryHistory({
|
||||||
|
initialEntries: [`${clusterTopicsPath(clusterName)}/${topicName}/edit`],
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.spyOn(mocked, 'push');
|
||||||
|
renderComponent({ updateTopic: updateTopicMock }, mocked);
|
||||||
|
|
||||||
|
const btn = screen.getAllByText(/submit/i)[0];
|
||||||
|
expect(btn).toBeEnabled();
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.type(
|
||||||
|
screen.getByPlaceholderText('Min In Sync Replicas'),
|
||||||
|
'1'
|
||||||
|
);
|
||||||
|
userEvent.click(btn);
|
||||||
|
});
|
||||||
|
expect(updateTopicMock).toHaveBeenCalledTimes(1);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mocked.push).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the submit functionality when topic updated is true', async () => {
|
||||||
|
const updateTopicMock = jest.fn();
|
||||||
|
const mocked = createMemoryHistory({
|
||||||
|
initialEntries: [`${clusterTopicsPath(clusterName)}/${topicName}/edit`],
|
||||||
|
});
|
||||||
|
jest.spyOn(mocked, 'push');
|
||||||
|
renderComponent(
|
||||||
|
{ updateTopic: updateTopicMock, isTopicUpdated: true },
|
||||||
|
mocked
|
||||||
|
);
|
||||||
|
|
||||||
|
const btn = screen.getAllByText(/submit/i)[0];
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
userEvent.type(
|
||||||
|
screen.getByPlaceholderText('Min In Sync Replicas'),
|
||||||
|
'1'
|
||||||
|
);
|
||||||
|
userEvent.click(btn);
|
||||||
|
});
|
||||||
|
expect(updateTopicMock).toHaveBeenCalledTimes(1);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mocked.push).toHaveBeenCalled();
|
||||||
|
expect(mocked.location.pathname).toBe(
|
||||||
|
clusterTopicPath(clusterName, topicName)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,9 +18,14 @@ import { store } from 'redux/store';
|
||||||
import { fetchTopicDetailsAction } from 'redux/actions';
|
import { fetchTopicDetailsAction } from 'redux/actions';
|
||||||
import { initialState } from 'redux/reducers/topics/reducer';
|
import { initialState } from 'redux/reducers/topics/reducer';
|
||||||
import { externalTopicPayload } from 'redux/reducers/topics/__test__/fixtures';
|
import { externalTopicPayload } from 'redux/reducers/topics/__test__/fixtures';
|
||||||
|
import validateMessage from 'components/Topics/Topic/SendMessage/validateMessage';
|
||||||
|
import Alerts from 'components/Alerts/Alerts';
|
||||||
|
import * as S from 'components/App.styled';
|
||||||
|
|
||||||
import { testSchema } from './fixtures';
|
import { testSchema } from './fixtures';
|
||||||
|
|
||||||
|
import Mock = jest.Mock;
|
||||||
|
|
||||||
jest.mock('json-schema-faker', () => ({
|
jest.mock('json-schema-faker', () => ({
|
||||||
generate: () => ({
|
generate: () => ({
|
||||||
f1: -93251214,
|
f1: -93251214,
|
||||||
|
@ -30,6 +35,10 @@ jest.mock('json-schema-faker', () => ({
|
||||||
option: jest.fn(),
|
option: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('components/Topics/Topic/SendMessage/validateMessage', () =>
|
||||||
|
jest.fn()
|
||||||
|
);
|
||||||
|
|
||||||
const clusterName = 'testCluster';
|
const clusterName = 'testCluster';
|
||||||
const topicName = externalTopicPayload.name;
|
const topicName = externalTopicPayload.name;
|
||||||
const history = createMemoryHistory();
|
const history = createMemoryHistory();
|
||||||
|
@ -37,15 +46,30 @@ const history = createMemoryHistory();
|
||||||
const renderComponent = () => {
|
const renderComponent = () => {
|
||||||
history.push(clusterTopicSendMessagePath(clusterName, topicName));
|
history.push(clusterTopicSendMessagePath(clusterName, topicName));
|
||||||
render(
|
render(
|
||||||
<Router history={history}>
|
<>
|
||||||
<Route path={clusterTopicSendMessagePath(':clusterName', ':topicName')}>
|
<Router history={history}>
|
||||||
<SendMessage />
|
<Route path={clusterTopicSendMessagePath(':clusterName', ':topicName')}>
|
||||||
</Route>
|
<SendMessage />
|
||||||
</Router>,
|
</Route>
|
||||||
|
</Router>
|
||||||
|
<S.AlertsContainer role="toolbar">
|
||||||
|
<Alerts />
|
||||||
|
</S.AlertsContainer>
|
||||||
|
</>,
|
||||||
{ store }
|
{ store }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderAndSubmitData = async (error: string[] = []) => {
|
||||||
|
renderComponent();
|
||||||
|
await waitForElementToBeRemoved(() => screen.getByRole('progressbar'));
|
||||||
|
|
||||||
|
userEvent.selectOptions(screen.getByLabelText('Partition'), '0');
|
||||||
|
const sendBtn = await screen.findByText('Send');
|
||||||
|
(validateMessage as Mock).mockImplementation(() => error);
|
||||||
|
userEvent.click(sendBtn);
|
||||||
|
};
|
||||||
|
|
||||||
describe('SendMessage', () => {
|
describe('SendMessage', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
|
@ -71,6 +95,8 @@ describe('SendMessage', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when schema is fetched', () => {
|
describe('when schema is fetched', () => {
|
||||||
|
const url = `/api/clusters/${clusterName}/topics/${topicName}/messages`;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fetchMock.getOnce(
|
fetchMock.getOnce(
|
||||||
`/api/clusters/${clusterName}/topics/${topicName}/messages/schema`,
|
`/api/clusters/${clusterName}/topics/${topicName}/messages/schema`,
|
||||||
|
@ -79,20 +105,41 @@ describe('SendMessage', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls sendTopicMessage on submit', async () => {
|
it('calls sendTopicMessage on submit', async () => {
|
||||||
const sendTopicMessageMock = fetchMock.postOnce(
|
const sendTopicMessageMock = fetchMock.postOnce(url, 200);
|
||||||
`/api/clusters/${clusterName}/topics/${topicName}/messages`,
|
await renderAndSubmitData();
|
||||||
200
|
|
||||||
);
|
|
||||||
renderComponent();
|
|
||||||
await waitForElementToBeRemoved(() => screen.getByRole('progressbar'));
|
|
||||||
|
|
||||||
userEvent.selectOptions(screen.getByLabelText('Partition'), '0');
|
await waitFor(() =>
|
||||||
await screen.findByText('Send');
|
expect(sendTopicMessageMock.called(url)).toBeTruthy()
|
||||||
userEvent.click(screen.getByText('Send'));
|
);
|
||||||
await waitFor(() => expect(sendTopicMessageMock.called()).toBeTruthy());
|
|
||||||
expect(history.location.pathname).toEqual(
|
expect(history.location.pathname).toEqual(
|
||||||
clusterTopicMessagesPath(clusterName, topicName)
|
clusterTopicMessagesPath(clusterName, topicName)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should make the sendTopicMessage but most find an error within it', async () => {
|
||||||
|
const sendTopicMessageMock = fetchMock.postOnce(url, {
|
||||||
|
throws: 'Error',
|
||||||
|
});
|
||||||
|
await renderAndSubmitData();
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(sendTopicMessageMock.called(url)).toBeTruthy();
|
||||||
|
});
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole('alert')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(history.location.pathname).toEqual(
|
||||||
|
clusterTopicMessagesPath(clusterName, topicName)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check and view validation error message when is not valid', async () => {
|
||||||
|
const sendTopicMessageMock = fetchMock.postOnce(url, 200);
|
||||||
|
await renderAndSubmitData(['error']);
|
||||||
|
|
||||||
|
await waitFor(() => expect(sendTopicMessageMock.called(url)).toBeFalsy());
|
||||||
|
expect(history.location.pathname).not.toEqual(
|
||||||
|
clusterTopicMessagesPath(clusterName, topicName)
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +1,62 @@
|
||||||
import validateMessage from 'components/Topics/Topic/SendMessage/validateMessage';
|
import validateMessage from 'components/Topics/Topic/SendMessage/validateMessage';
|
||||||
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
|
|
||||||
import { testSchema } from './fixtures';
|
import { testSchema } from './fixtures';
|
||||||
|
|
||||||
describe('validateMessage', () => {
|
describe('validateMessage', () => {
|
||||||
it('returns no errors on correct input data', () => {
|
const defaultValidKey = `{"f1": 32, "f2": "multi-state", "schema": "Bedfordshire violet SAS"}`;
|
||||||
|
const defaultValidContent = `{"f1": 21128, "f2": "Health Berkshire", "schema": "Dynamic"}`;
|
||||||
|
|
||||||
|
it('should return empty error data if value is empty', () => {
|
||||||
|
const key = ``;
|
||||||
|
const content = ``;
|
||||||
|
expect(validateMessage(key, content, testSchema)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty error data if schema is empty', () => {
|
||||||
const key = `{"f1": 32, "f2": "multi-state", "schema": "Bedfordshire violet SAS"}`;
|
const key = `{"f1": 32, "f2": "multi-state", "schema": "Bedfordshire violet SAS"}`;
|
||||||
const content = `{"f1": 21128, "f2": "Health Berkshire", "schema": "Dynamic"}`;
|
const content = `{"f1": 21128, "f2": "Health Berkshire", "schema": "Dynamic"}`;
|
||||||
expect(validateMessage(key, content, testSchema)).toEqual([]);
|
const schema = cloneDeep(testSchema);
|
||||||
|
schema.key.schema = '';
|
||||||
|
schema.value.schema = '';
|
||||||
|
expect(validateMessage(key, content, schema)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return parsing error data if schema is not parsed with type of key', () => {
|
||||||
|
const schema = cloneDeep(testSchema);
|
||||||
|
schema.key.schema = '{invalid';
|
||||||
|
expect(
|
||||||
|
validateMessage(defaultValidKey, defaultValidContent, schema)
|
||||||
|
).toEqual([`Error in parsing the "key" field schema`]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return parsing error data if schema is not parsed with type of value', () => {
|
||||||
|
const schema = cloneDeep(testSchema);
|
||||||
|
schema.value.schema = '{invalid';
|
||||||
|
expect(
|
||||||
|
validateMessage(defaultValidKey, defaultValidContent, schema)
|
||||||
|
).toEqual([`Error in parsing the "content" field schema`]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty error data if schema type is string', () => {
|
||||||
|
const schema = cloneDeep(testSchema);
|
||||||
|
schema.key.schema = `{"type": "string"}`;
|
||||||
|
schema.value.schema = `{"type": "string"}`;
|
||||||
|
expect(
|
||||||
|
validateMessage(defaultValidKey, defaultValidContent, schema)
|
||||||
|
).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return error data if compile Ajv data throws an error', () => {
|
||||||
|
expect(
|
||||||
|
validateMessage(defaultValidKey, defaultValidContent, testSchema)
|
||||||
|
).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns no errors on correct input data', () => {
|
||||||
|
expect(
|
||||||
|
validateMessage(defaultValidContent, defaultValidContent, testSchema)
|
||||||
|
).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns errors on invalid input data', () => {
|
it('returns errors on invalid input data', () => {
|
||||||
|
|
|
@ -9,64 +9,78 @@ import {
|
||||||
clusterTopicSendMessagePath,
|
clusterTopicSendMessagePath,
|
||||||
} from 'lib/paths';
|
} from 'lib/paths';
|
||||||
|
|
||||||
|
const topicText = {
|
||||||
|
edit: 'Edit Container',
|
||||||
|
send: 'Send Message',
|
||||||
|
detail: 'Details Container',
|
||||||
|
loading: 'Loading',
|
||||||
|
};
|
||||||
|
|
||||||
jest.mock('components/Topics/Topic/Edit/EditContainer', () => () => (
|
jest.mock('components/Topics/Topic/Edit/EditContainer', () => () => (
|
||||||
<div>Edit Container</div>
|
<div>{topicText.edit}</div>
|
||||||
));
|
));
|
||||||
jest.mock('components/Topics/Topic/SendMessage/SendMessage', () => () => (
|
jest.mock('components/Topics/Topic/SendMessage/SendMessage', () => () => (
|
||||||
<div>Send Message</div>
|
<div>{topicText.send}</div>
|
||||||
));
|
));
|
||||||
jest.mock('components/Topics/Topic/Details/DetailsContainer', () => () => (
|
jest.mock('components/Topics/Topic/Details/DetailsContainer', () => () => (
|
||||||
<div>Details Container</div>
|
<div>{topicText.detail}</div>
|
||||||
));
|
));
|
||||||
jest.mock('components/common/PageLoader/PageLoader', () => () => (
|
jest.mock('components/common/PageLoader/PageLoader', () => () => (
|
||||||
<div>Loading</div>
|
<div>{topicText.loading}</div>
|
||||||
));
|
));
|
||||||
|
|
||||||
const resetTopicMessages = jest.fn();
|
describe('Topic Component', () => {
|
||||||
const fetchTopicDetailsMock = jest.fn();
|
const resetTopicMessages = jest.fn();
|
||||||
|
const fetchTopicDetailsMock = jest.fn();
|
||||||
|
|
||||||
const renderComponent = (pathname: string, topicFetching: boolean) =>
|
const renderComponent = (pathname: string, topicFetching: boolean) =>
|
||||||
render(
|
render(
|
||||||
<Route path={clusterTopicPath(':clusterName', ':topicName')}>
|
<Route path={clusterTopicPath(':clusterName', ':topicName')}>
|
||||||
<Topic
|
<Topic
|
||||||
isTopicFetching={topicFetching}
|
isTopicFetching={topicFetching}
|
||||||
resetTopicMessages={resetTopicMessages}
|
resetTopicMessages={resetTopicMessages}
|
||||||
fetchTopicDetails={fetchTopicDetailsMock}
|
fetchTopicDetails={fetchTopicDetailsMock}
|
||||||
/>
|
/>
|
||||||
</Route>,
|
</Route>,
|
||||||
{ pathname }
|
{ pathname }
|
||||||
);
|
);
|
||||||
|
|
||||||
it('renders Edit page', () => {
|
afterEach(() => {
|
||||||
renderComponent(clusterTopicEditPath('local', 'myTopicName'), false);
|
resetTopicMessages.mockClear();
|
||||||
expect(screen.getByText('Edit Container')).toBeInTheDocument();
|
fetchTopicDetailsMock.mockClear();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders Send Message page', () => {
|
it('renders Edit page', () => {
|
||||||
renderComponent(clusterTopicSendMessagePath('local', 'myTopicName'), false);
|
renderComponent(clusterTopicEditPath('local', 'myTopicName'), false);
|
||||||
expect(screen.getByText('Send Message')).toBeInTheDocument();
|
expect(screen.getByText(topicText.edit)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders Details Container page', () => {
|
it('renders Send Message page', () => {
|
||||||
renderComponent(clusterTopicPath('local', 'myTopicName'), false);
|
renderComponent(clusterTopicSendMessagePath('local', 'myTopicName'), false);
|
||||||
expect(screen.getByText('Details Container')).toBeInTheDocument();
|
expect(screen.getByText(topicText.send)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders Page loader', () => {
|
it('renders Details Container page', () => {
|
||||||
renderComponent(clusterTopicPath('local', 'myTopicName'), true);
|
renderComponent(clusterTopicPath('local', 'myTopicName'), false);
|
||||||
expect(screen.getByText('Loading')).toBeInTheDocument();
|
expect(screen.getByText(topicText.detail)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fetches topicDetails', () => {
|
it('renders Page loader', () => {
|
||||||
renderComponent(clusterTopicPath('local', 'myTopicName'), false);
|
renderComponent(clusterTopicPath('local', 'myTopicName'), true);
|
||||||
expect(fetchTopicDetailsMock).toHaveBeenCalledTimes(1);
|
expect(screen.getByText(topicText.loading)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resets topic messages after unmount', () => {
|
it('fetches topicDetails', () => {
|
||||||
const component = renderComponent(
|
renderComponent(clusterTopicPath('local', 'myTopicName'), false);
|
||||||
clusterTopicPath('local', 'myTopicName'),
|
expect(fetchTopicDetailsMock).toHaveBeenCalledTimes(1);
|
||||||
false
|
});
|
||||||
);
|
|
||||||
component.unmount();
|
it('resets topic messages after unmount', () => {
|
||||||
expect(resetTopicMessages).toHaveBeenCalledTimes(1);
|
const component = renderComponent(
|
||||||
|
clusterTopicPath('local', 'myTopicName'),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
component.unmount();
|
||||||
|
expect(resetTopicMessages).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { render } from 'lib/testHelpers';
|
||||||
|
import Topics from 'components/Topics/Topics';
|
||||||
|
import { Router } from 'react-router-dom';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import {
|
||||||
|
clusterTopicCopyPath,
|
||||||
|
clusterTopicNewPath,
|
||||||
|
clusterTopicPath,
|
||||||
|
clusterTopicsPath,
|
||||||
|
} from 'lib/paths';
|
||||||
|
|
||||||
|
const listContainer = 'listContainer';
|
||||||
|
const topicContainer = 'topicContainer';
|
||||||
|
const newCopyContainer = 'newCopyContainer';
|
||||||
|
|
||||||
|
jest.mock('components/Topics/List/ListContainer', () => () => (
|
||||||
|
<div>{listContainer}</div>
|
||||||
|
));
|
||||||
|
jest.mock('components/Topics/Topic/TopicContainer', () => () => (
|
||||||
|
<div>{topicContainer}</div>
|
||||||
|
));
|
||||||
|
jest.mock('components/Topics/New/New', () => () => (
|
||||||
|
<div>{newCopyContainer}</div>
|
||||||
|
));
|
||||||
|
|
||||||
|
describe('Topics Component', () => {
|
||||||
|
const clusterName = 'clusterName';
|
||||||
|
const topicName = 'topicName';
|
||||||
|
const setUpComponent = (path: string) => {
|
||||||
|
const history = createMemoryHistory({
|
||||||
|
initialEntries: [path],
|
||||||
|
});
|
||||||
|
return render(
|
||||||
|
<Router history={history}>
|
||||||
|
<Topics />
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should check if the page is Topics List rendered', () => {
|
||||||
|
setUpComponent(clusterTopicsPath(clusterName));
|
||||||
|
expect(screen.getByText(listContainer)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if the page is New Topic rendered', () => {
|
||||||
|
setUpComponent(clusterTopicNewPath(clusterName));
|
||||||
|
expect(screen.getByText(newCopyContainer)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if the page is Copy Topic rendered', () => {
|
||||||
|
setUpComponent(clusterTopicCopyPath(clusterName));
|
||||||
|
expect(screen.getByText(newCopyContainer)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if the page is Topic page rendered', () => {
|
||||||
|
setUpComponent(clusterTopicPath(clusterName, topicName));
|
||||||
|
expect(screen.getByText(topicContainer)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
|
@ -12,8 +12,6 @@ const isDisabled = false;
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const existingFields: string[] = [];
|
const existingFields: string[] = [];
|
||||||
const field = { name: 'name', value: 'value', id: 'id' };
|
const field = { name: 'name', value: 'value', id: 'id' };
|
||||||
const remove = jest.fn();
|
|
||||||
const setExistingFields = jest.fn();
|
|
||||||
|
|
||||||
const SPACE_KEY = ' ';
|
const SPACE_KEY = ' ';
|
||||||
|
|
||||||
|
@ -23,6 +21,9 @@ const selectOption = async (listbox: HTMLElement, option: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('CustomParamsField', () => {
|
describe('CustomParamsField', () => {
|
||||||
|
const remove = jest.fn();
|
||||||
|
const setExistingFields = jest.fn();
|
||||||
|
|
||||||
const setupComponent = (props: Props) => {
|
const setupComponent = (props: Props) => {
|
||||||
const Wrapper: React.FC = ({ children }) => {
|
const Wrapper: React.FC = ({ children }) => {
|
||||||
const methods = useForm();
|
const methods = useForm();
|
||||||
|
@ -36,7 +37,12 @@ describe('CustomParamsField', () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
it('renders with props', () => {
|
afterEach(() => {
|
||||||
|
remove.mockClear();
|
||||||
|
setExistingFields.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the component with its view correctly', () => {
|
||||||
setupComponent({
|
setupComponent({
|
||||||
field,
|
field,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
|
@ -61,7 +67,7 @@ describe('CustomParamsField', () => {
|
||||||
setExistingFields,
|
setExistingFields,
|
||||||
});
|
});
|
||||||
userEvent.click(screen.getByRole('button'));
|
userEvent.click(screen.getByRole('button'));
|
||||||
expect(remove.mock.calls.length).toBe(1);
|
expect(remove).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pressing space on button triggers remove', () => {
|
it('pressing space on button triggers remove', () => {
|
||||||
|
@ -75,7 +81,7 @@ describe('CustomParamsField', () => {
|
||||||
});
|
});
|
||||||
userEvent.type(screen.getByRole('button'), SPACE_KEY);
|
userEvent.type(screen.getByRole('button'), SPACE_KEY);
|
||||||
// userEvent.type triggers remove two times as at first it clicks on element and then presses space
|
// userEvent.type triggers remove two times as at first it clicks on element and then presses space
|
||||||
expect(remove.mock.calls.length).toBe(2);
|
expect(remove).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can select option', async () => {
|
it('can select option', async () => {
|
||||||
|
@ -123,7 +129,7 @@ describe('CustomParamsField', () => {
|
||||||
const listbox = screen.getByRole('listbox');
|
const listbox = screen.getByRole('listbox');
|
||||||
await selectOption(listbox, 'compression.type');
|
await selectOption(listbox, 'compression.type');
|
||||||
|
|
||||||
expect(setExistingFields.mock.calls.length).toBe(1);
|
expect(setExistingFields).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -80,14 +80,14 @@ describe('CustomParams', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('works with user inputs correctly', () => {
|
describe('works with user inputs correctly', () => {
|
||||||
beforeEach(() => {
|
let button: HTMLButtonElement;
|
||||||
|
beforeEach(async () => {
|
||||||
renderComponent({ isSubmitting: false });
|
renderComponent({ isSubmitting: false });
|
||||||
|
button = screen.getByRole('button');
|
||||||
|
await waitFor(() => userEvent.click(button));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('button click creates custom param fieldset', async () => {
|
it('button click creates custom param fieldset', async () => {
|
||||||
const button = screen.getByRole('button');
|
|
||||||
await waitFor(() => userEvent.click(button));
|
|
||||||
|
|
||||||
const listbox = screen.getByRole('listbox');
|
const listbox = screen.getByRole('listbox');
|
||||||
expect(listbox).toBeInTheDocument();
|
expect(listbox).toBeInTheDocument();
|
||||||
|
|
||||||
|
@ -96,8 +96,6 @@ describe('CustomParams', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can select option', async () => {
|
it('can select option', async () => {
|
||||||
const button = screen.getByRole('button');
|
|
||||||
await waitFor(() => userEvent.click(button));
|
|
||||||
const listbox = screen.getByRole('listbox');
|
const listbox = screen.getByRole('listbox');
|
||||||
|
|
||||||
await selectOption(listbox, 'compression.type');
|
await selectOption(listbox, 'compression.type');
|
||||||
|
@ -109,9 +107,6 @@ describe('CustomParams', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('when selected option changes disabled options update correctly', async () => {
|
it('when selected option changes disabled options update correctly', async () => {
|
||||||
const button = screen.getByRole('button');
|
|
||||||
await waitFor(() => userEvent.click(button));
|
|
||||||
|
|
||||||
const listbox = screen.getByRole('listbox');
|
const listbox = screen.getByRole('listbox');
|
||||||
|
|
||||||
await selectOption(listbox, 'compression.type');
|
await selectOption(listbox, 'compression.type');
|
||||||
|
@ -124,8 +119,6 @@ describe('CustomParams', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('multiple button clicks create multiple fieldsets', async () => {
|
it('multiple button clicks create multiple fieldsets', async () => {
|
||||||
const button = screen.getByRole('button');
|
|
||||||
await waitFor(() => userEvent.click(button));
|
|
||||||
await waitFor(() => userEvent.click(button));
|
await waitFor(() => userEvent.click(button));
|
||||||
await waitFor(() => userEvent.click(button));
|
await waitFor(() => userEvent.click(button));
|
||||||
|
|
||||||
|
@ -137,8 +130,6 @@ describe('CustomParams', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can't select already selected option", async () => {
|
it("can't select already selected option", async () => {
|
||||||
const button = screen.getByRole('button');
|
|
||||||
await waitFor(() => userEvent.click(button));
|
|
||||||
await waitFor(() => userEvent.click(button));
|
await waitFor(() => userEvent.click(button));
|
||||||
|
|
||||||
const listboxes = screen.getAllByRole('listbox');
|
const listboxes = screen.getAllByRole('listbox');
|
||||||
|
@ -152,8 +143,6 @@ describe('CustomParams', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('when fieldset with selected custom property type is deleted disabled options update correctly', async () => {
|
it('when fieldset with selected custom property type is deleted disabled options update correctly', async () => {
|
||||||
const button = screen.getByRole('button');
|
|
||||||
await waitFor(() => userEvent.click(button));
|
|
||||||
await waitFor(() => userEvent.click(button));
|
await waitFor(() => userEvent.click(button));
|
||||||
await waitFor(() => userEvent.click(button));
|
await waitFor(() => userEvent.click(button));
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { render } from 'lib/testHelpers';
|
||||||
|
import * as S from 'components/Topics/shared/Form/TopicForm.styled';
|
||||||
|
import { screen } from '@testing-library/react';
|
||||||
|
import theme from 'theme/theme';
|
||||||
|
|
||||||
|
describe('TopicForm styled components', () => {
|
||||||
|
describe('Button', () => {
|
||||||
|
it('should check the button styling in isActive state', () => {
|
||||||
|
render(<S.Button isActive />);
|
||||||
|
const button = screen.getByRole('button');
|
||||||
|
expect(button).toHaveStyle({
|
||||||
|
border: `1px solid ${theme.button.border.active}`,
|
||||||
|
backgroundColor: theme.button.primary.backgroundColor.active,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check the button styling in non Active state', () => {
|
||||||
|
render(<S.Button isActive={false} />);
|
||||||
|
const button = screen.getByRole('button');
|
||||||
|
expect(button).toHaveStyle({
|
||||||
|
border: `1px solid ${theme.button.primary.color}`,
|
||||||
|
backgroundColor: theme.button.primary.backgroundColor.normal,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Topic } from 'generated-sources';
|
||||||
|
|
||||||
export const internalTopicPayload = {
|
export const internalTopicPayload = {
|
||||||
name: '__internal.topic',
|
name: '__internal.topic',
|
||||||
internal: true,
|
internal: true,
|
||||||
|
@ -40,4 +42,7 @@ export const externalTopicPayload = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const topicsPayload = [internalTopicPayload, externalTopicPayload];
|
export const topicsPayload: Topic[] = [
|
||||||
|
internalTopicPayload,
|
||||||
|
externalTopicPayload,
|
||||||
|
];
|
||||||
|
|
Loading…
Add table
Reference in a new issue