[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:
Marco Liberati 2022-10-10 12:25:19 +02:00 committed by GitHub
parent c38712a5be
commit 7e6df96968
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 187 additions and 116 deletions

View file

@ -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', () => {

View file

@ -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,
},
},
});

View file

@ -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>;
}