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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,41 @@ describe('AutoRefresh', () => {
expect(onRefresh).toBeCalledTimes(3)
})
})

it('should NOT call onRefresh with disabled state', async () => {
const onRefresh = jest.fn()
const { rerender } = render(<AutoRefresh {...instance(mockedProps)} onRefresh={onRefresh} />)

fireEvent.click(screen.getByTestId('auto-refresh-config-btn'))
fireEvent.click(screen.getByTestId('auto-refresh-switch'))
fireEvent.click(screen.getByTestId('refresh-rate'))
fireEvent.change(screen.getByTestId(INLINE_ITEM_EDITOR), { target: { value: '1' } })

expect(screen.getByTestId(INLINE_ITEM_EDITOR)).toHaveValue('1')

screen.getByTestId(/apply-btn/).click()

await act(() => {
rerender(<AutoRefresh {...instance(mockedProps)} onRefresh={onRefresh} disabled />)
})

await act(async () => {
await new Promise((r) => setTimeout(r, 1300))
})
expect(onRefresh).toBeCalledTimes(0)

await act(async () => {
await new Promise((r) => setTimeout(r, 1300))
})
expect(onRefresh).toBeCalledTimes(0)

await act(() => {
rerender(<AutoRefresh {...instance(mockedProps)} onRefresh={onRefresh} disabled={false} />)
})

await act(async () => {
await new Promise((r) => setTimeout(r, 1300))
})
expect(onRefresh).toBeCalledTimes(1)
})
})
10 changes: 5 additions & 5 deletions redisinsight/ui/src/components/auto-refresh/AutoRefresh.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const AutoRefresh = ({
useEffect(() => {
updateLastRefresh()

if (enableAutoRefresh && !loading) {
if (enableAutoRefresh && !loading && !disabled) {
intervalRefresh = setInterval(() => {
if (document.hidden) return

Expand All @@ -117,7 +117,7 @@ const AutoRefresh = ({
}

return () => clearInterval(intervalRefresh)
}, [enableAutoRefresh, refreshRate, loading, lastRefreshTime])
}, [enableAutoRefresh, refreshRate, loading, disabled, lastRefreshTime])

const getLastRefreshDelta = (time:Nullable<number>) => (Date.now() - (time || 0)) / 1_000

Expand Down Expand Up @@ -163,12 +163,12 @@ const AutoRefresh = ({
}

return (
<div className={cx(styles.container, containerClassName, { [styles.enable]: enableAutoRefresh })}>
<div className={cx(styles.container, containerClassName, { [styles.enable]: !disabled && enableAutoRefresh })}>
<EuiTextColor className={styles.summary}>
{displayText && (
<span data-testid="refresh-message-label">{`${enableAutoRefresh ? 'Auto refresh:' : 'Last refresh:'}`}</span>
)}
<span className={styles.time} data-testid="refresh-message">
<span className={cx(styles.time, { [styles.disabled]: disabled })} data-testid="refresh-message">
{` ${enableAutoRefresh ? refreshRateMessage : refreshMessage}`}
</span>
</EuiTextColor>
Expand All @@ -185,7 +185,7 @@ const AutoRefresh = ({
disabled={loading || disabled}
onClick={handleRefreshClick}
onMouseEnter={updateLastRefresh}
className={cx(styles.btn, { [styles.rolling]: enableAutoRefresh })}
className={cx(styles.btn, { [styles.rolling]: !disabled && enableAutoRefresh })}
aria-labelledby={testid?.replaceAll?.('-', ' ') || 'Refresh button'}
data-testid={testid || 'refresh-btn'}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
.time {
padding-right: 6px;
color: var(--euiTextSubduedColor) !important;

&.disabled {
opacity: 0.5;
}
}

.summary {
Expand Down
8 changes: 4 additions & 4 deletions redisinsight/ui/src/pages/browser/BrowserPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useSelector } from 'react-redux'
import { render, screen, fireEvent, mockedStore, cleanup, act, waitForEuiToolTipVisible } from 'uiSrc/utils/test-utils'
import { KeyTypes } from 'uiSrc/constants'
import { RootState } from 'uiSrc/slices/store'
import { toggleBrowserFullScreen } from 'uiSrc/slices/browser/keys'
import { setSelectedKeyRefreshDisabled, toggleBrowserFullScreen } from 'uiSrc/slices/browser/keys'
import BrowserPage from './BrowserPage'
import KeyList, { Props as KeyListProps } from './components/key-list/KeyList'

Expand Down Expand Up @@ -198,7 +198,7 @@ describe('KeyDetailsWrapper', () => {
expect(queryByTestId('apply-btn')).toBeInTheDocument()
expect(queryByTestId('apply-btn')).toBeDisabled()

expect(store.getActions()).toEqual([...afterRenderActions])
expect(store.getActions()).toEqual([...afterRenderActions, setSelectedKeyRefreshDisabled(true)])

await act(async () => {
fireEvent.mouseOver(screen.getByTestId('apply-btn'))
Expand Down Expand Up @@ -259,7 +259,7 @@ describe('KeyDetailsWrapper', () => {
expect(queryByTestId('apply-btn')).toBeInTheDocument()
expect(queryByTestId('apply-btn')).toBeDisabled()

expect(store.getActions()).toEqual([...afterRenderActions])
expect(store.getActions()).toEqual([...afterRenderActions, setSelectedKeyRefreshDisabled(true)])
})

it('Verify that user cannot save key value (List) with unprintable characters', () => {
Expand Down Expand Up @@ -313,7 +313,7 @@ describe('KeyDetailsWrapper', () => {
expect(queryByTestId('apply-btn')).toBeInTheDocument()
expect(queryByTestId('apply-btn')).toBeDisabled()

expect(store.getActions()).toEqual([...afterRenderActions])
expect(store.getActions()).toEqual([...afterRenderActions, setSelectedKeyRefreshDisabled(true)])
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
import { RedisResponseBuffer } from 'uiSrc/slices/interfaces'
import { getBasedOnViewTypeEvent, sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'

import { resetStringValue } from 'uiSrc/slices/browser/string'
import { Nullable } from 'uiSrc/utils'
import { KeyDetailsHeaderFormatter } from './components/key-details-header-formatter'
import { KeyDetailsHeaderName } from './components/key-details-header-name'
import { KeyDetailsHeaderTTL } from './components/key-details-header-ttl'
Expand Down Expand Up @@ -61,7 +59,7 @@ const KeyDetailsHeader = ({
keyType,
Actions,
}: KeyDetailsHeaderProps) => {
const { loading, lastRefreshTime } = useSelector(selectedKeySelector)
const { refreshing, loading, lastRefreshTime, isRefreshDisabled } = useSelector(selectedKeySelector)
const {
type,
length,
Expand Down Expand Up @@ -169,7 +167,8 @@ const KeyDetailsHeader = ({
<div className={styles.subtitleActionBtns}>
<AutoRefresh
postfix={type}
loading={loading}
disabled={isRefreshDisabled}
loading={loading || refreshing}
lastRefreshTime={lastRefreshTime}
displayText={width > HIDE_LAST_REFRESH}
containerClassName={styles.actionBtn}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
import React from 'react'
import { instance, mock } from 'ts-mockito'
import { render, screen } from 'uiSrc/utils/test-utils'
import { cloneDeep } from 'lodash'
import { cleanup, mockedStore, render, screen } from 'uiSrc/utils/test-utils'

import { defaultSelectedKeyAction, setSelectedKeyRefreshDisabled } from 'uiSrc/slices/browser/keys'
import KeyDetails, { Props as KeyDetailsProps } from './KeyDetails'

const mockedProps = mock<KeyDetailsProps>()

let store: typeof mockedStore
beforeEach(() => {
cleanup()
store = cloneDeep(mockedStore)
store.clearActions()
})

describe('KeyDetails', () => {
it('should render', () => {
expect(render(<KeyDetails {...instance(mockedProps)} />)).toBeTruthy()
})

it('should call proper actions after render', () => {
render(<KeyDetails {...instance(mockedProps)} />)

expect(store.getActions()).toEqual([
defaultSelectedKeyAction(),
setSelectedKeyRefreshDisabled(false)
])
})

it('should render nothing when there are no keys', () => {
render(<KeyDetails {...instance(mockedProps)} totalKeys={0} keysLastRefreshTime={null} />)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
keysSelector,
selectedKeyDataSelector,
selectedKeySelector,
setSelectedKeyRefreshDisabled,
} from 'uiSrc/slices/browser/keys'
import { KeyTypes } from 'uiSrc/constants'

Expand Down Expand Up @@ -57,6 +58,7 @@ const KeyDetails = (props: Props) => {
// Restore key details from context in future
// (selectedKey.data?.name !== keyProp)
dispatch(fetchKeyInfo(keyProp))
dispatch(setSelectedKeyRefreshDisabled(false))
}, [keyProp])

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import React from 'react'
import { instance, mock } from 'ts-mockito'
import { cloneDeep } from 'lodash'
import { KeyValueCompressor, TEXT_DISABLED_COMPRESSED_VALUE } from 'uiSrc/constants'
import { hashDataSelector } from 'uiSrc/slices/browser/hash'
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
import { RedisResponseBufferType } from 'uiSrc/slices/interfaces'
import { anyToBuffer, bufferToString } from 'uiSrc/utils'
import { act, fireEvent, render, screen, waitForEuiToolTipVisible } from 'uiSrc/utils/test-utils'
import { act, cleanup, fireEvent, mockedStore, render, screen, waitForEuiToolTipVisible } from 'uiSrc/utils/test-utils'
import { GZIP_COMPRESSED_VALUE_1, GZIP_COMPRESSED_VALUE_2, DECOMPRESSED_VALUE_STR_1, DECOMPRESSED_VALUE_STR_2 } from 'uiSrc/utils/tests/decompressors'
import { setSelectedKeyRefreshDisabled } from 'uiSrc/slices/browser/keys'
import { HashDetailsTable, Props } from './HashDetailsTable'

const mockedProps = mock<Props>()
const fields: Array<{ field: RedisResponseBufferType, value: RedisResponseBufferType }> = [
const fields: Array<{ field: any, value: any }> = [
{ field: { type: 'Buffer', data: [49] }, value: { type: 'Buffer', data: [49, 65] } },
{ field: { type: 'Buffer', data: [49, 50, 51] }, value: { type: 'Buffer', data: [49, 11] } },
{ field: { type: 'Buffer', data: [50] }, value: { type: 'Buffer', data: [49, 234, 453] } },
Expand Down Expand Up @@ -39,6 +40,13 @@ jest.mock('uiSrc/slices/instances/instances', () => ({
}),
}))

let store: typeof mockedStore
beforeEach(() => {
cleanup()
store = cloneDeep(mockedStore)
store.clearActions()
})

describe('HashDetailsTable', () => {
it('should render', () => {
expect(render(<HashDetailsTable {...instance(mockedProps)} />)).toBeTruthy()
Expand Down Expand Up @@ -92,8 +100,8 @@ describe('HashDetailsTable', () => {
fields: [
{ field: anyToBuffer(GZIP_COMPRESSED_VALUE_1), value: anyToBuffer(GZIP_COMPRESSED_VALUE_2) },
]
})
hashDataSelector.mockImplementation(hashDataSelectorMock)
});
(hashDataSelector as jest.Mock).mockImplementation(hashDataSelectorMock)

const { queryByTestId, queryAllByTestId } = render(<HashDetailsTable {...instance(mockedProps)} />)
const fieldEl = queryAllByTestId(/hash-field-/)?.[0]
Expand All @@ -105,22 +113,22 @@ describe('HashDetailsTable', () => {

it('edit button should be disabled if data was compressed', async () => {
const defaultState = jest.requireActual('uiSrc/slices/browser/hash').initialState
const hashDataSelectorMock = jest.fn().mockReturnValue({
const hashDataSelectorMock = jest.fn().mockReturnValueOnce({
...defaultState,
total: 1,
key: '123zxczxczxc',
fields: [
{ field: anyToBuffer(GZIP_COMPRESSED_VALUE_1), value: anyToBuffer(GZIP_COMPRESSED_VALUE_2) },
]
})
hashDataSelector.mockImplementation(hashDataSelectorMock)
});
(hashDataSelector as jest.Mock).mockImplementationOnce(hashDataSelectorMock);

connectedInstanceSelector.mockImplementation(() => ({
(connectedInstanceSelector as jest.Mock).mockImplementationOnce(() => ({
compressor: KeyValueCompressor.GZIP,
}))

const { queryByTestId } = render(<HashDetailsTable {...instance(mockedProps)} />)
const editBtn = queryByTestId(/edit-hash-button/)
const editBtn = queryByTestId(/edit-hash-button/)!

fireEvent.click(editBtn)

Expand All @@ -134,4 +142,19 @@ describe('HashDetailsTable', () => {
expect(queryByTestId('hash-value-editor')).not.toBeInTheDocument()
})
})

it('should disable refresh after click on edit', async () => {
render(<HashDetailsTable {...instance(mockedProps)} />)

const afterRenderActions = [...store.getActions()]

await act(() => {
fireEvent.click(screen.queryAllByTestId(/edit-hash-button/)[0])
})

expect(store.getActions()).toEqual([
...afterRenderActions,
setSelectedKeyRefreshDisabled(true)
])
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ import {
updateHashFieldsAction,
updateHashValueStateSelector,
} from 'uiSrc/slices/browser/hash'
import { keysSelector, selectedKeyDataSelector, selectedKeySelector } from 'uiSrc/slices/browser/keys'
import {
keysSelector,
selectedKeyDataSelector,
selectedKeySelector,
setSelectedKeyRefreshDisabled
} from 'uiSrc/slices/browser/keys'
import { connectedInstanceSelector } from 'uiSrc/slices/instances/instances'
import { RedisResponseBuffer, RedisString } from 'uiSrc/slices/interfaces'
import { getBasedOnViewTypeEvent, getMatchType, sendEventTelemetry, TelemetryEvent } from 'uiSrc/telemetry'
Expand Down Expand Up @@ -120,6 +125,7 @@ const HashDetailsTable = (props: Props) => {
setExpandedRows([])
setViewFormat(viewFormatProp)
setEditingIndex(null)
dispatch(setSelectedKeyRefreshDisabled(false))

clearCache()
}
Expand Down Expand Up @@ -165,6 +171,7 @@ const HashDetailsTable = (props: Props) => {
valueItem?: RedisResponseBuffer
) => {
setEditingIndex(editing ? rowIndex : null)
dispatch(setSelectedKeyRefreshDisabled(editing))

if (editing) {
const value = bufferToSerializedFormat(viewFormat, valueItem, 4)
Expand Down
Loading