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
17 changes: 17 additions & 0 deletions redisinsight/ui/src/components/query-card/QueryCard.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,21 @@ describe('QueryCard', () => {

expect(summaryString).toEqual(summaryText)
})

it('should render QueryCardCliResultWrapper when command is null', () => {
const { queryByTestId } = render(
<QueryCard
{...instance(mockedProps)}
resultsMode={ResultsMode.GroupMode}
result={null}
isOpen
command={null}
/>
)
const queryCommonResultEl = queryByTestId('query-common-result-wrapper')
const queryCliResultEl = queryByTestId('query-cli-result-wrapper')

expect(queryCommonResultEl).toBeInTheDocument()
expect(queryCliResultEl).not.toBeInTheDocument()
})
})
3 changes: 2 additions & 1 deletion redisinsight/ui/src/components/query-card/QueryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useDispatch, useSelector } from 'react-redux'
import cx from 'classnames'
import { EuiLoadingContent, keys } from '@elastic/eui'
import { useParams } from 'react-router-dom'
import { isNull } from 'lodash'

import { WBQueryType, ProfileQueryType, DEFAULT_TEXT_VIEW_TYPE } from 'uiSrc/pages/workbench/constants'
import { RunQueryMode, ResultsMode, ResultsSummary } from 'uiSrc/slices/interfaces/workbench'
Expand Down Expand Up @@ -194,7 +195,7 @@ const QueryCard = (props: Props) => {
/>
{isOpen && (
<>
{React.isValidElement(commonError) && !isGroupResults(resultsMode)
{React.isValidElement(commonError) && (!isGroupResults(resultsMode) || isNull(command))
? <QueryCardCommonResult loading={loading} result={commonError} />
: (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React from 'react'
import { render } from 'uiSrc/utils/test-utils'
import QueryCardCliGroupResult from './QueryCardCliGroupResult'
import { instance, mock } from 'ts-mockito'
import { render, screen } from 'uiSrc/utils/test-utils'
import { CommandExecutionStatus } from 'uiSrc/slices/interfaces/cli'
import QueryCardCliGroupResult, { Props } from './QueryCardCliGroupResult'

const mockedProps = mock<Props>()

describe('QueryCardCliGroupResult', () => {
it('should render', () => {
Expand All @@ -11,10 +15,59 @@ describe('QueryCardCliGroupResult', () => {
}],
status: 'success'
}]
expect(render(<QueryCardCliGroupResult result={mockResult} />)).toBeTruthy()
expect(render(<QueryCardCliGroupResult {...instance(mockedProps)} result={mockResult} />)).toBeTruthy()
})

it('Should render result when result is undefined', () => {
expect(render(<QueryCardCliGroupResult />)).toBeTruthy()
expect(render(<QueryCardCliGroupResult {...instance(mockedProps)} />)).toBeTruthy()
})

it('should render error when command is psubscribe', () => {
const mockResult = [
{
response: [
{
id: 'id',
command: 'psubscribe',
response: 'response',
status: CommandExecutionStatus.Success
}
]
}
]
const { container } = render(
<QueryCardCliGroupResult
{...instance(mockedProps)}
result={mockResult}
/>
)
const errorBtn = container.querySelector('[data-test-subj="pubsub-page-btn"]')

expect(errorBtn).toBeInTheDocument()
})

it('should render (nil) when response is null', () => {
const mockResult = [
{
response: [
{
id: 'id',
command: 'psubscribe',
response: null,
status: CommandExecutionStatus.Success
}
]
}
]
const { container } = render(
<QueryCardCliGroupResult
{...instance(mockedProps)}
result={mockResult}
/>
)
const errorBtn = container.querySelector('[data-test-subj="pubsub-page-btn"]')

expect(errorBtn).not.toBeInTheDocument()
expect(screen.getByText('(nil)')).toBeInTheDocument()
})
})
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { flatten } from 'lodash'
import { flatten, isNull } from 'lodash'
import React from 'react'

import { CommandExecutionResult } from 'uiSrc/slices/interfaces'
Expand All @@ -21,7 +21,7 @@ const QueryCardCliGroupResult = (props: Props) => {
isFullScreen={isFullScreen}
items={flatten(result?.[0]?.response.map((item: any) => {
const commonError = CommonErrorResponse(item.id, item.command, item.response)
if (React.isValidElement(commonError)) {
if (React.isValidElement(commonError) && !isNull(item.response)) {
return ([wbSummaryCommand(item.command), commonError])
}
return flatten(cliParseCommandsGroupResult(item, db))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { instance, mock } from 'ts-mockito'
import { PluginEvents } from 'uiSrc/plugins/pluginEvents'
import { pluginApi } from 'uiSrc/services/PluginAPI'
import { cleanup, mockedStore, render } from 'uiSrc/utils/test-utils'
import { formatToText } from 'uiSrc/utils'
import { formatToText, replaceEmptyValue } from 'uiSrc/utils'
import { sendPluginCommandAction, getPluginStateAction, setPluginStateAction } from 'uiSrc/slices/app/plugins'
import QueryCardCliPlugin, { Props } from './QueryCardCliPlugin'

Expand All @@ -19,7 +19,8 @@ jest.mock('uiSrc/services/PluginAPI', () => ({

jest.mock('uiSrc/utils', () => ({
...jest.requireActual('uiSrc/utils'),
formatToText: jest.fn()
formatToText: jest.fn(),
replaceEmptyValue: jest.fn(),
}))

jest.mock('uiSrc/slices/app/plugins', () => ({
Expand Down Expand Up @@ -156,7 +157,9 @@ describe('QueryCardCliPlugin', () => {
})

it('should subscribes and call formatToText', () => {
const formatToTextMock = jest.fn();
const formatToTextMock = jest.fn()
const replaceEmptyValueMock = jest.fn();
(replaceEmptyValue as jest.Mock).mockImplementation(replaceEmptyValueMock).mockReturnValue([]);
(formatToText as jest.Mock).mockImplementation(formatToTextMock)
const onEventMock = jest.fn().mockImplementation(
(_iframeId: string, event: string, callback: (dat: any) => void) => {
Expand All @@ -172,4 +175,22 @@ describe('QueryCardCliPlugin', () => {

expect(formatToTextMock).toBeCalledWith([], 'info')
})

it('should subscribes and call replaceEmptyValue', () => {
const replaceEmptyValueMock = jest.fn();
(replaceEmptyValue as jest.Mock).mockImplementation(replaceEmptyValueMock)
const onEventMock = jest.fn().mockImplementation(
(_iframeId: string, event: string, callback: (dat: any) => void) => {
if (event === PluginEvents.formatRedisReply) {
callback({ requestId: '1', data: { response: [], command: 'info' } })
}
}
);

(pluginApi.onEvent as jest.Mock).mockImplementation(onEventMock)

render(<QueryCardCliPlugin {...instance(mockedProps)} id="1" />)

expect(replaceEmptyValueMock).toBeCalledWith([])
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid'
import { EuiFlexItem, EuiIcon, EuiLoadingContent, EuiTextColor } from '@elastic/eui'
import { pluginApi } from 'uiSrc/services/PluginAPI'
import { ThemeContext } from 'uiSrc/contexts/themeContext'
import { getBaseApiUrl, Nullable, formatToText } from 'uiSrc/utils'
import { getBaseApiUrl, Nullable, formatToText, replaceEmptyValue } from 'uiSrc/utils'
import { Theme } from 'uiSrc/constants'
import { CommandExecutionResult, IPluginVisualization, RunQueryMode } from 'uiSrc/slices/interfaces'
import { PluginEvents } from 'uiSrc/plugins/pluginEvents'
Expand Down Expand Up @@ -161,7 +161,7 @@ const QueryCardCliPlugin = (props: Props) => {
{ requestId, data }: { requestId: string, data: { response: any, command: string } }
) => {
try {
const reply = formatToText(data?.response || '(nil)', data.command)
const reply = formatToText(replaceEmptyValue(data?.response), data.command)

sendMessageToPlugin({
event: PluginEvents.formatRedisReply,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { isArray } from 'lodash'

import { CommandExecutionResult } from 'uiSrc/slices/interfaces'
import { ResultsMode } from 'uiSrc/slices/interfaces/workbench'
import { cliParseTextResponse, formatToText, isGroupResults, Maybe } from 'uiSrc/utils'
import { cliParseTextResponse, formatToText, replaceEmptyValue, isGroupResults, Maybe } from 'uiSrc/utils'

import { CommandExecutionStatus } from 'uiSrc/slices/interfaces/cli'
import QueryCardCliDefaultResult from '../QueryCardCliDefaultResult'
Expand All @@ -16,7 +16,6 @@ export interface Props {
query: string
result: Maybe<CommandExecutionResult[]>
loading?: boolean
status?: string
resultsMode?: ResultsMode
isNotStored?: boolean
isFullScreen?: boolean
Expand All @@ -27,7 +26,7 @@ const QueryCardCliResultWrapper = (props: Props) => {
const { result = [], query, loading, resultsMode, isNotStored, isFullScreen, db } = props

return (
<div className={cx('queryResultsContainer', styles.container)}>
<div data-testid="query-cli-result-wrapper" className={cx('queryResultsContainer', styles.container)}>
{!loading && (
<div data-testid="query-cli-result" className={cx(styles.content)}>
{isNotStored && (
Expand All @@ -42,9 +41,9 @@ const QueryCardCliResultWrapper = (props: Props) => {
<QueryCardCliDefaultResult
isFullScreen={isFullScreen}
items={
result[0].status === CommandExecutionStatus.Success
? formatToText(result[0].response || '(nil)', query).split('\n')
: [cliParseTextResponse(result[0].response || '(nil)', '', result[0].status)]
result[0]?.status === CommandExecutionStatus.Success
? formatToText(replaceEmptyValue(result[0]?.response), query).split('\n')
: [cliParseTextResponse(replaceEmptyValue(result[0]?.response), '', result[0]?.status)]
}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const QueryCardCommonResult = (props: Props) => {
const { result, loading } = props

return (
<div className={cx('queryResultsContainer', styles.container)}>
<div data-testid="query-common-result-wrapper" className={cx('queryResultsContainer', styles.container)}>
{!loading && (
<div data-testid="query-common-result">
{ result || '(nil)' }
Expand Down
12 changes: 11 additions & 1 deletion redisinsight/ui/src/utils/cliHelper.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import { Dispatch, PayloadAction } from '@reduxjs/toolkit'
import parse from 'html-react-parser'
import { isUndefined } from 'lodash'

import { localStorageService } from 'uiSrc/services'
import { CommandExecutionStatus } from 'uiSrc/slices/interfaces/cli'
Expand Down Expand Up @@ -49,6 +50,14 @@ const cliParseTextResponseWithOffset = (
status: CommandExecutionStatus = CommandExecutionStatus.Success,
) => [cliParseTextResponse(text, command, status), '\n']

const replaceEmptyValue = (value: any) => {
if (
isUndefined(value) || value === '' || value === false) {
return '(nil)'
}
return value
}

const cliParseTextResponse = (
text: string | JSX.Element = '',
command: string = '',
Expand Down Expand Up @@ -104,7 +113,7 @@ const cliParseCommandsGroupResult = (

let executionResult = []
if (result.status === CommandExecutionStatus.Success) {
executionResult = formatToText(result.response || '(nil)', result.command).split('\n')
executionResult = formatToText(replaceEmptyValue(result.response), result.command).split('\n')
} else {
executionResult = [cliParseTextResponse(result.response || '(nil)', result.command, result.status)]
}
Expand Down Expand Up @@ -229,4 +238,5 @@ export {
getDbIndexFromSelectQuery,
getCommandNameFromQuery,
wbSummaryCommand,
replaceEmptyValue,
}
18 changes: 18 additions & 0 deletions redisinsight/ui/src/utils/tests/cliHelper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
checkCommandModule,
checkUnsupportedCommand,
checkBlockingCommand,
replaceEmptyValue,
} from 'uiSrc/utils'
import { MOCK_COMMANDS_SPEC } from 'uiSrc/constants'
import { render, screen } from 'uiSrc/utils/test-utils'
Expand All @@ -29,6 +30,23 @@ const getDbIndexFromSelectQueryTests = [
{ input: 'select ', expected: new Error('Parsing error') },
]

const replaceEmptyValueTests = [
{ input: '', expected: '(nil)' },
{ input: undefined, expected: '(nil)' },
{ input: false, expected: '(nil)' },
{ input: 'string', expected: 'string' },
{ input: 0, expected: 0 },
{ input: 1, expected: 1 },
{ input: [], expected: [] },
{ input: {}, expected: {} },
]

describe('replaceEmptyValue', () => {
test.each(replaceEmptyValueTests)('%j', ({ input, expected }) => {
expect(replaceEmptyValue(input)).toEqual(expected)
})
})

describe('getDbIndexFromSelectQuery', () => {
test.each(getDbIndexFromSelectQueryTests)('%j', ({ input, expected }) => {
if (expected instanceof Error) {
Expand Down