mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Lens] Mocks refactoring for formula operations (#142777)
* ♻️ Refactor mocks * ♻️ First rewrite with operation mocks * Update x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/math_completion.test.ts Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com>
This commit is contained in:
parent
c38712a5be
commit
7e6df96968
3 changed files with 187 additions and 116 deletions
|
@ -11,7 +11,7 @@ import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks
|
|||
import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
|
||||
import { createMockedIndexPattern } from '../../../../mocks';
|
||||
import { GenericOperationDefinition } from '../..';
|
||||
import type { OperationMetadata, IndexPatternField } from '../../../../../types';
|
||||
import type { IndexPatternField, OperationMetadata } from '../../../../../types';
|
||||
import { tinymathFunctions } from '../util';
|
||||
import {
|
||||
getSignatureHelp,
|
||||
|
@ -21,9 +21,10 @@ import {
|
|||
offsetToRowColumn,
|
||||
getInfoAtZeroIndexedPosition,
|
||||
} from './math_completion';
|
||||
import { createOperationDefinitionMock } from '../mocks/operation_mocks';
|
||||
|
||||
const buildGenericColumn = (type: string) => {
|
||||
return ({ field }: { field?: IndexPatternField }) => {
|
||||
const buildGenericColumn = <T extends 'field' | 'fullReference' = 'field'>(type: string) => {
|
||||
return (({ field }: { field?: IndexPatternField }) => {
|
||||
return {
|
||||
label: type,
|
||||
dataType: 'number',
|
||||
|
@ -33,44 +34,39 @@ const buildGenericColumn = (type: string) => {
|
|||
scale: 'ratio',
|
||||
timeScale: false,
|
||||
};
|
||||
};
|
||||
}) as unknown as Extract<GenericOperationDefinition, { input: T }>['buildColumn'];
|
||||
};
|
||||
|
||||
const numericOperation = () => ({ dataType: 'number', isBucketed: false });
|
||||
const stringOperation = () => ({ dataType: 'string', isBucketed: true });
|
||||
// Mind the OperationMetadata shape here, it is very important for the field suggestions;
|
||||
// internally they are serialized and compared as strings
|
||||
const numericOperation = (): OperationMetadata => ({ dataType: 'number', isBucketed: false });
|
||||
const stringOperation = (): OperationMetadata => ({ dataType: 'string', isBucketed: true });
|
||||
|
||||
// Only one of each type is needed
|
||||
const operationDefinitionMap: Record<string, GenericOperationDefinition> = {
|
||||
sum: {
|
||||
type: 'sum',
|
||||
input: 'field',
|
||||
buildColumn: buildGenericColumn('sum'),
|
||||
getPossibleOperationForField: (field: IndexPatternField) =>
|
||||
field.type === 'number' ? numericOperation() : null,
|
||||
sum: createOperationDefinitionMock('sum', {
|
||||
getPossibleOperationForField: jest.fn((field: IndexPatternField) =>
|
||||
field.type === 'number' ? numericOperation() : undefined
|
||||
),
|
||||
documentation: {
|
||||
section: 'elasticsearch',
|
||||
signature: 'field: string',
|
||||
description: 'description',
|
||||
},
|
||||
} as unknown as GenericOperationDefinition,
|
||||
count: {
|
||||
type: 'count',
|
||||
input: 'field',
|
||||
buildColumn: buildGenericColumn('count'),
|
||||
}),
|
||||
count: createOperationDefinitionMock('count', {
|
||||
getPossibleOperationForField: (field: IndexPatternField) =>
|
||||
field.name === '___records___' ? numericOperation() : null,
|
||||
} as unknown as GenericOperationDefinition,
|
||||
last_value: {
|
||||
type: 'last_value',
|
||||
input: 'field',
|
||||
field.name === '___records___' ? numericOperation() : undefined,
|
||||
}),
|
||||
last_value: createOperationDefinitionMock('last_value', {
|
||||
buildColumn: buildGenericColumn('last_value'),
|
||||
getPossibleOperationForField: (field: IndexPatternField) => ({
|
||||
dataType: field.type,
|
||||
isBucketed: false,
|
||||
}),
|
||||
} as unknown as GenericOperationDefinition,
|
||||
moving_average: {
|
||||
type: 'moving_average',
|
||||
getPossibleOperationForField: (field: IndexPatternField) =>
|
||||
({
|
||||
dataType: field.type,
|
||||
isBucketed: false,
|
||||
} as OperationMetadata),
|
||||
}),
|
||||
moving_average: createOperationDefinitionMock('moving_average', {
|
||||
input: 'fullReference',
|
||||
requiredReferences: [
|
||||
{
|
||||
|
@ -80,20 +76,21 @@ const operationDefinitionMap: Record<string, GenericOperationDefinition> = {
|
|||
},
|
||||
],
|
||||
operationParams: [{ name: 'window', type: 'number', required: true }],
|
||||
buildColumn: buildGenericColumn('moving_average'),
|
||||
getPossibleOperation: numericOperation,
|
||||
} as unknown as GenericOperationDefinition,
|
||||
cumulative_sum: {
|
||||
type: 'cumulative_sum',
|
||||
buildColumn: buildGenericColumn<'fullReference'>('moving_average'),
|
||||
}),
|
||||
cumulative_sum: createOperationDefinitionMock('cumulative_sum', {
|
||||
input: 'fullReference',
|
||||
buildColumn: buildGenericColumn('cumulative_sum'),
|
||||
getPossibleOperation: numericOperation,
|
||||
} as unknown as GenericOperationDefinition,
|
||||
terms: {
|
||||
type: 'terms',
|
||||
input: 'field',
|
||||
getPossibleOperationForField: stringOperation,
|
||||
} as unknown as GenericOperationDefinition,
|
||||
buildColumn: buildGenericColumn<'fullReference'>('cumulative_sum'),
|
||||
}),
|
||||
terms: createOperationDefinitionMock(
|
||||
'terms',
|
||||
{
|
||||
getPossibleOperationForField: stringOperation,
|
||||
},
|
||||
{
|
||||
scale: 'ordinal',
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
describe('math completion', () => {
|
||||
|
|
|
@ -6,16 +6,21 @@
|
|||
*/
|
||||
|
||||
import { createMockedIndexPattern } from '../../../mocks';
|
||||
import { formulaOperation, GenericOperationDefinition, GenericIndexPatternColumn } from '..';
|
||||
import { FormulaIndexPatternColumn } from './formula';
|
||||
import {
|
||||
formulaOperation,
|
||||
type GenericOperationDefinition,
|
||||
type GenericIndexPatternColumn,
|
||||
} from '..';
|
||||
import type { FormulaIndexPatternColumn } from './formula';
|
||||
import { insertOrReplaceFormulaColumn } from './parse';
|
||||
import type { IndexPatternLayer } from '../../../types';
|
||||
import { IndexPattern, IndexPatternField } from '../../../../types';
|
||||
import { IndexPattern } from '../../../../types';
|
||||
import { tinymathFunctions } from './util';
|
||||
import { TermsIndexPatternColumn } from '../terms';
|
||||
import { MovingAverageIndexPatternColumn } from '../calculations';
|
||||
import { StaticValueIndexPatternColumn } from '../static_value';
|
||||
import { getFilter } from '../helpers';
|
||||
import { createOperationDefinitionMock } from './mocks/operation_mocks';
|
||||
|
||||
jest.mock('../../layer_helpers', () => {
|
||||
return {
|
||||
|
@ -26,89 +31,38 @@ jest.mock('../../layer_helpers', () => {
|
|||
};
|
||||
});
|
||||
|
||||
interface PartialColumnParams {
|
||||
kql?: string;
|
||||
lucene?: string;
|
||||
shift?: string;
|
||||
}
|
||||
|
||||
const operationDefinitionMap: Record<string, GenericOperationDefinition> = {
|
||||
average: {
|
||||
input: 'field',
|
||||
buildColumn: ({ field }: { field: IndexPatternField }) => ({
|
||||
label: 'avg',
|
||||
dataType: 'number',
|
||||
operationType: 'average',
|
||||
sourceField: field.name,
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
timeScale: false,
|
||||
}),
|
||||
getPossibleOperationForField: () => ({ scale: 'ratio' }),
|
||||
} as unknown as GenericOperationDefinition,
|
||||
terms: {
|
||||
input: 'field',
|
||||
getPossibleOperationForField: () => ({ scale: 'ordinal' }),
|
||||
} as unknown as GenericOperationDefinition,
|
||||
sum: {
|
||||
input: 'field',
|
||||
filterable: true,
|
||||
getPossibleOperationForField: () => ({ scale: 'ratio' }),
|
||||
} as unknown as GenericOperationDefinition,
|
||||
last_value: {
|
||||
input: 'field',
|
||||
getPossibleOperationForField: ({ type }) => ({
|
||||
average: createOperationDefinitionMock('average', {}, { label: 'avg' }),
|
||||
terms: createOperationDefinitionMock('terms', {}, { scale: 'ordinal' }),
|
||||
sum: createOperationDefinitionMock('sum', { filterable: true }),
|
||||
last_value: createOperationDefinitionMock('last_value', {
|
||||
getPossibleOperationForField: jest.fn(({ type }) => ({
|
||||
scale: type === 'string' ? 'ordinal' : 'ratio',
|
||||
}),
|
||||
} as GenericOperationDefinition,
|
||||
max: {
|
||||
input: 'field',
|
||||
getPossibleOperationForField: () => ({ scale: 'ratio' }),
|
||||
} as unknown as GenericOperationDefinition,
|
||||
count: {
|
||||
input: 'field',
|
||||
filterable: true,
|
||||
buildColumn: ({ field }: { field: IndexPatternField }, columnsParams: PartialColumnParams) => ({
|
||||
label: 'avg',
|
||||
dataType: 'number',
|
||||
operationType: 'count',
|
||||
sourceField: field.name,
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
timeScale: false,
|
||||
filter: getFilter(undefined, columnsParams),
|
||||
}),
|
||||
getPossibleOperationForField: () => ({ scale: 'ratio' }),
|
||||
} as unknown as GenericOperationDefinition,
|
||||
derivative: {
|
||||
input: 'fullReference',
|
||||
getPossibleOperationForField: () => ({ scale: 'ratio' }),
|
||||
} as unknown as GenericOperationDefinition,
|
||||
moving_average: {
|
||||
dataType: type === 'string' ? type : 'number',
|
||||
})),
|
||||
}),
|
||||
max: createOperationDefinitionMock('max'),
|
||||
count: createOperationDefinitionMock('count', { filterable: true, canReduceTimeRange: true }),
|
||||
derivative: createOperationDefinitionMock('derivative', { input: 'fullReference' }),
|
||||
moving_average: createOperationDefinitionMock('moving_average', {
|
||||
input: 'fullReference',
|
||||
operationParams: [{ name: 'window', type: 'number', required: true }],
|
||||
buildColumn: (
|
||||
{ references }: { references: string[] },
|
||||
columnsParams: PartialColumnParams
|
||||
) => ({
|
||||
filterable: true,
|
||||
getErrorMessage: jest.fn(() => ['mock error']),
|
||||
buildColumn: ({ referenceIds }, columnsParams) => ({
|
||||
label: 'moving_average',
|
||||
dataType: 'number',
|
||||
operationType: 'moving_average',
|
||||
isBucketed: false,
|
||||
scale: 'ratio',
|
||||
timeScale: false,
|
||||
timeScale: undefined,
|
||||
params: { window: 5 },
|
||||
references,
|
||||
references: referenceIds,
|
||||
filter: getFilter(undefined, columnsParams),
|
||||
}),
|
||||
getErrorMessage: () => ['mock error'],
|
||||
getPossibleOperationForField: () => ({ scale: 'ratio' }),
|
||||
filterable: true,
|
||||
} as unknown as GenericOperationDefinition,
|
||||
cumulative_sum: {
|
||||
input: 'fullReference',
|
||||
getPossibleOperationForField: () => ({ scale: 'ratio' }),
|
||||
} as unknown as GenericOperationDefinition,
|
||||
}),
|
||||
cumulative_sum: createOperationDefinitionMock('cumulative_sum', { input: 'fullReference' }),
|
||||
};
|
||||
|
||||
describe('formula', () => {
|
||||
|
@ -550,7 +504,7 @@ describe('formula', () => {
|
|||
operationType: 'average',
|
||||
scale: 'ratio',
|
||||
sourceField: 'bytes',
|
||||
timeScale: false,
|
||||
timeScale: undefined,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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 type { GenericOperationDefinition } from '../..';
|
||||
import type { GenericIndexPatternColumn } from '../../column_types';
|
||||
import { getFilter } from '../../helpers';
|
||||
|
||||
interface PartialColumnParams {
|
||||
kql?: string;
|
||||
lucene?: string;
|
||||
shift?: string;
|
||||
reducedTimeRange?: string;
|
||||
}
|
||||
|
||||
type OperationByInputType<Input extends GenericOperationDefinition['input']> = Extract<
|
||||
GenericOperationDefinition,
|
||||
{ input: Input }
|
||||
>;
|
||||
|
||||
export function createOperationDefinitionMock(
|
||||
operation: string,
|
||||
{
|
||||
input = 'field',
|
||||
getErrorMessage,
|
||||
buildColumn,
|
||||
...params
|
||||
}: Partial<GenericOperationDefinition> = {},
|
||||
{
|
||||
label = operation,
|
||||
dataType = 'number',
|
||||
isBucketed = false,
|
||||
scale = 'ratio',
|
||||
timeScale,
|
||||
}: Partial<GenericIndexPatternColumn> = {}
|
||||
): OperationByInputType<typeof input> {
|
||||
const sharedColumnParams = {
|
||||
label,
|
||||
dataType,
|
||||
operationType: operation,
|
||||
isBucketed,
|
||||
scale,
|
||||
timeScale,
|
||||
};
|
||||
const sharedDefinitionParams = {
|
||||
input,
|
||||
getDefaultLabel: jest.fn(),
|
||||
isTransferable: jest.fn(),
|
||||
displayName: label,
|
||||
type: operation,
|
||||
getErrorMessage: getErrorMessage ?? jest.fn(),
|
||||
toExpression: jest.fn(),
|
||||
};
|
||||
if (input === 'field') {
|
||||
return {
|
||||
buildColumn:
|
||||
buildColumn ??
|
||||
jest.fn(({ field }, columnParams: PartialColumnParams) => ({
|
||||
sourceField: field.name,
|
||||
filter: getFilter(undefined, columnParams),
|
||||
reducedTimeRange: columnParams.reducedTimeRange,
|
||||
...sharedColumnParams,
|
||||
})),
|
||||
onFieldChange: jest.fn(),
|
||||
toEsAggsFn: jest.fn(),
|
||||
getPossibleOperationForField:
|
||||
(params as OperationByInputType<typeof input>).getPossibleOperationForField ??
|
||||
jest.fn((arg) => ({
|
||||
scale,
|
||||
dataType,
|
||||
isBucketed,
|
||||
})),
|
||||
...sharedDefinitionParams,
|
||||
...params,
|
||||
} as OperationByInputType<typeof input>;
|
||||
}
|
||||
if (input === 'fullReference') {
|
||||
return {
|
||||
buildColumn:
|
||||
buildColumn ??
|
||||
jest.fn(({ referenceIds }, columnParams: PartialColumnParams) => ({
|
||||
references: referenceIds,
|
||||
filter: getFilter(undefined, columnParams),
|
||||
...sharedColumnParams,
|
||||
})),
|
||||
onFieldChange: jest.fn(),
|
||||
toEsAggsFn: jest.fn(),
|
||||
getPossibleOperation: jest.fn(() => ({
|
||||
scale: 'ratio',
|
||||
dataType: 'number',
|
||||
isBucketed: false,
|
||||
})),
|
||||
requiredReferences: [
|
||||
{
|
||||
input: ['field', 'managedReference'],
|
||||
validateMetadata: jest.fn(),
|
||||
},
|
||||
],
|
||||
selectionStyle: 'field',
|
||||
...sharedDefinitionParams,
|
||||
...params,
|
||||
} as OperationByInputType<typeof input>;
|
||||
}
|
||||
return {
|
||||
buildColumn:
|
||||
buildColumn ??
|
||||
jest.fn((_, columnsParams: PartialColumnParams) => ({
|
||||
references: [],
|
||||
filter: getFilter(undefined, columnsParams),
|
||||
...sharedColumnParams,
|
||||
})),
|
||||
getPossibleOperation: jest.fn(),
|
||||
createCopy: jest.fn(),
|
||||
...sharedDefinitionParams,
|
||||
...params,
|
||||
} as OperationByInputType<typeof input>;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue