Issues/1740 live tailing improvements (#1774)
* Implementing Context to the Topic messages pages * Using TopicContext in the Topics Topic MessageTable component * Using TopicContext variable in the Filters component * Fixing the Ordering of the Live mode Topic messaging * Fixing isLive parameter bug during page refresh * Minor code modification in Topic Filter Message page * Implement the correct seekType during live mode in url as well as in api call * Add Test cases to Messages and refactor eventSource Mock * Add initial Testing file for messages table * improve the MessagesTable test File * improve the MessagesTable test File + Filter Test File * improve the MessagesTable test File * Change the function name toggleSeekDirection to changeSeekDirection * change the name of the test suites to be more declarative * Display the table progress bar in live mode only when no data is fetched
This commit is contained in:
parent
c79905ce32
commit
68f8eed8f8
10 changed files with 325 additions and 96 deletions
|
@ -1,4 +1,4 @@
|
|||
import { render } from 'lib/testHelpers';
|
||||
import { render, EventSourceMock } from 'lib/testHelpers';
|
||||
import React from 'react';
|
||||
import Query, {
|
||||
getFormattedErrorFromTableData,
|
||||
|
@ -20,27 +20,6 @@ const renderComponent = () =>
|
|||
}
|
||||
);
|
||||
|
||||
// Small mock to get rid of reference error
|
||||
class EventSourceMock {
|
||||
url: string;
|
||||
|
||||
close: () => void;
|
||||
|
||||
open: () => void;
|
||||
|
||||
error: () => void;
|
||||
|
||||
onmessage: () => void;
|
||||
|
||||
constructor(url: string) {
|
||||
this.url = url;
|
||||
this.open = jest.fn();
|
||||
this.error = jest.fn();
|
||||
this.onmessage = jest.fn();
|
||||
this.close = jest.fn();
|
||||
}
|
||||
}
|
||||
|
||||
describe('Query', () => {
|
||||
it('renders', () => {
|
||||
renderComponent();
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
TopicMessageEventTypeEnum,
|
||||
MessageFilterType,
|
||||
} from 'generated-sources';
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { omitBy } from 'lodash';
|
||||
import { useHistory, useLocation } from 'react-router';
|
||||
import DatePicker from 'react-datepicker';
|
||||
|
@ -25,6 +25,8 @@ import { Button } from 'components/common/Button/Button';
|
|||
import FilterModal, {
|
||||
FilterEdit,
|
||||
} from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
|
||||
import { SeekDirectionOptions } from 'components/Topics/Topic/Details/Messages/Messages';
|
||||
import TopicMessagesContext from 'components/contexts/TopicMessagesContext';
|
||||
|
||||
import * as S from './Filters.styled';
|
||||
import {
|
||||
|
@ -66,11 +68,6 @@ export const SeekTypeOptions = [
|
|||
{ value: SeekType.OFFSET, label: 'Offset' },
|
||||
{ value: SeekType.TIMESTAMP, label: 'Timestamp' },
|
||||
];
|
||||
export const SeekDirectionOptions = [
|
||||
{ value: SeekDirection.FORWARD, label: 'Oldest First', isLive: false },
|
||||
{ value: SeekDirection.BACKWARD, label: 'Newest First', isLive: false },
|
||||
{ value: SeekDirection.TAILING, label: 'Live Mode', isLive: true },
|
||||
];
|
||||
|
||||
const Filters: React.FC<FiltersProps> = ({
|
||||
clusterName,
|
||||
|
@ -88,16 +85,14 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
|
||||
const { searchParams, seekDirection, isLive, changeSeekDirection } =
|
||||
useContext(TopicMessagesContext);
|
||||
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const toggleIsOpen = () => setIsOpen(!isOpen);
|
||||
|
||||
const source = React.useRef<EventSource | null>(null);
|
||||
|
||||
const searchParams = React.useMemo(
|
||||
() => new URLSearchParams(location.search),
|
||||
[location]
|
||||
);
|
||||
|
||||
const [selectedPartitions, setSelectedPartitions] = React.useState<Option[]>(
|
||||
getSelectedPartitionsFromSeekToParam(searchParams, partitions)
|
||||
);
|
||||
|
@ -132,10 +127,6 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
: MessageFilterType.STRING_CONTAINS
|
||||
);
|
||||
const [query, setQuery] = React.useState<string>(searchParams.get('q') || '');
|
||||
const [seekDirection, setSeekDirection] = React.useState<SeekDirection>(
|
||||
(searchParams.get('seekDirection') as SeekDirection) ||
|
||||
SeekDirection.FORWARD
|
||||
);
|
||||
const isSeekTypeControlVisible = React.useMemo(
|
||||
() => selectedPartitions.length > 0,
|
||||
[selectedPartitions]
|
||||
|
@ -178,7 +169,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
setAttempt(attempt + 1);
|
||||
|
||||
if (isSeekTypeControlVisible) {
|
||||
props.seekType = currentSeekType;
|
||||
props.seekType = isLive ? SeekType.LATEST : currentSeekType;
|
||||
props.seekTo = selectedPartitions.map(({ value }) => {
|
||||
let seekToOffset;
|
||||
|
||||
|
@ -217,21 +208,6 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
query,
|
||||
]);
|
||||
|
||||
const toggleSeekDirection = (val: string) => {
|
||||
switch (val) {
|
||||
case SeekDirection.FORWARD:
|
||||
setSeekDirection(SeekDirection.FORWARD);
|
||||
break;
|
||||
case SeekDirection.BACKWARD:
|
||||
setSeekDirection(SeekDirection.BACKWARD);
|
||||
break;
|
||||
case SeekDirection.TAILING:
|
||||
setSeekDirection(SeekDirection.TAILING);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
};
|
||||
|
||||
const handleSSECancel = () => {
|
||||
if (!source.current) return;
|
||||
|
||||
|
@ -295,7 +271,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
};
|
||||
// eslint-disable-next-line consistent-return
|
||||
React.useEffect(() => {
|
||||
if (location.search.length !== 0) {
|
||||
if (location.search?.length !== 0) {
|
||||
const url = `${BASE_PARAMS.basePath}/api/clusters/${clusterName}/topics/${topicName}/messages${location.search}`;
|
||||
const sse = new EventSource(url);
|
||||
|
||||
|
@ -346,7 +322,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
updatePhase,
|
||||
]);
|
||||
React.useEffect(() => {
|
||||
if (location.search.length === 0) {
|
||||
if (location.search?.length === 0) {
|
||||
handleFiltersSubmit();
|
||||
}
|
||||
}, [handleFiltersSubmit, location]);
|
||||
|
@ -376,7 +352,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
selectSize="M"
|
||||
minWidth="100px"
|
||||
options={SeekTypeOptions}
|
||||
disabled={seekDirection === SeekDirection.TAILING}
|
||||
disabled={isLive}
|
||||
/>
|
||||
{currentSeekType === SeekType.OFFSET ? (
|
||||
<Input
|
||||
|
@ -387,7 +363,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
className="offset-selector"
|
||||
placeholder="Offset"
|
||||
onChange={({ target: { value } }) => setOffset(value)}
|
||||
disabled={seekDirection === SeekDirection.TAILING}
|
||||
disabled={isLive}
|
||||
/>
|
||||
) : (
|
||||
<DatePicker
|
||||
|
@ -398,7 +374,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
dateFormat="MMMM d, yyyy HH:mm"
|
||||
className="date-picker"
|
||||
placeholderText="Select timestamp"
|
||||
disabled={seekDirection === SeekDirection.TAILING}
|
||||
disabled={isLive}
|
||||
/>
|
||||
)}
|
||||
</S.SeekTypeSelectorWrapper>
|
||||
|
@ -440,11 +416,11 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
</S.FilterInputs>
|
||||
<Select
|
||||
selectSize="M"
|
||||
onChange={(option) => toggleSeekDirection(option as string)}
|
||||
onChange={(option) => changeSeekDirection(option as string)}
|
||||
value={seekDirection}
|
||||
minWidth="120px"
|
||||
options={SeekDirectionOptions}
|
||||
isLive={seekDirection === SeekDirection.TAILING}
|
||||
isLive={isLive}
|
||||
/>
|
||||
</div>
|
||||
<S.ActiveSmartFilterWrapper>
|
||||
|
@ -479,12 +455,12 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
isFetching &&
|
||||
phaseMessage}
|
||||
</p>
|
||||
<S.MessageLoading isLive={seekDirection === SeekDirection.TAILING}>
|
||||
<S.MessageLoading isLive={isLive}>
|
||||
<S.MessageLoadingSpinner isFetching={isFetching} />
|
||||
Loading messages.
|
||||
<S.StopLoading
|
||||
onClick={() => {
|
||||
setSeekDirection(SeekDirection.FORWARD);
|
||||
changeSeekDirection(SeekDirection.FORWARD);
|
||||
setIsFetching(false);
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -1,29 +1,46 @@
|
|||
import React from 'react';
|
||||
import { SeekDirectionOptions } from 'components/Topics/Topic/Details/Messages/Messages';
|
||||
import Filters, {
|
||||
FiltersProps,
|
||||
SeekDirectionOptions,
|
||||
SeekTypeOptions,
|
||||
} from 'components/Topics/Topic/Details/Messages/Filters/Filters';
|
||||
import { render } from 'lib/testHelpers';
|
||||
import { screen, waitFor, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import TopicMessagesContext, {
|
||||
ContextProps,
|
||||
} from 'components/contexts/TopicMessagesContext';
|
||||
import { SeekDirection } from 'generated-sources';
|
||||
|
||||
const setupWrapper = (props?: Partial<FiltersProps>) =>
|
||||
const defaultContextValue: ContextProps = {
|
||||
isLive: false,
|
||||
seekDirection: SeekDirection.FORWARD,
|
||||
searchParams: new URLSearchParams(''),
|
||||
changeSeekDirection: jest.fn(),
|
||||
};
|
||||
|
||||
const setupWrapper = (
|
||||
props: Partial<FiltersProps> = {},
|
||||
ctx: ContextProps = defaultContextValue
|
||||
) => {
|
||||
render(
|
||||
<Filters
|
||||
clusterName="test-cluster"
|
||||
topicName="test-topic"
|
||||
partitions={[{ partition: 0, offsetMin: 0, offsetMax: 100 }]}
|
||||
meta={{}}
|
||||
isFetching={false}
|
||||
addMessage={jest.fn()}
|
||||
resetMessages={jest.fn()}
|
||||
updatePhase={jest.fn()}
|
||||
updateMeta={jest.fn()}
|
||||
setIsFetching={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
<TopicMessagesContext.Provider value={ctx}>
|
||||
<Filters
|
||||
clusterName="test-cluster"
|
||||
topicName="test-topic"
|
||||
partitions={[{ partition: 0, offsetMin: 0, offsetMax: 100 }]}
|
||||
meta={{}}
|
||||
isFetching={false}
|
||||
addMessage={jest.fn()}
|
||||
resetMessages={jest.fn()}
|
||||
updatePhase={jest.fn()}
|
||||
updateMeta={jest.fn()}
|
||||
setIsFetching={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
</TopicMessagesContext.Provider>
|
||||
);
|
||||
};
|
||||
describe('Filters component', () => {
|
||||
it('renders component', () => {
|
||||
setupWrapper();
|
||||
|
|
|
@ -1,13 +1,84 @@
|
|||
import React from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import TopicMessagesContext from 'components/contexts/TopicMessagesContext';
|
||||
import { SeekDirection } from 'generated-sources';
|
||||
import { useLocation } from 'react-router';
|
||||
|
||||
import FiltersContainer from './Filters/FiltersContainer';
|
||||
import MessagesTable from './MessagesTable';
|
||||
|
||||
const Messages: React.FC = () => (
|
||||
<div>
|
||||
<FiltersContainer />
|
||||
<MessagesTable />
|
||||
</div>
|
||||
);
|
||||
export const SeekDirectionOptionsObj = {
|
||||
[SeekDirection.FORWARD]: {
|
||||
value: SeekDirection.FORWARD,
|
||||
label: 'Oldest First',
|
||||
isLive: false,
|
||||
},
|
||||
[SeekDirection.BACKWARD]: {
|
||||
value: SeekDirection.BACKWARD,
|
||||
label: 'Newest First',
|
||||
isLive: false,
|
||||
},
|
||||
[SeekDirection.TAILING]: {
|
||||
value: SeekDirection.TAILING,
|
||||
label: 'Live Mode',
|
||||
isLive: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const SeekDirectionOptions = Object.values(SeekDirectionOptionsObj);
|
||||
|
||||
const Messages: React.FC = () => {
|
||||
const location = useLocation();
|
||||
|
||||
const searchParams = React.useMemo(
|
||||
() => new URLSearchParams(location.search),
|
||||
[location.search]
|
||||
);
|
||||
|
||||
const defaultSeekValue = SeekDirectionOptions[0];
|
||||
|
||||
const [seekDirection, setSeekDirection] = React.useState<SeekDirection>(
|
||||
(searchParams.get('seekDirection') as SeekDirection) ||
|
||||
defaultSeekValue.value
|
||||
);
|
||||
|
||||
const [isLive, setIsLive] = useState<boolean>(
|
||||
SeekDirectionOptionsObj[seekDirection].isLive
|
||||
);
|
||||
|
||||
const changeSeekDirection = useCallback((val: string) => {
|
||||
switch (val) {
|
||||
case SeekDirection.FORWARD:
|
||||
setSeekDirection(SeekDirection.FORWARD);
|
||||
setIsLive(SeekDirectionOptionsObj[SeekDirection.FORWARD].isLive);
|
||||
break;
|
||||
case SeekDirection.BACKWARD:
|
||||
setSeekDirection(SeekDirection.BACKWARD);
|
||||
setIsLive(SeekDirectionOptionsObj[SeekDirection.BACKWARD].isLive);
|
||||
break;
|
||||
case SeekDirection.TAILING:
|
||||
setSeekDirection(SeekDirection.TAILING);
|
||||
setIsLive(SeekDirectionOptionsObj[SeekDirection.TAILING].isLive);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}, []);
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
seekDirection,
|
||||
searchParams,
|
||||
changeSeekDirection,
|
||||
isLive,
|
||||
}),
|
||||
[seekDirection, searchParams, changeSeekDirection]
|
||||
);
|
||||
|
||||
return (
|
||||
<TopicMessagesContext.Provider value={contextValue}>
|
||||
<FiltersContainer />
|
||||
<MessagesTable />
|
||||
</TopicMessagesContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default Messages;
|
||||
|
|
|
@ -4,13 +4,14 @@ import TableHeaderCell from 'components/common/table/TableHeaderCell/TableHeader
|
|||
import { SeekDirection, TopicMessage } from 'generated-sources';
|
||||
import styled from 'styled-components';
|
||||
import { compact, concat, groupBy, map, maxBy, minBy } from 'lodash';
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useHistory, useLocation } from 'react-router';
|
||||
import { useHistory } from 'react-router';
|
||||
import {
|
||||
getTopicMessges,
|
||||
getIsTopicMessagesFetching,
|
||||
} from 'redux/reducers/topicMessages/selectors';
|
||||
import TopicMessagesContext from 'components/contexts/TopicMessagesContext';
|
||||
|
||||
import Message from './Message';
|
||||
import * as S from './MessageContent/MessageContent.styled';
|
||||
|
@ -22,13 +23,9 @@ const MessagesPaginationWrapperStyled = styled.div`
|
|||
`;
|
||||
|
||||
const MessagesTable: React.FC = () => {
|
||||
const location = useLocation();
|
||||
const history = useHistory();
|
||||
|
||||
const searchParams = React.useMemo(
|
||||
() => new URLSearchParams(location.search),
|
||||
[location]
|
||||
);
|
||||
const { searchParams, isLive } = useContext(TopicMessagesContext);
|
||||
|
||||
const messages = useSelector(getTopicMessges);
|
||||
const isFetching = useSelector(getIsTopicMessagesFetching);
|
||||
|
@ -94,7 +91,7 @@ const MessagesTable: React.FC = () => {
|
|||
message={message}
|
||||
/>
|
||||
))}
|
||||
{isFetching && (
|
||||
{(isFetching || isLive) && !messages.length && (
|
||||
<tr>
|
||||
<td colSpan={10}>
|
||||
<PageLoader />
|
||||
|
@ -108,9 +105,13 @@ const MessagesTable: React.FC = () => {
|
|||
)}
|
||||
</tbody>
|
||||
</Table>
|
||||
<MessagesPaginationWrapperStyled>
|
||||
<S.PaginationButton onClick={handleNextClick}>Next</S.PaginationButton>
|
||||
</MessagesPaginationWrapperStyled>
|
||||
{!isLive && (
|
||||
<MessagesPaginationWrapperStyled>
|
||||
<S.PaginationButton onClick={handleNextClick}>
|
||||
Next
|
||||
</S.PaginationButton>
|
||||
</MessagesPaginationWrapperStyled>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { render, EventSourceMock } from 'lib/testHelpers';
|
||||
import Messages, {
|
||||
SeekDirectionOptions,
|
||||
SeekDirectionOptionsObj,
|
||||
} from 'components/Topics/Topic/Details/Messages/Messages';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { SeekDirection, SeekType } from 'generated-sources';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
describe('Messages', () => {
|
||||
const searchParams = `?filterQueryType=STRING_CONTAINS&attempt=0&limit=100&seekDirection=${SeekDirection.FORWARD}&seekType=${SeekType.OFFSET}&seekTo=0::9`;
|
||||
|
||||
const setUpComponent = (param: string = searchParams) => {
|
||||
const history = createMemoryHistory();
|
||||
history.push({
|
||||
search: new URLSearchParams(param).toString(),
|
||||
});
|
||||
return render(
|
||||
<Router history={history}>
|
||||
<Messages />
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(window, 'EventSource', {
|
||||
value: EventSourceMock,
|
||||
});
|
||||
});
|
||||
describe('component rendering default behavior with the search params', () => {
|
||||
beforeEach(() => {
|
||||
setUpComponent();
|
||||
});
|
||||
it('should check default seekDirection if it actually take the value from the url', () => {
|
||||
expect(screen.getByRole('listbox')).toHaveTextContent(
|
||||
SeekDirectionOptionsObj[SeekDirection.FORWARD].label
|
||||
);
|
||||
});
|
||||
|
||||
it('should check the SeekDirection select changes', () => {
|
||||
const seekDirectionSelect = screen.getByRole('listbox');
|
||||
const seekDirectionOption = screen.getByRole('option');
|
||||
|
||||
expect(seekDirectionOption).toHaveTextContent(
|
||||
SeekDirectionOptionsObj[SeekDirection.FORWARD].label
|
||||
);
|
||||
|
||||
const labelValue1 = SeekDirectionOptions[1].label;
|
||||
userEvent.click(seekDirectionSelect);
|
||||
userEvent.selectOptions(seekDirectionSelect, [
|
||||
SeekDirectionOptions[1].label,
|
||||
]);
|
||||
expect(seekDirectionOption).toHaveTextContent(labelValue1);
|
||||
|
||||
const labelValue0 = SeekDirectionOptions[0].label;
|
||||
userEvent.click(seekDirectionSelect);
|
||||
userEvent.selectOptions(seekDirectionSelect, [
|
||||
SeekDirectionOptions[0].label,
|
||||
]);
|
||||
expect(seekDirectionOption).toHaveTextContent(labelValue0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Component rendering with custom Url search params', () => {
|
||||
it('reacts to a change of seekDirection in the url which make the select pick up different value', () => {
|
||||
setUpComponent(
|
||||
searchParams.replace(SeekDirection.FORWARD, SeekDirection.BACKWARD)
|
||||
);
|
||||
expect(screen.getByRole('listbox')).toHaveTextContent(
|
||||
SeekDirectionOptionsObj[SeekDirection.BACKWARD].label
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
import React from 'react';
|
||||
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 userEvent from '@testing-library/user-event';
|
||||
import TopicMessagesContext, {
|
||||
ContextProps,
|
||||
} from 'components/contexts/TopicMessagesContext';
|
||||
|
||||
describe('MessagesTable', () => {
|
||||
const searchParams = new URLSearchParams(
|
||||
`?filterQueryType=STRING_CONTAINS&attempt=0&limit=100&seekDirection=${SeekDirection.FORWARD}&seekType=${SeekType.OFFSET}&seekTo=0::9`
|
||||
);
|
||||
const contextValue: ContextProps = {
|
||||
isLive: false,
|
||||
seekDirection: SeekDirection.FORWARD,
|
||||
searchParams,
|
||||
changeSeekDirection: jest.fn(),
|
||||
};
|
||||
|
||||
const setUpComponent = (
|
||||
params: URLSearchParams = searchParams,
|
||||
ctx: ContextProps = contextValue
|
||||
) => {
|
||||
const history = createMemoryHistory();
|
||||
history.push({
|
||||
search: params.toString(),
|
||||
});
|
||||
return render(
|
||||
<Router history={history}>
|
||||
<TopicMessagesContext.Provider value={ctx}>
|
||||
<MessagesTable />
|
||||
</TopicMessagesContext.Provider>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
describe('Default props Setup for MessagesTable component', () => {
|
||||
beforeEach(() => {
|
||||
setUpComponent();
|
||||
});
|
||||
|
||||
it('should check the render', () => {
|
||||
expect(screen.getByRole('table')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should check the if no elements is rendered in the table', () => {
|
||||
expect(screen.getByText(/No messages found/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should check if next button exist and check the click after next click', () => {
|
||||
const nextBtnElement = screen.getByText(/next/i);
|
||||
expect(nextBtnElement).toBeInTheDocument();
|
||||
userEvent.click(nextBtnElement);
|
||||
expect(screen.getByText(/No messages found/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Custom Setup with different props value', () => {
|
||||
it('should check if next click is gone during isLive Param', () => {
|
||||
setUpComponent(searchParams, { ...contextValue, isLive: true });
|
||||
expect(screen.queryByText(/next/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should check the display of the loader element', () => {
|
||||
setUpComponent(searchParams, { ...contextValue, isLive: true });
|
||||
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
import React from 'react';
|
||||
import { SeekDirection } from 'generated-sources';
|
||||
|
||||
export interface ContextProps {
|
||||
seekDirection: SeekDirection;
|
||||
searchParams: URLSearchParams;
|
||||
changeSeekDirection(val: string): void;
|
||||
isLive: boolean;
|
||||
}
|
||||
|
||||
const TopicMessagesContext = React.createContext<ContextProps>(
|
||||
{} as ContextProps
|
||||
);
|
||||
|
||||
export default TopicMessagesContext;
|
|
@ -101,3 +101,23 @@ const customRender = (
|
|||
};
|
||||
|
||||
export { customRender as render };
|
||||
|
||||
export class EventSourceMock {
|
||||
url: string;
|
||||
|
||||
close: () => void;
|
||||
|
||||
open: () => void;
|
||||
|
||||
error: () => void;
|
||||
|
||||
onmessage: () => void;
|
||||
|
||||
constructor(url: string) {
|
||||
this.url = url;
|
||||
this.open = jest.fn();
|
||||
this.error = jest.fn();
|
||||
this.onmessage = jest.fn();
|
||||
this.close = jest.fn();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ const reducer = (state = initialState, action: Action): TopicMessagesState => {
|
|||
case getType(actions.addTopicMessage): {
|
||||
return {
|
||||
...state,
|
||||
messages: [...state.messages, action.payload],
|
||||
messages: [action.payload, ...state.messages],
|
||||
};
|
||||
}
|
||||
case getType(actions.resetTopicMessages):
|
||||
|
|
Loading…
Add table
Reference in a new issue