[FieldFormats] Add editors tests (#107770)

This commit is contained in:
Anton Dosov 2021-08-10 15:44:24 +02:00 committed by GitHub
parent 7d6a2b520f
commit 6450df1885
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 655 additions and 55 deletions

View file

@ -43,6 +43,7 @@ exports[`BytesFormatEditor should render normally 1`] = `
labelType="label"
>
<EuiFieldText
data-test-subj="numberEditorFormatPattern"
isInvalid={false}
onChange={[Function]}
placeholder="0,0.[000]b"

View file

@ -45,6 +45,7 @@ exports[`ColorFormatEditor should render multiple colors 1`] = `
Object {
"available": [Function],
"color": "danger",
"data-test-subj": "colorEditorRemoveColor",
"description": "Delete color format",
"icon": "trash",
"name": "Delete",
@ -83,6 +84,7 @@ exports[`ColorFormatEditor should render multiple colors 1`] = `
size="m"
/>
<EuiButton
data-test-subj="colorEditorAddColor"
iconType="plusInCircle"
onClick={[Function]}
size="s"
@ -144,6 +146,7 @@ exports[`ColorFormatEditor should render other type normally (range field) 1`] =
Object {
"available": [Function],
"color": "danger",
"data-test-subj": "colorEditorRemoveColor",
"description": "Delete color format",
"icon": "trash",
"name": "Delete",
@ -175,6 +178,7 @@ exports[`ColorFormatEditor should render other type normally (range field) 1`] =
size="m"
/>
<EuiButton
data-test-subj="colorEditorAddColor"
iconType="plusInCircle"
onClick={[Function]}
size="s"
@ -236,6 +240,7 @@ exports[`ColorFormatEditor should render string type normally (regex field) 1`]
Object {
"available": [Function],
"color": "danger",
"data-test-subj": "colorEditorRemoveColor",
"description": "Delete color format",
"icon": "trash",
"name": "Delete",
@ -267,6 +272,7 @@ exports[`ColorFormatEditor should render string type normally (regex field) 1`]
size="m"
/>
<EuiButton
data-test-subj="colorEditorAddColor"
iconType="plusInCircle"
onClick={[Function]}
size="s"

View file

@ -96,6 +96,7 @@ export class ColorFormatEditor extends DefaultFormatEditor<ColorFormatEditorForm
return (
<EuiFieldText
value={value}
data-test-subj={`colorEditorKeyPattern ${item.index}`}
onChange={(e) => {
this.onColorChange(
{
@ -120,6 +121,7 @@ export class ColorFormatEditor extends DefaultFormatEditor<ColorFormatEditorForm
return (
<EuiFieldText
value={value}
data-test-subj={`colorEditorKeyRange ${item.index}`}
onChange={(e) => {
this.onColorChange(
{
@ -144,6 +146,7 @@ export class ColorFormatEditor extends DefaultFormatEditor<ColorFormatEditorForm
return (
<EuiColorPicker
color={color}
data-test-subj={`colorEditorColorPicker ${item.index}`}
onChange={(newColor) => {
this.onColorChange(
{
@ -168,6 +171,7 @@ export class ColorFormatEditor extends DefaultFormatEditor<ColorFormatEditorForm
return (
<EuiColorPicker
color={color}
data-test-subj={`colorEditorBackgroundPicker ${item.index}`}
onChange={(newColor) => {
this.onColorChange(
{
@ -220,6 +224,7 @@ export class ColorFormatEditor extends DefaultFormatEditor<ColorFormatEditorForm
icon: 'trash',
color: 'danger',
available: () => items.length > 1,
'data-test-subj': 'colorEditorRemoveColor',
},
],
},
@ -229,7 +234,12 @@ export class ColorFormatEditor extends DefaultFormatEditor<ColorFormatEditorForm
<Fragment>
<EuiBasicTable items={items} columns={columns} />
<EuiSpacer size="m" />
<EuiButton iconType="plusInCircle" size="s" onClick={this.addColor}>
<EuiButton
iconType="plusInCircle"
size="s"
onClick={this.addColor}
data-test-subj={'colorEditorAddColor'}
>
<FormattedMessage
id="indexPatternFieldEditor.color.addColorButton"
defaultMessage="Add color"

View file

@ -19,6 +19,7 @@ exports[`DurationFormatEditor should not render show suffix on dynamic output 1`
labelType="label"
>
<EuiSelect
data-test-subj="durationEditorInputFormat"
isInvalid={false}
onChange={[Function]}
options={
@ -49,6 +50,7 @@ exports[`DurationFormatEditor should not render show suffix on dynamic output 1`
labelType="label"
>
<EuiSelect
data-test-subj="durationEditorOutputFormat"
isInvalid={false}
onChange={[Function]}
options={
@ -198,6 +200,7 @@ exports[`DurationFormatEditor should render human readable output normally 1`] =
labelType="label"
>
<EuiSelect
data-test-subj="durationEditorInputFormat"
isInvalid={false}
onChange={[Function]}
options={
@ -228,6 +231,7 @@ exports[`DurationFormatEditor should render human readable output normally 1`] =
labelType="label"
>
<EuiSelect
data-test-subj="durationEditorOutputFormat"
isInvalid={false}
onChange={[Function]}
options={
@ -310,6 +314,7 @@ exports[`DurationFormatEditor should render non-human readable output normally 1
labelType="label"
>
<EuiSelect
data-test-subj="durationEditorInputFormat"
isInvalid={false}
onChange={[Function]}
options={
@ -340,6 +345,7 @@ exports[`DurationFormatEditor should render non-human readable output normally 1
labelType="label"
>
<EuiSelect
data-test-subj="durationEditorOutputFormat"
isInvalid={false}
onChange={[Function]}
options={

View file

@ -103,6 +103,7 @@ export class DurationFormatEditor extends DefaultFormatEditor<
error={hasDecimalError ? null : error}
>
<EuiSelect
data-test-subj={'durationEditorInputFormat'}
value={formatParams.inputFormat}
options={format.type.inputFormats.map((fmt: InputFormat) => {
return {
@ -126,6 +127,7 @@ export class DurationFormatEditor extends DefaultFormatEditor<
isInvalid={!!error}
>
<EuiSelect
data-test-subj={'durationEditorOutputFormat'}
value={formatParams.outputFormat}
options={format.type.outputFormats.map((fmt: OutputFormat) => {
return {

View file

@ -43,6 +43,7 @@ exports[`NumberFormatEditor should render normally 1`] = `
labelType="label"
>
<EuiFieldText
data-test-subj="numberEditorFormatPattern"
isInvalid={false}
onChange={[Function]}
placeholder="0,0.[000]"

View file

@ -66,6 +66,7 @@ export class NumberFormatEditor extends DefaultFormatEditor<NumberFormatEditorPa
error={error}
>
<EuiFieldText
data-test-subj={'numberEditorFormatPattern'}
value={formatParams.pattern}
placeholder={defaultPattern}
onChange={(e) => {

View file

@ -43,6 +43,7 @@ exports[`PercentFormatEditor should render normally 1`] = `
labelType="label"
>
<EuiFieldText
data-test-subj="numberEditorFormatPattern"
isInvalid={false}
onChange={[Function]}
placeholder="0,0.[000]%"

View file

@ -28,6 +28,7 @@ exports[`StaticLookupFormatEditor should render multiple lookup entries and unkn
Object {
"available": [Function],
"color": "danger",
"data-test-subj": "staticLookupEditorRemoveEntry",
"description": "Delete entry",
"icon": "trash",
"name": "Delete",
@ -67,6 +68,7 @@ exports[`StaticLookupFormatEditor should render multiple lookup entries and unkn
size="m"
/>
<EuiButton
data-test-subj="staticLookupEditorAddEntry"
iconType="plusInCircle"
onClick={[Function]}
size="s"
@ -96,6 +98,7 @@ exports[`StaticLookupFormatEditor should render multiple lookup entries and unkn
labelType="label"
>
<EuiFieldText
data-test-subj="staticLookupEditorUnknownValue"
onChange={[Function]}
placeholder="Leave blank to keep value as-is"
value="test value"
@ -135,6 +138,7 @@ exports[`StaticLookupFormatEditor should render normally 1`] = `
Object {
"available": [Function],
"color": "danger",
"data-test-subj": "staticLookupEditorRemoveEntry",
"description": "Delete entry",
"icon": "trash",
"name": "Delete",
@ -168,6 +172,7 @@ exports[`StaticLookupFormatEditor should render normally 1`] = `
size="m"
/>
<EuiButton
data-test-subj="staticLookupEditorAddEntry"
iconType="plusInCircle"
onClick={[Function]}
size="s"
@ -197,6 +202,7 @@ exports[`StaticLookupFormatEditor should render normally 1`] = `
labelType="label"
>
<EuiFieldText
data-test-subj="staticLookupEditorUnknownValue"
onChange={[Function]}
placeholder="Leave blank to keep value as-is"
value=""

View file

@ -81,6 +81,7 @@ export class StaticLookupFormatEditor extends DefaultFormatEditor<StaticLookupFo
return (
<EuiFieldText
value={value || ''}
data-test-subj={`staticLookupEditorKey ${item.index}`}
onChange={(e) => {
this.onLookupChange(
{
@ -105,6 +106,7 @@ export class StaticLookupFormatEditor extends DefaultFormatEditor<StaticLookupFo
return (
<EuiFieldText
value={value || ''}
data-test-subj={`staticLookupEditorValue ${item.index}`}
onChange={(e) => {
this.onLookupChange(
{
@ -136,6 +138,7 @@ export class StaticLookupFormatEditor extends DefaultFormatEditor<StaticLookupFo
type: 'icon',
icon: 'trash',
color: 'danger',
'data-test-subj': 'staticLookupEditorRemoveEntry',
available: () => items.length > 1,
},
],
@ -147,7 +150,12 @@ export class StaticLookupFormatEditor extends DefaultFormatEditor<StaticLookupFo
<Fragment>
<EuiBasicTable items={items} columns={columns} style={{ maxWidth: '400px' }} />
<EuiSpacer size="m" />
<EuiButton iconType="plusInCircle" size="s" onClick={this.addLookup}>
<EuiButton
iconType="plusInCircle"
size="s"
onClick={this.addLookup}
data-test-subj={'staticLookupEditorAddEntry'}
>
<FormattedMessage
id="indexPatternFieldEditor.staticLookup.addEntryButton"
defaultMessage="Add entry"
@ -164,6 +172,7 @@ export class StaticLookupFormatEditor extends DefaultFormatEditor<StaticLookupFo
>
<EuiFieldText
value={formatParams.unknownKeyValue || ''}
data-test-subj={'staticLookupEditorUnknownValue'}
placeholder={i18n.translate(
'indexPatternFieldEditor.staticLookup.leaveBlankPlaceholder',
{

View file

@ -19,6 +19,7 @@ exports[`TruncateFormatEditor should render normally 1`] = `
labelType="label"
>
<EuiFieldNumber
data-test-subj="truncateEditorLength"
defaultValue={5}
isInvalid={false}
min={1}

View file

@ -48,6 +48,7 @@ export class TruncateFormatEditor extends DefaultFormatEditor<TruncateFormatEdit
<EuiFieldNumber
defaultValue={formatParams.fieldLength}
min={1}
data-test-subj={'truncateEditorLength'}
onChange={(e) => {
if (e.target.checkValidity()) {
this.onChange({

View file

@ -15,6 +15,7 @@ import { RandomnessService } from './randomness';
import { SecurityServiceProvider } from './security';
import { EsDeleteAllIndicesProvider } from './es_delete_all_indices';
import { SavedObjectInfoService } from './saved_object_info';
import { IndexPatternsService } from './index_patterns';
export const services = {
deployment: DeploymentService,
@ -26,4 +27,5 @@ export const services = {
security: SecurityServiceProvider,
esDeleteAllIndices: EsDeleteAllIndicesProvider,
savedObjectInfo: SavedObjectInfoService,
indexPatterns: IndexPatternsService,
};

View file

@ -0,0 +1,35 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { FtrService } from '../ftr_provider_context';
import { IndexPatternSpec } from '../../../src/plugins/data/common';
export class IndexPatternsService extends FtrService {
private readonly kibanaServer = this.ctx.getService('kibanaServer');
/**
* Create a new index pattern
*/
async create(
indexPattern: { title: string },
{ override = false }: { override: boolean } = { override: false }
): Promise<IndexPatternSpec> {
const response = await this.kibanaServer.request<{
index_pattern: IndexPatternSpec;
}>({
path: '/api/index_patterns/index_pattern',
method: 'POST',
body: {
override,
index_pattern: indexPattern,
},
});
return response.data.index_pattern;
}
}

View file

@ -1,53 +0,0 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export default function ({ getService, getPageObjects }) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const browser = getService('browser');
const PageObjects = getPageObjects(['settings']);
const testSubjects = getService('testSubjects');
describe('field formatter', function () {
this.tags(['skipFirefox']);
before(async function () {
await browser.setWindowSize(1200, 800);
await esArchiver.load('test/functional/fixtures/es_archiver/discover');
await kibanaServer.uiSettings.replace({});
await kibanaServer.uiSettings.update({});
});
after(async function afterAll() {
await PageObjects.settings.navigateTo();
await esArchiver.emptyKibanaIndex();
});
describe('set and change field formatter', function describeIndexTests() {
// addresses https://github.com/elastic/kibana/issues/93349
it('can change format more than once', async function () {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.clickIndexPatternLogstash();
await PageObjects.settings.clickAddField();
await PageObjects.settings.setFieldType('Long');
const formatRow = await testSubjects.find('formatRow');
const formatRowToggle = (
await formatRow.findAllByCssSelector('[data-test-subj="toggle"]')
)[0];
await formatRowToggle.click();
await PageObjects.settings.setFieldFormat('duration');
await PageObjects.settings.setFieldFormat('bytes');
await PageObjects.settings.setFieldFormat('duration');
await testSubjects.click('euiFlyoutCloseButton');
await PageObjects.settings.closeIndexPatternFieldEditor();
});
});
});
}

View file

@ -0,0 +1,571 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { ES_FIELD_TYPES } from '@kbn/field-types';
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
import { FIELD_FORMAT_IDS } from '../../../../src/plugins/field_formats/common';
import { WebElementWrapper } from '../../services/lib/web_element_wrapper';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const browser = getService('browser');
const PageObjects = getPageObjects(['settings', 'common']);
const testSubjects = getService('testSubjects');
const es = getService('es');
const indexPatterns = getService('indexPatterns');
const toasts = getService('toasts');
describe('field formatter', function () {
this.tags(['skipFirefox']);
before(async function () {
await browser.setWindowSize(1200, 800);
await esArchiver.load('test/functional/fixtures/es_archiver/discover');
await kibanaServer.uiSettings.replace({});
await kibanaServer.uiSettings.update({});
});
after(async function afterAll() {
await PageObjects.settings.navigateTo();
await esArchiver.emptyKibanaIndex();
});
describe('set and change field formatter', function describeIndexTests() {
// addresses https://github.com/elastic/kibana/issues/93349
it('can change format more than once', async function () {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.clickIndexPatternLogstash();
await PageObjects.settings.clickAddField();
await PageObjects.settings.setFieldType('Long');
const formatRow = await testSubjects.find('formatRow');
const formatRowToggle = (
await formatRow.findAllByCssSelector('[data-test-subj="toggle"]')
)[0];
await formatRowToggle.click();
await PageObjects.settings.setFieldFormat('duration');
await PageObjects.settings.setFieldFormat('bytes');
await PageObjects.settings.setFieldFormat('duration');
await testSubjects.click('euiFlyoutCloseButton');
await PageObjects.settings.closeIndexPatternFieldEditor();
});
});
/**
* The purpose of these tests is to cover **editing experience** of different field formats editors,
* The logic of each converter is extensively covered by unit tests.
* TODO: these tests also could check field formats behaviour with different combinations of browser locale, timezone and ui settings
*/
describe('field format editors', () => {
describe('String format', () => {
testFormatEditors([
{
fieldType: ES_FIELD_TYPES.TEXT,
fieldValue: 'A regular text',
applyFormatterType: FIELD_FORMAT_IDS.STRING,
expectFormattedValue: 'A regular text',
// check available formats for ES_FIELD_TYPES.TEXT
expectFormatterTypes: [
FIELD_FORMAT_IDS.BOOLEAN,
FIELD_FORMAT_IDS.COLOR,
FIELD_FORMAT_IDS.STATIC_LOOKUP,
FIELD_FORMAT_IDS.STRING,
FIELD_FORMAT_IDS.TRUNCATE,
FIELD_FORMAT_IDS.URL,
],
},
{
fieldType: ES_FIELD_TYPES.TEXT,
fieldValue: 'A regular text',
applyFormatterType: FIELD_FORMAT_IDS.STRING,
expectFormattedValue: 'a regular text',
beforeSave: async () => {
await testSubjects.selectValue('stringEditorTransform', 'lower');
},
},
{
fieldType: ES_FIELD_TYPES.KEYWORD,
fieldValue: 'a keyword',
applyFormatterType: FIELD_FORMAT_IDS.STRING,
expectFormattedValue: 'A KEYWORD',
beforeSave: async () => {
await testSubjects.selectValue('stringEditorTransform', 'upper');
},
// check available formats for ES_FIELD_TYPES.KEYWORD
expectFormatterTypes: [
FIELD_FORMAT_IDS.BOOLEAN,
FIELD_FORMAT_IDS.COLOR,
FIELD_FORMAT_IDS.STATIC_LOOKUP,
FIELD_FORMAT_IDS.STRING,
FIELD_FORMAT_IDS.TRUNCATE,
FIELD_FORMAT_IDS.URL,
],
},
{
fieldType: ES_FIELD_TYPES.KEYWORD,
fieldValue: 'a keyword',
applyFormatterType: FIELD_FORMAT_IDS.STRING,
expectFormattedValue: 'A Keyword',
beforeSave: async () => {
await testSubjects.selectValue('stringEditorTransform', 'title');
},
},
{
fieldType: ES_FIELD_TYPES.KEYWORD,
fieldValue: 'com.organizations.project.ClassName',
applyFormatterType: FIELD_FORMAT_IDS.STRING,
expectFormattedValue: 'c.o.p.ClassName',
beforeSave: async () => {
await testSubjects.selectValue('stringEditorTransform', 'short');
},
},
{
fieldType: ES_FIELD_TYPES.KEYWORD,
fieldValue: 'SGVsbG8gd29ybGQ=',
applyFormatterType: FIELD_FORMAT_IDS.STRING,
expectFormattedValue: 'Hello world',
beforeSave: async () => {
await testSubjects.selectValue('stringEditorTransform', 'base64');
},
},
{
fieldType: ES_FIELD_TYPES.KEYWORD,
fieldValue: '%EC%95%88%EB%85%95%20%ED%82%A4%EB%B0%94%EB%82%98',
applyFormatterType: FIELD_FORMAT_IDS.STRING,
expectFormattedValue: '안녕 키바나',
beforeSave: async () => {
await testSubjects.selectValue('stringEditorTransform', 'urlparam');
},
},
{
fieldType: ES_FIELD_TYPES.KEYWORD,
fieldValue: '123456789',
applyFormatterType: FIELD_FORMAT_IDS.TRUNCATE,
expectFormattedValue: '123...',
beforeSave: async () => {
await testSubjects.setValue('truncateEditorLength', '3');
},
},
{
fieldType: ES_FIELD_TYPES.INTEGER,
fieldValue: 324,
applyFormatterType: FIELD_FORMAT_IDS.STRING,
expectFormattedValue: '324',
// check available formats for ES_FIELD_TYPES.INTEGER
expectFormatterTypes: [
FIELD_FORMAT_IDS.BOOLEAN,
FIELD_FORMAT_IDS.BYTES,
FIELD_FORMAT_IDS.COLOR,
FIELD_FORMAT_IDS.DURATION,
FIELD_FORMAT_IDS.NUMBER,
FIELD_FORMAT_IDS.PERCENT,
FIELD_FORMAT_IDS.STATIC_LOOKUP,
FIELD_FORMAT_IDS.STRING,
FIELD_FORMAT_IDS.URL,
],
},
]);
});
describe('Number format', () => {
testFormatEditors([
{
fieldType: ES_FIELD_TYPES.LONG,
fieldValue: 324,
applyFormatterType: FIELD_FORMAT_IDS.NUMBER,
expectFormattedValue: '324',
// check available formats for ES_FIELD_TYPES.LONG
expectFormatterTypes: [
FIELD_FORMAT_IDS.BOOLEAN,
FIELD_FORMAT_IDS.BYTES,
FIELD_FORMAT_IDS.COLOR,
FIELD_FORMAT_IDS.DURATION,
FIELD_FORMAT_IDS.NUMBER,
FIELD_FORMAT_IDS.PERCENT,
FIELD_FORMAT_IDS.STATIC_LOOKUP,
FIELD_FORMAT_IDS.STRING,
FIELD_FORMAT_IDS.URL,
],
},
{
fieldType: ES_FIELD_TYPES.LONG,
fieldValue: 324,
applyFormatterType: FIELD_FORMAT_IDS.NUMBER,
expectFormattedValue: '+324',
beforeSave: async () => {
await testSubjects.setValue('numberEditorFormatPattern', '+0,0');
},
},
]);
});
describe('URL format', () => {
testFormatEditors([
{
fieldType: ES_FIELD_TYPES.LONG,
fieldValue: 100,
applyFormatterType: FIELD_FORMAT_IDS.URL,
expectFormattedValue: 'https://elastic.co/?value=100',
beforeSave: async () => {
await testSubjects.setValue(
'urlEditorUrlTemplate',
'https://elastic.co/?value={{value}}'
);
},
expect: async (renderedValueContainer) => {
expect(
await (await renderedValueContainer.findByTagName('a')).getAttribute('href')
).to.be('https://elastic.co/?value=100');
},
},
{
fieldType: ES_FIELD_TYPES.LONG,
fieldValue: 100,
applyFormatterType: FIELD_FORMAT_IDS.URL,
expectFormattedValue: 'url label',
beforeSave: async () => {
await testSubjects.setValue(
'urlEditorUrlTemplate',
'https://elastic.co/?value={{value}}'
);
await testSubjects.setValue('urlEditorLabelTemplate', 'url label');
},
expect: async (renderedValueContainer) => {
expect(
await (await renderedValueContainer.findByTagName('a')).getAttribute('href')
).to.be('https://elastic.co/?value=100');
},
},
]);
});
describe('Date format', () => {
testFormatEditors([
{
fieldType: ES_FIELD_TYPES.DATE,
fieldValue: '2021-08-05T15:05:37.151Z',
applyFormatterType: FIELD_FORMAT_IDS.DATE,
expectFormattedValue: 'Aug 5, 2021',
beforeSave: async () => {
await testSubjects.setValue('dateEditorPattern', 'MMM D, YYYY');
},
// check available formats for ES_FIELD_TYPES.DATE
expectFormatterTypes: [
FIELD_FORMAT_IDS.DATE,
FIELD_FORMAT_IDS.DATE_NANOS,
FIELD_FORMAT_IDS.RELATIVE_DATE,
FIELD_FORMAT_IDS.STRING,
FIELD_FORMAT_IDS.URL,
],
},
{
fieldType: ES_FIELD_TYPES.DATE_NANOS,
fieldValue: '2015-01-01T12:10:30.123456789Z',
applyFormatterType: FIELD_FORMAT_IDS.DATE,
expectFormattedValue: 'Jan 1, 2015 @ 12:10:30.123',
// check available formats for ES_FIELD_TYPES.DATE_NANOS
expectFormatterTypes: [
FIELD_FORMAT_IDS.DATE,
FIELD_FORMAT_IDS.DATE_NANOS,
FIELD_FORMAT_IDS.RELATIVE_DATE,
FIELD_FORMAT_IDS.STRING,
FIELD_FORMAT_IDS.URL,
],
},
{
fieldType: ES_FIELD_TYPES.DATE_NANOS,
fieldValue: '2015-01-01T12:10:30.123456789Z',
applyFormatterType: FIELD_FORMAT_IDS.DATE_NANOS,
expectFormattedValue: 'Jan 1, 2015 @ 12:10:30.123456789',
},
]);
});
describe('Static lookup format', () => {
testFormatEditors([
{
fieldType: ES_FIELD_TYPES.KEYWORD,
fieldValue: 'look me up',
applyFormatterType: FIELD_FORMAT_IDS.STATIC_LOOKUP,
expectFormattedValue: 'looked up!',
beforeSave: async () => {
await testSubjects.click('staticLookupEditorAddEntry');
await testSubjects.setValue('~staticLookupEditorKey', 'look me up');
await testSubjects.setValue('~staticLookupEditorValue', 'looked up!');
},
},
{
fieldType: ES_FIELD_TYPES.BOOLEAN,
fieldValue: 'true',
applyFormatterType: FIELD_FORMAT_IDS.STATIC_LOOKUP,
// check available formats for ES_FIELD_TYPES.BOOLEAN
expectFormatterTypes: [
FIELD_FORMAT_IDS.BOOLEAN,
FIELD_FORMAT_IDS.STATIC_LOOKUP,
FIELD_FORMAT_IDS.STRING,
FIELD_FORMAT_IDS.URL,
],
expectFormattedValue: 'yes',
beforeSave: async () => {
await testSubjects.click('staticLookupEditorAddEntry');
await testSubjects.setValue('~staticLookupEditorKey', 'true');
await testSubjects.setValue('~staticLookupEditorValue', 'yes');
await testSubjects.setValue('staticLookupEditorUnknownValue', 'no');
},
},
{
fieldType: ES_FIELD_TYPES.BOOLEAN,
fieldValue: 'false',
applyFormatterType: FIELD_FORMAT_IDS.STATIC_LOOKUP,
expectFormattedValue: 'no',
beforeSave: async () => {
await testSubjects.click('staticLookupEditorAddEntry');
await testSubjects.setValue('~staticLookupEditorKey', 'true');
await testSubjects.setValue('~staticLookupEditorValue', 'yes');
await testSubjects.setValue('staticLookupEditorUnknownValue', 'no');
},
},
{
fieldType: ES_FIELD_TYPES.BOOLEAN,
fieldValue: 'false',
applyFormatterType: FIELD_FORMAT_IDS.STATIC_LOOKUP,
expectFormattedValue: 'false',
beforeSave: async () => {
await testSubjects.click('staticLookupEditorAddEntry');
await testSubjects.setValue('~staticLookupEditorKey', 'true');
await testSubjects.setValue('~staticLookupEditorValue', 'yes');
},
},
]);
});
describe('Other formats', () => {
testFormatEditors([
{
fieldType: ES_FIELD_TYPES.LONG,
fieldValue: 123292,
applyFormatterType: FIELD_FORMAT_IDS.DURATION,
expectFormattedValue: '2 minutes',
beforeSave: async () => {
await testSubjects.setValue('durationEditorInputFormat', 'milliseconds');
},
},
{
fieldType: ES_FIELD_TYPES.DOUBLE,
fieldValue: 0.1,
applyFormatterType: FIELD_FORMAT_IDS.PERCENT,
// check available formats for ES_FIELD_TYPES.DOUBLE
expectFormatterTypes: [
FIELD_FORMAT_IDS.BOOLEAN,
FIELD_FORMAT_IDS.BYTES,
FIELD_FORMAT_IDS.COLOR,
FIELD_FORMAT_IDS.DURATION,
FIELD_FORMAT_IDS.NUMBER,
FIELD_FORMAT_IDS.PERCENT,
FIELD_FORMAT_IDS.STATIC_LOOKUP,
FIELD_FORMAT_IDS.STRING,
FIELD_FORMAT_IDS.URL,
],
expectFormattedValue: '10.0%',
beforeSave: async () => {
await testSubjects.setValue('numberEditorFormatPattern', '0.0%');
},
},
{
fieldType: ES_FIELD_TYPES.LONG,
fieldValue: 1990000000,
applyFormatterType: FIELD_FORMAT_IDS.BYTES,
expectFormattedValue: '2GB',
beforeSave: async () => {
await testSubjects.setValue('numberEditorFormatPattern', '0b');
},
},
{
fieldType: ES_FIELD_TYPES.KEYWORD,
fieldValue: 'red',
applyFormatterType: FIELD_FORMAT_IDS.COLOR,
expectFormattedValue: 'red',
beforeSave: async () => {
await testSubjects.click('colorEditorAddColor');
await testSubjects.setValue('~colorEditorKeyPattern', 'red');
await testSubjects.setValue('~colorEditorColorPicker', '#ffffff');
await testSubjects.setValue('~colorEditorBackgroundPicker', '#ff0000');
},
expect: async (renderedValueContainer) => {
const span = await renderedValueContainer.findByTagName('span');
expect(await span.getComputedStyle('color')).to.be('rgba(255, 255, 255, 1)');
expect(await span.getComputedStyle('background-color')).to.be('rgba(255, 0, 0, 1)');
},
},
]);
});
});
});
/**
* Runs a field format editors tests covering data setup, editing a field and checking a resulting formatting in Discover app
* TODO: might be useful to reuse this util for runtime fields formats tests
* @param specs - {@link FieldFormatEditorSpecDescriptor}
*/
function testFormatEditors(specs: FieldFormatEditorSpecDescriptor[]) {
const indexTitle = 'field_formats_management_functional_tests';
let indexPatternId: string;
let testDocumentId: string;
before(async () => {
if ((await es.indices.exists({ index: indexTitle })).body) {
await es.indices.delete({ index: indexTitle });
}
await es.indices.create({
index: indexTitle,
body: {
mappings: {
properties: specs.reduce((properties, spec, index) => {
properties[`${index}`] = { type: spec.fieldType };
return properties;
}, {} as Record<string, { type: ES_FIELD_TYPES }>),
},
},
});
const docResult = await es.index({
index: indexTitle,
body: specs.reduce((properties, spec, index) => {
properties[`${index}`] = spec.fieldValue;
return properties;
}, {} as Record<string, FieldFormatEditorSpecDescriptor['fieldValue']>),
refresh: 'wait_for',
});
testDocumentId = docResult.body._id;
const indexPatternResult = await indexPatterns.create(
{ title: indexTitle },
{ override: true }
);
indexPatternId = indexPatternResult.id!;
});
describe('edit formats', () => {
before(async () => {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.clickIndexPatternByName(indexTitle);
});
afterEach(async () => {
try {
await PageObjects.settings.controlChangeSave();
} catch (e) {
// in case previous test failed in a state when save is disabled
await PageObjects.settings.controlChangeCancel();
}
await toasts.dismissAllToasts(); // dismiss "saved" toast, otherwise it could overlap save button for a next test
});
specs.forEach((spec, index) => {
const fieldName = `${index}`;
it(
`edit field format of "${fieldName}" field to "${spec.applyFormatterType}"` +
spec.expectFormatterTypes
? `, and check available formats types`
: '',
async () => {
await PageObjects.settings.filterField(fieldName);
await PageObjects.settings.openControlsByName(fieldName);
await PageObjects.settings.toggleRow('formatRow');
if (spec.expectFormatterTypes) {
expect(
(
await Promise.all(
(
await (await testSubjects.find('editorSelectedFormatId')).findAllByTagName(
'option'
)
).map((option) => option.getAttribute('value'))
)
).filter(Boolean)
).to.eql(spec.expectFormatterTypes);
}
await PageObjects.settings.setFieldFormat(spec.applyFormatterType);
if (spec.beforeSave) {
await spec.beforeSave(await testSubjects.find('formatRow'));
}
}
);
});
});
describe('check formats', async () => {
before(async () => {
await PageObjects.common.navigateToApp('discover', {
hash: `/doc/${indexPatternId}/${indexTitle}?id=${testDocumentId}`,
});
await testSubjects.exists('doc-hit');
});
specs.forEach((spec, index) => {
it(`check field format of "${index}" field`, async () => {
const renderedValue = await testSubjects.find(`tableDocViewRow-${index}-value`);
const text = await renderedValue.getVisibleText();
expect(text).to.be(spec.expectFormattedValue);
if (spec.expect) {
await spec.expect(renderedValue);
}
});
});
});
}
}
/**
* Describes a field format editor test
*/
interface FieldFormatEditorSpecDescriptor {
/**
* Raw field value to put into document
*/
fieldValue: string | number | boolean | null;
/**
* Explicitly specify a type for a {@link fieldValue}
*/
fieldType: ES_FIELD_TYPES;
/**
* Type of a field formatter to apply
*/
applyFormatterType: FIELD_FORMAT_IDS;
/**
* Optionally check available formats for {@link fieldType}
*/
expectFormatterTypes?: FIELD_FORMAT_IDS[];
/**
* Function to execute before field format is applied.
* Use it set specific configuration params for applied field formatter
* @param formatRowContainer - field format editor container
*/
beforeSave?: (formatRowContainer: WebElementWrapper) => Promise<void>;
/**
* An expected formatted value rendered by Discover app,
* Use this for final assertion
*/
expectFormattedValue: string;
/**
* Run additional assertions on rendered element
*/
expect?: (renderedValueContainer: WebElementWrapper) => Promise<void>;
}