kibana/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/static_value.test.tsx
Marta Bondyra b87852071b
[Lens] fix passing 0 as static value (#118032)
* [Lens] fix passing 0 as static value

* allow computed static_value to be passed

* Update x-pack/plugins/lens/public/xy_visualization/reference_line_helpers.tsx

Co-authored-by: Marco Liberati <dej611@users.noreply.github.com>

* ci fix

Co-authored-by: Marco Liberati <dej611@users.noreply.github.com>
2021-11-11 08:26:48 +01:00

446 lines
12 KiB
TypeScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { shallow, mount } from 'enzyme';
import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
import { createMockedIndexPattern } from '../../mocks';
import { staticValueOperation } from './index';
import { IndexPattern, IndexPatternLayer } from '../../types';
import { StaticValueIndexPatternColumn } from './static_value';
import { EuiFieldNumber } from '@elastic/eui';
import { act } from 'react-dom/test-utils';
jest.mock('lodash', () => {
const original = jest.requireActual('lodash');
return {
...original,
debounce: (fn: unknown) => fn,
};
});
const uiSettingsMock = {} as IUiSettingsClient;
const defaultProps = {
storage: {} as IStorageWrapper,
uiSettings: uiSettingsMock,
savedObjectsClient: {} as SavedObjectsClientContract,
dateRange: { fromDate: 'now-1d', toDate: 'now' },
data: dataPluginMock.createStartContract(),
http: {} as HttpSetup,
indexPattern: {
...createMockedIndexPattern(),
hasRestrictions: false,
} as IndexPattern,
operationDefinitionMap: {},
isFullscreen: false,
toggleFullscreen: jest.fn(),
setIsCloseable: jest.fn(),
layerId: '1',
};
describe('static_value', () => {
let layer: IndexPatternLayer;
beforeEach(() => {
layer = {
indexPatternId: '1',
columnOrder: ['col1', 'col2'],
columns: {
col1: {
label: 'Top value of category',
dataType: 'string',
isBucketed: true,
operationType: 'terms',
params: {
orderBy: { type: 'alphabetical' },
size: 3,
orderDirection: 'asc',
},
sourceField: 'category',
},
col2: {
label: 'Static value: 23',
dataType: 'number',
isBucketed: false,
operationType: 'static_value',
references: [],
params: {
value: '23',
},
},
},
};
});
function getLayerWithStaticValue(newValue: string | null | undefined): IndexPatternLayer {
return {
...layer,
columns: {
...layer.columns,
col2: {
...layer.columns.col2,
label: `Static value: ${newValue ?? String(newValue)}`,
params: {
value: newValue,
},
} as StaticValueIndexPatternColumn,
},
};
}
describe('getDefaultLabel', () => {
it('should return the label for the given value', () => {
expect(
staticValueOperation.getDefaultLabel(
{
label: 'Static value: 23',
dataType: 'number',
isBucketed: false,
operationType: 'static_value',
references: [],
params: {
value: '23',
},
},
createMockedIndexPattern(),
layer.columns
)
).toBe('Static value: 23');
});
it('should return the default label for non valid value', () => {
expect(
staticValueOperation.getDefaultLabel(
{
label: 'Static value',
dataType: 'number',
isBucketed: false,
operationType: 'static_value',
references: [],
params: {
value: '',
},
},
createMockedIndexPattern(),
layer.columns
)
).toBe('Static value');
});
});
describe('getErrorMessage', () => {
it('should return no error for valid values', () => {
expect(
staticValueOperation.getErrorMessage!(
getLayerWithStaticValue('23'),
'col2',
createMockedIndexPattern()
)
).toBeUndefined();
// test for potential falsy value
expect(
staticValueOperation.getErrorMessage!(
getLayerWithStaticValue('0'),
'col2',
createMockedIndexPattern()
)
).toBeUndefined();
});
it.each(['NaN', 'Infinity', 'string'])(
'should return error for invalid values: %s',
(value) => {
expect(
staticValueOperation.getErrorMessage!(
getLayerWithStaticValue(value),
'col2',
createMockedIndexPattern()
)
).toEqual(expect.arrayContaining([expect.stringMatching('is not a valid number')]));
}
);
it.each([null, undefined])('should return no error for: %s', (value) => {
expect(
staticValueOperation.getErrorMessage!(
getLayerWithStaticValue(value),
'col2',
createMockedIndexPattern()
)
).toBe(undefined);
});
});
describe('toExpression', () => {
it('should return a mathColumn operation with valid value', () => {
for (const value of ['23', '0', '-1']) {
expect(
staticValueOperation.toExpression(
getLayerWithStaticValue(value),
'col2',
createMockedIndexPattern()
)
).toEqual([
{
type: 'function',
function: 'mathColumn',
arguments: {
id: ['col2'],
name: [`Static value: ${value}`],
expression: [value],
},
},
]);
}
});
it('should fallback to mapColumn for invalid value', () => {
for (const value of ['NaN', '', 'Infinity']) {
expect(
staticValueOperation.toExpression(
getLayerWithStaticValue(value),
'col2',
createMockedIndexPattern()
)
).toEqual([
{
type: 'function',
function: 'mapColumn',
arguments: {
id: ['col2'],
name: [`Static value`],
expression: ['100'],
},
},
]);
}
});
});
describe('buildColumn', () => {
it('should set default static value', () => {
expect(
staticValueOperation.buildColumn({
indexPattern: createMockedIndexPattern(),
layer: { columns: {}, columnOrder: [], indexPatternId: '' },
})
).toEqual({
label: 'Static value',
dataType: 'number',
operationType: 'static_value',
isBucketed: false,
scale: 'ratio',
params: { value: '100' },
references: [],
});
});
it('should merge a previousColumn', () => {
expect(
staticValueOperation.buildColumn({
indexPattern: createMockedIndexPattern(),
layer: { columns: {}, columnOrder: [], indexPatternId: '' },
previousColumn: {
label: 'Static value',
dataType: 'number',
operationType: 'static_value',
isBucketed: false,
scale: 'ratio',
params: { value: '23' },
references: [],
},
})
).toEqual({
label: 'Static value: 23',
dataType: 'number',
operationType: 'static_value',
isBucketed: false,
scale: 'ratio',
params: { value: '23' },
references: [],
});
});
it('should create a static_value from passed arguments', () => {
expect(
staticValueOperation.buildColumn(
{
indexPattern: createMockedIndexPattern(),
layer: { columns: {}, columnOrder: [], indexPatternId: '' },
},
{ value: '23' }
)
).toEqual({
label: 'Static value: 23',
dataType: 'number',
operationType: 'static_value',
isBucketed: false,
scale: 'ratio',
params: { value: '23' },
references: [],
});
});
it('should prioritize passed arguments over previousColumn', () => {
expect(
staticValueOperation.buildColumn(
{
indexPattern: createMockedIndexPattern(),
layer: { columns: {}, columnOrder: [], indexPatternId: '' },
previousColumn: {
label: 'Static value',
dataType: 'number',
operationType: 'static_value',
isBucketed: false,
scale: 'ratio',
params: { value: '23' },
references: [],
},
},
{ value: '53' }
)
).toEqual({
label: 'Static value: 53',
dataType: 'number',
operationType: 'static_value',
isBucketed: false,
scale: 'ratio',
params: { value: '53' },
references: [],
});
});
});
describe('paramEditor', () => {
const ParamEditor = staticValueOperation.paramEditor!;
it('should render current static_value', () => {
const updateLayerSpy = jest.fn();
const instance = shallow(
<ParamEditor
{...defaultProps}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col2"
currentColumn={layer.columns.col2 as StaticValueIndexPatternColumn}
/>
);
const input = instance.find('[data-test-subj="lns-indexPattern-static_value-input"]');
expect(input.prop('value')).toEqual('23');
});
it('should allow 0 as initial value', () => {
const updateLayerSpy = jest.fn();
const zeroLayer = {
...layer,
columns: {
...layer.columns,
col2: {
...layer.columns.col2,
operationType: 'static_value',
references: [],
params: {
value: '0',
},
},
},
} as IndexPatternLayer;
const instance = shallow(
<ParamEditor
{...defaultProps}
layer={zeroLayer}
updateLayer={updateLayerSpy}
columnId="col2"
currentColumn={zeroLayer.columns.col2 as StaticValueIndexPatternColumn}
/>
);
const input = instance.find('[data-test-subj="lns-indexPattern-static_value-input"]');
expect(input.prop('value')).toEqual('0');
});
it('should update state on change', async () => {
const updateLayerSpy = jest.fn();
const instance = mount(
<ParamEditor
{...defaultProps}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col2"
currentColumn={layer.columns.col2 as StaticValueIndexPatternColumn}
/>
);
const input = instance
.find('[data-test-subj="lns-indexPattern-static_value-input"]')
.find(EuiFieldNumber);
await act(async () => {
input.prop('onChange')!({
currentTarget: { value: '27' },
} as React.ChangeEvent<HTMLInputElement>);
});
instance.update();
expect(updateLayerSpy.mock.calls[0]).toEqual([expect.any(Function)]);
// check that the result of the setter call is correct
expect(updateLayerSpy.mock.calls[0][0](layer)).toEqual({
...layer,
columns: {
...layer.columns,
col2: {
...layer.columns.col2,
params: {
value: '27',
},
label: 'Static value: 27',
},
},
});
});
it('should not update on invalid input, but show invalid value locally', async () => {
const updateLayerSpy = jest.fn();
const instance = mount(
<ParamEditor
{...defaultProps}
layer={layer}
updateLayer={updateLayerSpy}
columnId="col2"
currentColumn={layer.columns.col2 as StaticValueIndexPatternColumn}
/>
);
const input = instance
.find('[data-test-subj="lns-indexPattern-static_value-input"]')
.find(EuiFieldNumber);
await act(async () => {
input.prop('onChange')!({
currentTarget: { value: '' },
} as React.ChangeEvent<HTMLInputElement>);
});
instance.update();
expect(updateLayerSpy).not.toHaveBeenCalled();
expect(
instance
.find('[data-test-subj="lns-indexPattern-static_value-input"]')
.find(EuiFieldNumber)
.prop('value')
).toEqual('');
});
});
});