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 { mount, ReactWrapper } from 'enzyme';
|
||||
import { Route, Router } from 'react-router-dom';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { render } from 'lib/testHelpers';
|
||||
import { screen, waitFor, within } from '@testing-library/react';
|
||||
import { Route, Router, StaticRouter } from 'react-router';
|
||||
import ClusterContext, {
|
||||
ContextProps,
|
||||
} from 'components/contexts/ClusterContext';
|
||||
import List, { TopicsListProps } from 'components/Topics/List/List';
|
||||
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 { ConfirmationModalProps } from 'components/common/ConfirmationModal/ConfirmationModal';
|
||||
import theme from 'theme/theme';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { SortOrder } from 'generated-sources';
|
||||
|
||||
jest.mock(
|
||||
'components/common/ConfirmationModal/ConfirmationModal',
|
||||
() => 'mock-ConfirmationModal'
|
||||
);
|
||||
import { CleanUpPolicy, SortOrder } from 'generated-sources';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
describe('List', () => {
|
||||
const setupComponent = (props: Partial<TopicsListProps> = {}) => (
|
||||
<ThemeProvider theme={theme}>
|
||||
<List
|
||||
areTopicsFetching={false}
|
||||
topics={[]}
|
||||
totalPages={1}
|
||||
fetchTopicsList={jest.fn()}
|
||||
deleteTopic={jest.fn()}
|
||||
deleteTopics={jest.fn()}
|
||||
clearTopicsMessages={jest.fn()}
|
||||
clearTopicMessages={jest.fn()}
|
||||
recreateTopic={jest.fn()}
|
||||
search=""
|
||||
orderBy={null}
|
||||
sortOrder={SortOrder.ASC}
|
||||
setTopicsSearch={jest.fn()}
|
||||
setTopicsOrderBy={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
<List
|
||||
areTopicsFetching={false}
|
||||
topics={[]}
|
||||
totalPages={1}
|
||||
fetchTopicsList={jest.fn()}
|
||||
deleteTopic={jest.fn()}
|
||||
deleteTopics={jest.fn()}
|
||||
clearTopicsMessages={jest.fn()}
|
||||
clearTopicMessages={jest.fn()}
|
||||
recreateTopic={jest.fn()}
|
||||
search=""
|
||||
orderBy={null}
|
||||
sortOrder={SortOrder.ASC}
|
||||
setTopicsSearch={jest.fn()}
|
||||
setTopicsOrderBy={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
const historyMock = createMemoryHistory();
|
||||
|
||||
const mountComponentWithProviders = (
|
||||
const renderComponentWithProviders = (
|
||||
contextProps: Partial<ContextProps> = {},
|
||||
props: Partial<TopicsListProps> = {},
|
||||
history = historyMock
|
||||
) =>
|
||||
mount(
|
||||
render(
|
||||
<Router history={history}>
|
||||
<ClusterContext.Provider
|
||||
value={{
|
||||
|
@ -68,84 +57,79 @@ describe('List', () => {
|
|||
|
||||
describe('when it has readonly flag', () => {
|
||||
it('does not render the Add a Topic button', () => {
|
||||
const component = mountComponentWithProviders();
|
||||
expect(component.exists('Link')).toBeFalsy();
|
||||
renderComponentWithProviders();
|
||||
expect(screen.queryByText(/add a topic/)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when it does not have readonly flag', () => {
|
||||
let fetchTopicsList = jest.fn();
|
||||
let component: ReactWrapper;
|
||||
const internalTopicsSwitchName = 'input[name="ShowInternalTopics"]';
|
||||
const fetchTopicsList = jest.fn();
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
beforeEach(() => {
|
||||
fetchTopicsList = jest.fn();
|
||||
|
||||
component = mountComponentWithProviders(
|
||||
{ isReadOnly: false },
|
||||
{ fetchTopicsList }
|
||||
);
|
||||
afterEach(() => {
|
||||
fetchTopicsList.mockClear();
|
||||
});
|
||||
|
||||
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();
|
||||
component = mountComponentWithProviders({}, { setTopicsSearch });
|
||||
renderComponentWithProviders({}, { setTopicsSearch });
|
||||
const query = 'topic';
|
||||
const input = component.find(Search);
|
||||
input.props().handleSearch(query);
|
||||
expect(setTopicsSearch).toHaveBeenCalledWith(query);
|
||||
const searchElement = screen.getByPlaceholderText('Search by Topic Name');
|
||||
userEvent.type(searchElement, query);
|
||||
await waitFor(() => {
|
||||
expect(setTopicsSearch).toHaveBeenCalledWith(query);
|
||||
});
|
||||
});
|
||||
|
||||
it('show internal toggle state should be true if user has not used it yet', () => {
|
||||
const toggle = component.find(internalTopicsSwitchName);
|
||||
const { checked } = toggle.props();
|
||||
renderComponentWithProviders({ isReadOnly: false }, { fetchTopicsList });
|
||||
const internalCheckBox = screen.getByRole('checkbox');
|
||||
|
||||
expect(checked).toEqual(true);
|
||||
expect(internalCheckBox).toBeChecked();
|
||||
});
|
||||
|
||||
it('show internal toggle state should match user preference', () => {
|
||||
localStorage.setItem('hideInternalTopics', 'true');
|
||||
component = mountComponentWithProviders(
|
||||
{ isReadOnly: false },
|
||||
{ fetchTopicsList }
|
||||
);
|
||||
renderComponentWithProviders({ isReadOnly: false }, { fetchTopicsList });
|
||||
|
||||
const toggle = component.find(internalTopicsSwitchName);
|
||||
const { checked } = toggle.props();
|
||||
const internalCheckBox = screen.getByRole('checkbox');
|
||||
|
||||
expect(checked).toEqual(false);
|
||||
expect(internalCheckBox).not.toBeChecked();
|
||||
});
|
||||
|
||||
it('should refetch topics on show internal toggle change', () => {
|
||||
jest.clearAllMocks();
|
||||
const toggle = component.find(internalTopicsSwitchName);
|
||||
const { checked } = toggle.props();
|
||||
toggle.simulate('change');
|
||||
it('should re-fetch topics on show internal toggle change', async () => {
|
||||
renderComponentWithProviders({ isReadOnly: false }, { fetchTopicsList });
|
||||
const internalCheckBox: HTMLInputElement = screen.getByRole('checkbox');
|
||||
|
||||
expect(fetchTopicsList).toHaveBeenLastCalledWith({
|
||||
search: '',
|
||||
showInternal: !checked,
|
||||
sortOrder: SortOrder.ASC,
|
||||
userEvent.click(internalCheckBox);
|
||||
const { value } = internalCheckBox;
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetchTopicsList).toHaveBeenLastCalledWith({
|
||||
search: '',
|
||||
showInternal: value === 'on',
|
||||
sortOrder: SortOrder.ASC,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset page query param on show internal toggle change', () => {
|
||||
const mockedHistory = createMemoryHistory();
|
||||
jest.spyOn(mockedHistory, 'push');
|
||||
component = mountComponentWithProviders(
|
||||
renderComponentWithProviders(
|
||||
{ isReadOnly: false },
|
||||
{ fetchTopicsList },
|
||||
mockedHistory
|
||||
);
|
||||
|
||||
const toggle = component.find(internalTopicsSwitchName);
|
||||
toggle.simulate('change');
|
||||
const internalCheckBox: HTMLInputElement = screen.getByRole('checkbox');
|
||||
userEvent.click(internalCheckBox);
|
||||
|
||||
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 () => {
|
||||
const mockedHistory = createMemoryHistory();
|
||||
jest.spyOn(mockedHistory, 'push');
|
||||
component = mountComponentWithProviders(
|
||||
|
||||
const cachedPage = 5;
|
||||
mockedHistory.push(`/?page=${cachedPage}&perPage=25`);
|
||||
|
||||
renderComponentWithProviders(
|
||||
{ isReadOnly: false },
|
||||
{ fetchTopicsList, totalPages: 10 },
|
||||
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);
|
||||
input.props().handleSearch('nonEmptyString');
|
||||
userEvent.clear(searchInput);
|
||||
|
||||
expect(mockedHistory.push).toHaveBeenCalledWith('/?page=1&perPage=25');
|
||||
|
||||
input.props().handleSearch('');
|
||||
|
||||
expect(mockedHistory.push).toHaveBeenCalledWith(
|
||||
`/?page=${cachedPage}&perPage=25`
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(mockedHistory.push).toHaveBeenCalledWith(
|
||||
`/?page=${cachedPage}&perPage=25`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when some list items are selected', () => {
|
||||
const mockDeleteTopics = jest.fn();
|
||||
const mockDeleteTopic = jest.fn();
|
||||
const mockClearTopic = jest.fn();
|
||||
const mockClearTopicsMessages = jest.fn();
|
||||
const mockRecreate = jest.fn();
|
||||
const fetchTopicsList = jest.fn();
|
||||
|
||||
jest.useFakeTimers();
|
||||
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 = () =>
|
||||
component.find('mock-ConfirmationModal').at(0);
|
||||
|
||||
it('renders delete/purge buttons', () => {
|
||||
expect(getCheckboxInput(0).props().checked).toBeFalsy();
|
||||
expect(getCheckboxInput(1).props().checked).toBeFalsy();
|
||||
expect(component.find('.buttons').length).toEqual(0);
|
||||
|
||||
// check first item
|
||||
getCheckboxInput(0).simulate('change', { target: { checked: true } });
|
||||
expect(getCheckboxInput(0).props().checked).toBeTruthy();
|
||||
expect(getCheckboxInput(1).props().checked).toBeFalsy();
|
||||
|
||||
// check second item
|
||||
getCheckboxInput(1).simulate('change', { target: { checked: true } });
|
||||
expect(getCheckboxInput(0).props().checked).toBeTruthy();
|
||||
expect(getCheckboxInput(1).props().checked).toBeTruthy();
|
||||
expect(
|
||||
component.find('div[data-testid="delete-buttons"]').length
|
||||
).toEqual(1);
|
||||
|
||||
// uncheck second item
|
||||
getCheckboxInput(1).simulate('change', { target: { checked: false } });
|
||||
expect(getCheckboxInput(0).props().checked).toBeTruthy();
|
||||
expect(getCheckboxInput(1).props().checked).toBeFalsy();
|
||||
expect(
|
||||
component.find('div[data-testid="delete-buttons"]').length
|
||||
).toEqual(1);
|
||||
|
||||
// uncheck first item
|
||||
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);
|
||||
beforeEach(() => {
|
||||
render(
|
||||
<StaticRouter location={{ pathname }}>
|
||||
<Route path="/ui/clusters/:clusterName">
|
||||
<ClusterContext.Provider
|
||||
value={{
|
||||
isReadOnly: false,
|
||||
hasKafkaConnectConfigured: true,
|
||||
hasSchemaRegistryConfigured: true,
|
||||
isTopicDeletionAllowed: true,
|
||||
}}
|
||||
>
|
||||
{setupComponent({
|
||||
topics: [
|
||||
{
|
||||
...externalTopicPayload,
|
||||
cleanUpPolicy: CleanUpPolicy.DELETE,
|
||||
},
|
||||
{ ...externalTopicPayload, name: 'external.topic2' },
|
||||
],
|
||||
deleteTopics: mockDeleteTopics,
|
||||
clearTopicsMessages: mockClearTopicsMessages,
|
||||
recreateTopic: mockRecreate,
|
||||
deleteTopic: mockDeleteTopic,
|
||||
clearTopicMessages: mockClearTopic,
|
||||
fetchTopicsList,
|
||||
})}
|
||||
</ClusterContext.Provider>
|
||||
</Route>
|
||||
</StaticRouter>
|
||||
);
|
||||
});
|
||||
|
||||
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 confirmationText =
|
||||
action === 'deleteTopics'
|
||||
? 'Are you sure you want to remove selected topics?'
|
||||
: 'Are you sure you want to purge messages of selected topics?';
|
||||
const mockFn =
|
||||
action === 'deleteTopics' ? mockDeleteTopics : mockClearTopicsMessages;
|
||||
getCheckboxInput(0).simulate('change', { target: { checked: true } });
|
||||
getCheckboxInput(1).simulate('change', { target: { checked: true } });
|
||||
let modal = getConfirmationModal();
|
||||
expect(modal.prop('isOpen')).toBeFalsy();
|
||||
component
|
||||
.find('div[data-testid="delete-buttons"]')
|
||||
.find('button')
|
||||
.at(buttonIndex)
|
||||
.simulate('click');
|
||||
expect(modal.text()).toEqual(confirmationText);
|
||||
modal = getConfirmationModal();
|
||||
expect(modal.prop('isOpen')).toBeTruthy();
|
||||
await act(async () => {
|
||||
(modal.props() as ConfirmationModalProps).onConfirm();
|
||||
|
||||
const firstCheckbox = getCheckboxInput(0);
|
||||
const secondCheckbox = getCheckboxInput(1);
|
||||
userEvent.click(firstCheckbox);
|
||||
userEvent.click(secondCheckbox);
|
||||
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
|
||||
const deleteButtonContainer = screen.getByTestId('delete-buttons');
|
||||
const buttonClickedElement = within(deleteButtonContainer).getAllByRole(
|
||||
'button'
|
||||
)[buttonIndex];
|
||||
userEvent.click(buttonClickedElement);
|
||||
|
||||
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).toBeCalledWith('local', [
|
||||
externalTopicPayload.name,
|
||||
|
@ -290,28 +305,84 @@ describe('List', () => {
|
|||
});
|
||||
|
||||
it('closes ConfirmationModal when clicked on the cancel button', async () => {
|
||||
getCheckboxInput(0).simulate('change', { target: { checked: true } });
|
||||
getCheckboxInput(1).simulate('change', { target: { checked: true } });
|
||||
let modal = getConfirmationModal();
|
||||
expect(modal.prop('isOpen')).toBeFalsy();
|
||||
component
|
||||
.find('div[data-testid="delete-buttons"]')
|
||||
.find('button')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
modal = getConfirmationModal();
|
||||
expect(modal.prop('isOpen')).toBeTruthy();
|
||||
await act(async () => {
|
||||
(modal.props() as ConfirmationModalProps).onCancel();
|
||||
const firstCheckbox = getCheckboxInput(0);
|
||||
const secondCheckbox = getCheckboxInput(1);
|
||||
|
||||
userEvent.click(firstCheckbox);
|
||||
userEvent.click(secondCheckbox);
|
||||
|
||||
const deleteButton = screen.getByText('Delete selected topics');
|
||||
|
||||
userEvent.click(deleteButton);
|
||||
|
||||
const modal = screen.getByRole('dialog');
|
||||
userEvent.click(within(modal).getByText('Cancel'));
|
||||
|
||||
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();
|
||||
expect(getCheckboxInput(0).props().checked).toBeTruthy();
|
||||
expect(getCheckboxInput(1).props().checked).toBeTruthy();
|
||||
expect(
|
||||
component.find('div[data-testid="delete-buttons"]').length
|
||||
).toEqual(1);
|
||||
expect(mockDeleteTopics).toBeCalledTimes(0);
|
||||
|
||||
userEvent.click(ourAction);
|
||||
dialog = screen.getByRole('dialog');
|
||||
expect(dialog).toBeInTheDocument();
|
||||
userEvent.click(within(dialog).getByRole('button', { name: 'Cancel' }));
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
expect(mock).toHaveBeenCalledTimes(1);
|
||||
};
|
||||
|
||||
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(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;
|
||||
`;
|
||||
|
||||
export const MessageLoading = styled.div<MessageLoadingProps>`
|
||||
export const MessageLoading = styled.div.attrs({
|
||||
role: 'contentLoader',
|
||||
})<MessageLoadingProps>`
|
||||
color: ${({ theme }) => theme.heading.h3.color};
|
||||
font-size: ${({ theme }) => theme.heading.h3.fontSize};
|
||||
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;
|
||||
`;
|
||||
|
||||
const Message: React.FC<{ message: TopicMessage }> = ({
|
||||
export interface Props {
|
||||
message: TopicMessage;
|
||||
}
|
||||
|
||||
const Message: React.FC<Props> = ({
|
||||
message: {
|
||||
timestamp,
|
||||
timestampType,
|
||||
|
|
|
@ -1,29 +1,92 @@
|
|||
import React from 'react';
|
||||
import { TopicMessageTimestampTypeEnum } from 'generated-sources';
|
||||
import Message from 'components/Topics/Topic/Details/Messages/Message';
|
||||
import { TopicMessage, TopicMessageTimestampTypeEnum } from 'generated-sources';
|
||||
import Message, {
|
||||
Props,
|
||||
} from 'components/Topics/Topic/Details/Messages/Message';
|
||||
import { screen } from '@testing-library/react';
|
||||
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', () => {
|
||||
it('shows the data in the table row', async () => {
|
||||
render(
|
||||
const mockMessage: TopicMessage = {
|
||||
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>
|
||||
<tbody>
|
||||
<Message
|
||||
message={{
|
||||
timestamp: new Date(0),
|
||||
timestampType: TopicMessageTimestampTypeEnum.CREATE_TIME,
|
||||
offset: 0,
|
||||
key: 'test-key',
|
||||
partition: 0,
|
||||
content: '{"data": "test"}',
|
||||
headers: { header: 'test' },
|
||||
}}
|
||||
/>
|
||||
<Message message={props.message || mockMessage} />
|
||||
</tbody>
|
||||
</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 { screen } from '@testing-library/react';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { render, EventSourceMock } from 'lib/testHelpers';
|
||||
import Messages, {
|
||||
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 seekDirectionOption = screen.getAllByRole('option')[1];
|
||||
|
||||
|
@ -50,17 +50,28 @@ describe('Messages', () => {
|
|||
|
||||
const labelValue1 = SeekDirectionOptions[1].label;
|
||||
userEvent.click(seekDirectionSelect);
|
||||
userEvent.selectOptions(seekDirectionSelect, [
|
||||
SeekDirectionOptions[1].label,
|
||||
]);
|
||||
userEvent.selectOptions(seekDirectionSelect, [labelValue1]);
|
||||
expect(seekDirectionOption).toHaveTextContent(labelValue1);
|
||||
|
||||
const labelValue0 = SeekDirectionOptions[0].label;
|
||||
userEvent.click(seekDirectionSelect);
|
||||
userEvent.selectOptions(seekDirectionSelect, [
|
||||
SeekDirectionOptions[0].label,
|
||||
]);
|
||||
userEvent.selectOptions(seekDirectionSelect, [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 MessagesTable from 'components/Topics/Topic/Details/Messages/MessagesTable';
|
||||
import { Router } from 'react-router';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { SeekDirection, SeekType } from 'generated-sources';
|
||||
import { createMemoryHistory, MemoryHistory } from 'history';
|
||||
import { SeekDirection, SeekType, TopicMessage } from 'generated-sources';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import TopicMessagesContext, {
|
||||
ContextProps,
|
||||
} from 'components/contexts/TopicMessagesContext';
|
||||
import {
|
||||
topicMessagePayload,
|
||||
topicMessagesMetaPayload,
|
||||
} from 'redux/reducers/topicMessages/__test__/fixtures';
|
||||
|
||||
const mockTopicsMessages: TopicMessage[] = [{ ...topicMessagePayload }];
|
||||
|
||||
describe('MessagesTable', () => {
|
||||
const searchParams = new URLSearchParams(
|
||||
`?filterQueryType=STRING_CONTAINS&attempt=0&limit=100&seekDirection=${SeekDirection.FORWARD}&seekType=${SeekType.OFFSET}&seekTo=0::9`
|
||||
);
|
||||
const seekToResult = '&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 = {
|
||||
isLive: false,
|
||||
seekDirection: SeekDirection.FORWARD,
|
||||
|
@ -23,18 +29,32 @@ describe('MessagesTable', () => {
|
|||
|
||||
const setUpComponent = (
|
||||
params: URLSearchParams = searchParams,
|
||||
ctx: ContextProps = contextValue
|
||||
ctx: ContextProps = contextValue,
|
||||
messages: TopicMessage[] = [],
|
||||
customHistory?: MemoryHistory
|
||||
) => {
|
||||
const history = createMemoryHistory();
|
||||
history.push({
|
||||
search: params.toString(),
|
||||
});
|
||||
const history =
|
||||
customHistory ||
|
||||
createMemoryHistory({
|
||||
initialEntries: [params.toString()],
|
||||
});
|
||||
return render(
|
||||
<Router history={history}>
|
||||
<TopicMessagesContext.Provider value={ctx}>
|
||||
<MessagesTable />
|
||||
</TopicMessagesContext.Provider>
|
||||
</Router>
|
||||
</Router>,
|
||||
{
|
||||
preloadedState: {
|
||||
topicMessages: {
|
||||
messages,
|
||||
meta: {
|
||||
...topicMessagesMetaPayload,
|
||||
},
|
||||
isFetching: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -69,5 +89,55 @@ describe('MessagesTable', () => {
|
|||
setUpComponent(searchParams, { ...contextValue, isLive: true });
|
||||
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,
|
||||
inSyncReplicas?: number,
|
||||
replicas?: number
|
||||
) =>
|
||||
render(
|
||||
) => {
|
||||
return render(
|
||||
<ClusterContext.Provider value={contextValues}>
|
||||
<Overview
|
||||
underReplicatedPartitions={underReplicatedPartitions}
|
||||
|
@ -60,6 +60,11 @@ describe('Overview', () => {
|
|||
/>
|
||||
</ClusterContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
mockClearTopicMessages.mockClear();
|
||||
});
|
||||
|
||||
describe('when it has internal flag', () => {
|
||||
it('does not render the Action button a Topic', () => {
|
||||
|
@ -116,4 +121,49 @@ describe('Overview', () => {
|
|||
|
||||
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,
|
||||
replicationFactor: 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 Edit, { Props } from 'components/Topics/Topic/Edit/Edit';
|
||||
import { screen } from '@testing-library/react';
|
||||
import Edit, { DEFAULTS, Props } from 'components/Topics/Topic/Edit/Edit';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
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';
|
||||
|
||||
const renderComponent = (props?: Partial<Props>) =>
|
||||
const historyMock = createMemoryHistory();
|
||||
|
||||
const renderComponent = (props: Partial<Props> = {}, history = historyMock) =>
|
||||
render(
|
||||
<Edit
|
||||
clusterName={clusterName}
|
||||
topicName={topicName}
|
||||
topic={topicWithInfo}
|
||||
isFetched
|
||||
isTopicUpdated={false}
|
||||
fetchTopicConfig={jest.fn()}
|
||||
updateTopic={jest.fn()}
|
||||
updateTopicPartitionsCount={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
<Router history={history}>
|
||||
<Edit
|
||||
clusterName={props.clusterName || clusterName}
|
||||
topicName={props.topicName || topicName}
|
||||
topic={'topic' in props ? props.topic : topicWithInfo}
|
||||
isFetched={'isFetched' in props ? !!props.isFetched : true}
|
||||
isTopicUpdated={
|
||||
'isTopicUpdated' in props ? !!props.isTopicUpdated : false
|
||||
}
|
||||
fetchTopicConfig={jest.fn()}
|
||||
updateTopic={props.updateTopic || jest.fn()}
|
||||
updateTopicPartitionsCount={
|
||||
props.updateTopicPartitionsCount || jest.fn()
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
|
||||
describe('DangerZone', () => {
|
||||
it('renders', () => {
|
||||
describe('Edit Component', () => {
|
||||
it('renders the Edit Component', () => {
|
||||
renderComponent();
|
||||
|
||||
expect(
|
||||
|
@ -31,4 +43,112 @@ describe('DangerZone', () => {
|
|||
screen.getByRole('heading', { name: `Danger Zone` })
|
||||
).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 { initialState } from 'redux/reducers/topics/reducer';
|
||||
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 Mock = jest.Mock;
|
||||
|
||||
jest.mock('json-schema-faker', () => ({
|
||||
generate: () => ({
|
||||
f1: -93251214,
|
||||
|
@ -30,6 +35,10 @@ jest.mock('json-schema-faker', () => ({
|
|||
option: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('components/Topics/Topic/SendMessage/validateMessage', () =>
|
||||
jest.fn()
|
||||
);
|
||||
|
||||
const clusterName = 'testCluster';
|
||||
const topicName = externalTopicPayload.name;
|
||||
const history = createMemoryHistory();
|
||||
|
@ -37,15 +46,30 @@ const history = createMemoryHistory();
|
|||
const renderComponent = () => {
|
||||
history.push(clusterTopicSendMessagePath(clusterName, topicName));
|
||||
render(
|
||||
<Router history={history}>
|
||||
<Route path={clusterTopicSendMessagePath(':clusterName', ':topicName')}>
|
||||
<SendMessage />
|
||||
</Route>
|
||||
</Router>,
|
||||
<>
|
||||
<Router history={history}>
|
||||
<Route path={clusterTopicSendMessagePath(':clusterName', ':topicName')}>
|
||||
<SendMessage />
|
||||
</Route>
|
||||
</Router>
|
||||
<S.AlertsContainer role="toolbar">
|
||||
<Alerts />
|
||||
</S.AlertsContainer>
|
||||
</>,
|
||||
{ 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', () => {
|
||||
beforeAll(() => {
|
||||
store.dispatch(
|
||||
|
@ -71,6 +95,8 @@ describe('SendMessage', () => {
|
|||
});
|
||||
|
||||
describe('when schema is fetched', () => {
|
||||
const url = `/api/clusters/${clusterName}/topics/${topicName}/messages`;
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock.getOnce(
|
||||
`/api/clusters/${clusterName}/topics/${topicName}/messages/schema`,
|
||||
|
@ -79,20 +105,41 @@ describe('SendMessage', () => {
|
|||
});
|
||||
|
||||
it('calls sendTopicMessage on submit', async () => {
|
||||
const sendTopicMessageMock = fetchMock.postOnce(
|
||||
`/api/clusters/${clusterName}/topics/${topicName}/messages`,
|
||||
200
|
||||
);
|
||||
renderComponent();
|
||||
await waitForElementToBeRemoved(() => screen.getByRole('progressbar'));
|
||||
const sendTopicMessageMock = fetchMock.postOnce(url, 200);
|
||||
await renderAndSubmitData();
|
||||
|
||||
userEvent.selectOptions(screen.getByLabelText('Partition'), '0');
|
||||
await screen.findByText('Send');
|
||||
userEvent.click(screen.getByText('Send'));
|
||||
await waitFor(() => expect(sendTopicMessageMock.called()).toBeTruthy());
|
||||
await waitFor(() =>
|
||||
expect(sendTopicMessageMock.called(url)).toBeTruthy()
|
||||
);
|
||||
expect(history.location.pathname).toEqual(
|
||||
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 cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
import { testSchema } from './fixtures';
|
||||
|
||||
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 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', () => {
|
||||
|
|
|
@ -9,64 +9,78 @@ import {
|
|||
clusterTopicSendMessagePath,
|
||||
} from 'lib/paths';
|
||||
|
||||
const topicText = {
|
||||
edit: 'Edit Container',
|
||||
send: 'Send Message',
|
||||
detail: 'Details Container',
|
||||
loading: 'Loading',
|
||||
};
|
||||
|
||||
jest.mock('components/Topics/Topic/Edit/EditContainer', () => () => (
|
||||
<div>Edit Container</div>
|
||||
<div>{topicText.edit}</div>
|
||||
));
|
||||
jest.mock('components/Topics/Topic/SendMessage/SendMessage', () => () => (
|
||||
<div>Send Message</div>
|
||||
<div>{topicText.send}</div>
|
||||
));
|
||||
jest.mock('components/Topics/Topic/Details/DetailsContainer', () => () => (
|
||||
<div>Details Container</div>
|
||||
<div>{topicText.detail}</div>
|
||||
));
|
||||
jest.mock('components/common/PageLoader/PageLoader', () => () => (
|
||||
<div>Loading</div>
|
||||
<div>{topicText.loading}</div>
|
||||
));
|
||||
|
||||
const resetTopicMessages = jest.fn();
|
||||
const fetchTopicDetailsMock = jest.fn();
|
||||
describe('Topic Component', () => {
|
||||
const resetTopicMessages = jest.fn();
|
||||
const fetchTopicDetailsMock = jest.fn();
|
||||
|
||||
const renderComponent = (pathname: string, topicFetching: boolean) =>
|
||||
render(
|
||||
<Route path={clusterTopicPath(':clusterName', ':topicName')}>
|
||||
<Topic
|
||||
isTopicFetching={topicFetching}
|
||||
resetTopicMessages={resetTopicMessages}
|
||||
fetchTopicDetails={fetchTopicDetailsMock}
|
||||
/>
|
||||
</Route>,
|
||||
{ pathname }
|
||||
);
|
||||
const renderComponent = (pathname: string, topicFetching: boolean) =>
|
||||
render(
|
||||
<Route path={clusterTopicPath(':clusterName', ':topicName')}>
|
||||
<Topic
|
||||
isTopicFetching={topicFetching}
|
||||
resetTopicMessages={resetTopicMessages}
|
||||
fetchTopicDetails={fetchTopicDetailsMock}
|
||||
/>
|
||||
</Route>,
|
||||
{ pathname }
|
||||
);
|
||||
|
||||
it('renders Edit page', () => {
|
||||
renderComponent(clusterTopicEditPath('local', 'myTopicName'), false);
|
||||
expect(screen.getByText('Edit Container')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Send Message page', () => {
|
||||
renderComponent(clusterTopicSendMessagePath('local', 'myTopicName'), false);
|
||||
expect(screen.getByText('Send Message')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Details Container page', () => {
|
||||
renderComponent(clusterTopicPath('local', 'myTopicName'), false);
|
||||
expect(screen.getByText('Details Container')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Page loader', () => {
|
||||
renderComponent(clusterTopicPath('local', 'myTopicName'), true);
|
||||
expect(screen.getByText('Loading')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('fetches topicDetails', () => {
|
||||
renderComponent(clusterTopicPath('local', 'myTopicName'), false);
|
||||
expect(fetchTopicDetailsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('resets topic messages after unmount', () => {
|
||||
const component = renderComponent(
|
||||
clusterTopicPath('local', 'myTopicName'),
|
||||
false
|
||||
);
|
||||
component.unmount();
|
||||
expect(resetTopicMessages).toHaveBeenCalledTimes(1);
|
||||
afterEach(() => {
|
||||
resetTopicMessages.mockClear();
|
||||
fetchTopicDetailsMock.mockClear();
|
||||
});
|
||||
|
||||
it('renders Edit page', () => {
|
||||
renderComponent(clusterTopicEditPath('local', 'myTopicName'), false);
|
||||
expect(screen.getByText(topicText.edit)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Send Message page', () => {
|
||||
renderComponent(clusterTopicSendMessagePath('local', 'myTopicName'), false);
|
||||
expect(screen.getByText(topicText.send)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Details Container page', () => {
|
||||
renderComponent(clusterTopicPath('local', 'myTopicName'), false);
|
||||
expect(screen.getByText(topicText.detail)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Page loader', () => {
|
||||
renderComponent(clusterTopicPath('local', 'myTopicName'), true);
|
||||
expect(screen.getByText(topicText.loading)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('fetches topicDetails', () => {
|
||||
renderComponent(clusterTopicPath('local', 'myTopicName'), false);
|
||||
expect(fetchTopicDetailsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('resets topic messages after unmount', () => {
|
||||
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 existingFields: string[] = [];
|
||||
const field = { name: 'name', value: 'value', id: 'id' };
|
||||
const remove = jest.fn();
|
||||
const setExistingFields = jest.fn();
|
||||
|
||||
const SPACE_KEY = ' ';
|
||||
|
||||
|
@ -23,6 +21,9 @@ const selectOption = async (listbox: HTMLElement, option: string) => {
|
|||
};
|
||||
|
||||
describe('CustomParamsField', () => {
|
||||
const remove = jest.fn();
|
||||
const setExistingFields = jest.fn();
|
||||
|
||||
const setupComponent = (props: Props) => {
|
||||
const Wrapper: React.FC = ({ children }) => {
|
||||
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({
|
||||
field,
|
||||
isDisabled,
|
||||
|
@ -61,7 +67,7 @@ describe('CustomParamsField', () => {
|
|||
setExistingFields,
|
||||
});
|
||||
userEvent.click(screen.getByRole('button'));
|
||||
expect(remove.mock.calls.length).toBe(1);
|
||||
expect(remove).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('pressing space on button triggers remove', () => {
|
||||
|
@ -75,7 +81,7 @@ describe('CustomParamsField', () => {
|
|||
});
|
||||
userEvent.type(screen.getByRole('button'), SPACE_KEY);
|
||||
// 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 () => {
|
||||
|
@ -123,7 +129,7 @@ describe('CustomParamsField', () => {
|
|||
const listbox = screen.getByRole('listbox');
|
||||
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', () => {
|
||||
beforeEach(() => {
|
||||
let button: HTMLButtonElement;
|
||||
beforeEach(async () => {
|
||||
renderComponent({ isSubmitting: false });
|
||||
button = screen.getByRole('button');
|
||||
await waitFor(() => userEvent.click(button));
|
||||
});
|
||||
|
||||
it('button click creates custom param fieldset', async () => {
|
||||
const button = screen.getByRole('button');
|
||||
await waitFor(() => userEvent.click(button));
|
||||
|
||||
const listbox = screen.getByRole('listbox');
|
||||
expect(listbox).toBeInTheDocument();
|
||||
|
||||
|
@ -96,8 +96,6 @@ describe('CustomParams', () => {
|
|||
});
|
||||
|
||||
it('can select option', async () => {
|
||||
const button = screen.getByRole('button');
|
||||
await waitFor(() => userEvent.click(button));
|
||||
const listbox = screen.getByRole('listbox');
|
||||
|
||||
await selectOption(listbox, 'compression.type');
|
||||
|
@ -109,9 +107,6 @@ describe('CustomParams', () => {
|
|||
});
|
||||
|
||||
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');
|
||||
|
||||
await selectOption(listbox, 'compression.type');
|
||||
|
@ -124,8 +119,6 @@ describe('CustomParams', () => {
|
|||
});
|
||||
|
||||
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));
|
||||
|
||||
|
@ -137,8 +130,6 @@ describe('CustomParams', () => {
|
|||
});
|
||||
|
||||
it("can't select already selected option", async () => {
|
||||
const button = screen.getByRole('button');
|
||||
await waitFor(() => userEvent.click(button));
|
||||
await waitFor(() => userEvent.click(button));
|
||||
|
||||
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 () => {
|
||||
const button = screen.getByRole('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 = {
|
||||
name: '__internal.topic',
|
||||
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