From 27d0e70079e57dacdd2a6f8071272bd8e34a97fa Mon Sep 17 00:00:00 2001 From: Anton Dosov <anton.dosov@elastic.co> Date: Thu, 5 Aug 2021 17:46:01 +0200 Subject: [PATCH 1/3] wip --- .../bytes/__snapshots__/bytes.test.tsx.snap | 1 + .../__snapshots__/duration.test.tsx.snap | 6 + .../editors/duration/duration.tsx | 2 + .../number/__snapshots__/number.test.tsx.snap | 1 + .../editors/number/number.tsx | 1 + .../__snapshots__/percent.test.tsx.snap | 1 + .../__snapshots__/truncate.test.tsx.snap | 1 + .../editors/truncate/truncate.tsx | 1 + test/common/services/index.ts | 2 + test/common/services/index_patterns.ts | 32 ++ .../apps/management/_field_formatter.js | 53 --- .../apps/management/_field_formatter.ts | 366 ++++++++++++++++++ 12 files changed, 414 insertions(+), 53 deletions(-) create mode 100644 test/common/services/index_patterns.ts delete mode 100644 test/functional/apps/management/_field_formatter.js create mode 100644 test/functional/apps/management/_field_formatter.ts diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.tsx.snap index 2ab8037639f85..2b71d1882b0cf 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.tsx.snap +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.tsx.snap @@ -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" diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/__snapshots__/duration.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/__snapshots__/duration.test.tsx.snap index d000af4453cd1..85e0177964a29 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/__snapshots__/duration.test.tsx.snap +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/__snapshots__/duration.test.tsx.snap @@ -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={ diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx index 89bac89a95b76..24582e853d38f 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx @@ -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 { diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/__snapshots__/number.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/__snapshots__/number.test.tsx.snap index 4d42e3848d3cd..8b59c0da10167 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/__snapshots__/number.test.tsx.snap +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/__snapshots__/number.test.tsx.snap @@ -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]" diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.tsx index 83110a4e42574..9bb1b57f0a701 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.tsx @@ -66,6 +66,7 @@ export class NumberFormatEditor extends DefaultFormatEditor<NumberFormatEditorPa error={error} > <EuiFieldText + data-test-subj={'numberEditorFormatPattern'} value={formatParams.pattern} placeholder={defaultPattern} onChange={(e) => { diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/__snapshots__/percent.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/__snapshots__/percent.test.tsx.snap index fce51e8fa3871..7d8ab5e682a3e 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/__snapshots__/percent.test.tsx.snap +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/__snapshots__/percent.test.tsx.snap @@ -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]%" diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.tsx.snap index f982632bba523..cc6eddbf0dc8c 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.tsx.snap +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.tsx.snap @@ -19,6 +19,7 @@ exports[`TruncateFormatEditor should render normally 1`] = ` labelType="label" > <EuiFieldNumber + data-test-subj="truncateEditorLength" defaultValue={5} isInvalid={false} min={1} diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.tsx index 8d4a227270718..ef4365cc576e2 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.tsx @@ -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({ diff --git a/test/common/services/index.ts b/test/common/services/index.ts index 370d289093517..c04bd778468a9 100644 --- a/test/common/services/index.ts +++ b/test/common/services/index.ts @@ -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, }; diff --git a/test/common/services/index_patterns.ts b/test/common/services/index_patterns.ts new file mode 100644 index 0000000000000..eb1d5da4a9496 --- /dev/null +++ b/test/common/services/index_patterns.ts @@ -0,0 +1,32 @@ +/* + * 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'; + +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 } + ) { + const response = await this.kibanaServer.request({ + path: '/api/index_patterns/index_pattern', + method: 'POST', + body: { + override, + index_pattern: indexPattern, + }, + }); + + return response.data.index_pattern; + } +} diff --git a/test/functional/apps/management/_field_formatter.js b/test/functional/apps/management/_field_formatter.js deleted file mode 100644 index 383b4faecc40c..0000000000000 --- a/test/functional/apps/management/_field_formatter.js +++ /dev/null @@ -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(); - }); - }); - }); -} diff --git a/test/functional/apps/management/_field_formatter.ts b/test/functional/apps/management/_field_formatter.ts new file mode 100644 index 0000000000000..7b6ce17279eca --- /dev/null +++ b/test/functional/apps/management/_field_formatter.ts @@ -0,0 +1,366 @@ +/* + * 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/data/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', + }, + { + 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'); + }, + }, + { + 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', + }, + ]); + }); + + describe('Number format', () => { + testFormatEditors([ + { + fieldType: ES_FIELD_TYPES.LONG, + fieldValue: 324, + applyFormatterType: FIELD_FORMAT_IDS.NUMBER, + expectFormattedValue: '324', + }, + { + 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}}' + ); + }, + }, + { + 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'); + }, + }, + ]); + }); + + 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'); + }, + }, + { + 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', + }, + { + 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('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, + 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'); + }, + }, + ]); + }); + }); + }); + + 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); + }); + + specs.forEach((spec, index) => { + const fieldName = `${index}`; + it(`edit field format of "${fieldName}" field to "${spec.applyFormatterType}"`, async () => { + await PageObjects.settings.filterField(fieldName); + await PageObjects.settings.openControlsByName(fieldName); + await PageObjects.settings.toggleRow('formatRow'); + await PageObjects.settings.setFieldFormat(spec.applyFormatterType); + if (spec.beforeSave) { + await spec.beforeSave(await testSubjects.find('formatRow')); + } + await PageObjects.settings.controlChangeSave(); + await toasts.dismissToast(); // dismiss "saved" toast, otherwise it could overlap save button for a next test + }); + }); + }); + + 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); + }); + }); + }); + } +} + +/** + * 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; + /** + * 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; +} From 8727715b3fb3a4df5353d17fe510861a3554c667 Mon Sep 17 00:00:00 2001 From: Anton Dosov <anton.dosov@elastic.co> Date: Mon, 9 Aug 2021 11:47:47 +0200 Subject: [PATCH 2/3] fix typescript --- test/common/services/index_patterns.ts | 7 +++++-- test/functional/apps/management/_field_formatter.ts | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/test/common/services/index_patterns.ts b/test/common/services/index_patterns.ts index eb1d5da4a9496..5b6d20990b6d1 100644 --- a/test/common/services/index_patterns.ts +++ b/test/common/services/index_patterns.ts @@ -7,6 +7,7 @@ */ 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'); @@ -17,8 +18,10 @@ export class IndexPatternsService extends FtrService { async create( indexPattern: { title: string }, { override = false }: { override: boolean } = { override: false } - ) { - const response = await this.kibanaServer.request({ + ): Promise<IndexPatternSpec> { + const response = await this.kibanaServer.request<{ + index_pattern: IndexPatternSpec; + }>({ path: '/api/index_patterns/index_pattern', method: 'POST', body: { diff --git a/test/functional/apps/management/_field_formatter.ts b/test/functional/apps/management/_field_formatter.ts index 7b6ce17279eca..cf06eff671ce3 100644 --- a/test/functional/apps/management/_field_formatter.ts +++ b/test/functional/apps/management/_field_formatter.ts @@ -8,7 +8,7 @@ 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/data/common'; +import { FIELD_FORMAT_IDS } from '../../../../src/plugins/field_formats/common'; import { WebElementWrapper } from '../../services/lib/web_element_wrapper'; export default function ({ getService, getPageObjects }: FtrProviderContext) { @@ -290,7 +290,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { { title: indexTitle }, { override: true } ); - indexPatternId = indexPatternResult.id; + indexPatternId = indexPatternResult.id!; }); describe('edit formats', () => { From 44f02d70bf47f994465c1dc56e95d6c086a9e07a Mon Sep 17 00:00:00 2001 From: Anton Dosov <anton.dosov@elastic.co> Date: Mon, 9 Aug 2021 18:00:34 +0200 Subject: [PATCH 3/3] wip --- .../color/__snapshots__/color.test.tsx.snap | 6 + .../editors/color/color.tsx | 12 +- .../__snapshots__/static_lookup.test.tsx.snap | 6 + .../editors/static_lookup/static_lookup.tsx | 11 +- .../apps/management/_field_formatter.ts | 225 +++++++++++++++++- 5 files changed, 248 insertions(+), 12 deletions(-) diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/__snapshots__/color.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/__snapshots__/color.test.tsx.snap index c33bb57bfeac8..7406e5ae9bb2d 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/__snapshots__/color.test.tsx.snap +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/__snapshots__/color.test.tsx.snap @@ -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" diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.tsx index 67290212c04dd..9944c031198ef 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.tsx @@ -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" diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.tsx.snap index c5697cb699eb7..664912789b0e3 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.tsx.snap +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.tsx.snap @@ -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="" diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.tsx index 485660fbb2bd4..ab0653d141fce 100644 --- a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.tsx @@ -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', { diff --git a/test/functional/apps/management/_field_formatter.ts b/test/functional/apps/management/_field_formatter.ts index cf06eff671ce3..60c1bbe7b3d1d 100644 --- a/test/functional/apps/management/_field_formatter.ts +++ b/test/functional/apps/management/_field_formatter.ts @@ -71,6 +71,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 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, @@ -89,6 +99,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 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, @@ -140,6 +159,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 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, + ], }, ]); }); @@ -151,6 +182,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 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, @@ -177,6 +220,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { '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, @@ -190,6 +238,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); await testSubjects.setValue('urlEditorLabelTemplate', 'url label'); }, + expect: async (renderedValueContainer) => { + expect( + await (await renderedValueContainer.findByTagName('a')).getAttribute('href') + ).to.be('https://elastic.co/?value=100'); + }, }, ]); }); @@ -204,12 +257,28 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 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, @@ -220,6 +289,64 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ]); }); + 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([ { @@ -235,6 +362,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 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%'); @@ -249,11 +388,33 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 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; @@ -300,19 +461,49 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 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}"`, async () => { - await PageObjects.settings.filterField(fieldName); - await PageObjects.settings.openControlsByName(fieldName); - await PageObjects.settings.toggleRow('formatRow'); - await PageObjects.settings.setFieldFormat(spec.applyFormatterType); - if (spec.beforeSave) { - await spec.beforeSave(await testSubjects.find('formatRow')); + 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')); + } } - await PageObjects.settings.controlChangeSave(); - await toasts.dismissToast(); // dismiss "saved" toast, otherwise it could overlap save button for a next test - }); + ); }); }); @@ -329,6 +520,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 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); + } }); }); }); @@ -351,6 +545,12 @@ interface FieldFormatEditorSpecDescriptor { * 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 @@ -363,4 +563,9 @@ interface FieldFormatEditorSpecDescriptor { * Use this for final assertion */ expectFormattedValue: string; + + /** + * Run additional assertions on rendered element + */ + expect?: (renderedValueContainer: WebElementWrapper) => Promise<void>; }