[Discover] Add log level badge cell renderer for logs profile (#188281)

## Summary

This PR adds a log level badge cell render for `log.level` and
`log_level` fields when the logs data source profile is active in
Discover:

![log_level_badge](https://github.com/user-attachments/assets/fa90d5d6-538c-4b9e-bad5-912db23ee41c)

Resolves #186567.

### Checklist

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
Davis McPhee 2024-07-17 12:46:02 -03:00 committed by GitHub
parent 5f843a81ef
commit ab8bfda1ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 417 additions and 45 deletions

View file

@ -6,12 +6,12 @@
* Side Public License, v 1.
*/
import React from 'react';
import { EuiDataGridCellValueElementProps, type EuiDataGridColumn } from '@elastic/eui';
import type { ReactElement } from 'react';
import type { EuiDataGridCellValueElementProps, EuiDataGridColumn } from '@elastic/eui';
import type { DataTableRecord } from '@kbn/discover-utils/src/types';
import type { DataView } from '@kbn/data-views-plugin/common';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { EuiDataGridControlColumn } from '@elastic/eui/src/components/datagrid/data_grid_types';
import type { EuiDataGridControlColumn } from '@elastic/eui/src/components/datagrid/data_grid_types';
import type { DatatableColumnMeta } from '@kbn/expressions-plugin/common';
/**
@ -57,7 +57,7 @@ export type DataGridCellValueElementProps = EuiDataGridCellValueElementProps & {
export type CustomCellRenderer = Record<
string,
(props: DataGridCellValueElementProps) => React.ReactNode
(props: DataGridCellValueElementProps) => ReactElement
>;
export interface CustomGridColumnProps {

View file

@ -78,22 +78,24 @@ export const getRenderCellValueFn = ({
return <span className={CELL_CLASS}>-</span>;
}
if (!!externalCustomRenderers && !!externalCustomRenderers[columnId]) {
const CustomCellRenderer = externalCustomRenderers?.[columnId];
if (CustomCellRenderer) {
return (
<span className={CELL_CLASS}>
{externalCustomRenderers[columnId]({
rowIndex,
columnId,
isDetails,
setCellProps,
isExpandable,
isExpanded,
colIndex,
row,
dataView,
fieldFormats,
closePopover,
})}
<CustomCellRenderer
rowIndex={rowIndex}
columnId={columnId}
isDetails={isDetails}
setCellProps={setCellProps}
isExpandable={isExpandable}
isExpanded={isExpanded}
colIndex={colIndex}
row={row}
dataView={dataView}
fieldFormats={fieldFormats}
closePopover={closePopover}
/>
</span>
);
}

View file

@ -63,3 +63,5 @@ export const FILTER_OUT_FIELDS_PREFIXES_FOR_CONTENT = [
export const DEFAULT_ALLOWED_DATA_VIEWS = ['logs', 'auditbeat', 'filebeat', 'winlogbeat'];
export const DEFAULT_ALLOWED_LOGS_DATA_VIEWS = ['logs', 'auditbeat', 'filebeat', 'winlogbeat'];
export const LOG_LEVEL_FIELDS = ['log.level', 'log_level'];

View file

@ -0,0 +1,63 @@
/*
* 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 { buildDataTableRecord, DataTableRecord } from '@kbn/discover-utils';
import { dataViewMock } from '@kbn/discover-utils/src/__mocks__';
import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks';
import { render, screen } from '@testing-library/react';
import React from 'react';
import { getLogLevelBadgeCell } from './log_level_badge_cell';
const renderCell = (logLevelField: string, record: DataTableRecord) => {
const LogLevelBadgeCell = getLogLevelBadgeCell(logLevelField);
render(
<LogLevelBadgeCell
rowIndex={0}
colIndex={0}
columnId="log.level"
isExpandable={false}
isExpanded={false}
isDetails={false}
row={record}
dataView={dataViewMock}
fieldFormats={fieldFormatsMock}
setCellProps={() => {}}
closePopover={() => {}}
/>
);
};
describe('getLogLevelBadgeCell', () => {
it('renders badge with color based on provided logLevelField', () => {
const record = buildDataTableRecord({ fields: { 'log.level': 'info' } }, dataViewMock);
renderCell('log.level', record);
const badge = screen.getByTestId('logLevelBadgeCell-info');
expect(badge).toBeInTheDocument();
expect(badge).toHaveTextContent('info');
expect(getComputedStyle(badge).getPropertyValue('--euiBadgeBackgroundColor')).toEqual(
'#90b0d1'
);
});
it('renders unknown badge if logLevelField is not recognized', () => {
const record = buildDataTableRecord({ fields: { 'log.level': 'unknown_level' } }, dataViewMock);
renderCell('log.level', record);
const badge = screen.getByTestId('logLevelBadgeCell-unknown');
expect(badge).toBeInTheDocument();
expect(badge).toHaveTextContent('unknown_level');
expect(getComputedStyle(badge).getPropertyValue('--euiBadgeBackgroundColor')).toEqual('');
});
it('renders empty if no matching logLevelField is found', () => {
const record = buildDataTableRecord({ fields: { 'log.level': 'info' } }, dataViewMock);
renderCell('log_level', record);
const badge = screen.getByTestId('logLevelBadgeCell-empty');
expect(badge).toBeInTheDocument();
expect(badge).toHaveTextContent('-');
});
});

View file

@ -0,0 +1,42 @@
/*
* 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 { EuiBadge, mathWithUnits, useEuiTheme } from '@elastic/eui';
import type { CSSObject } from '@emotion/react';
import { getLogLevelCoalescedValue, getLogLevelColor } from '@kbn/discover-utils';
import { euiThemeVars } from '@kbn/ui-theme';
import type { DataGridCellValueElementProps } from '@kbn/unified-data-table';
import React from 'react';
const badgeCss: CSSObject = {
marginTop: '-4px',
maxWidth: mathWithUnits(euiThemeVars.euiSize, (size) => size * 7.5),
};
export const getLogLevelBadgeCell =
(logLevelField: string) => (props: DataGridCellValueElementProps) => {
const { euiTheme } = useEuiTheme();
const value = props.row.flattened[logLevelField];
if (!value) {
return <span data-test-subj="logLevelBadgeCell-empty">-</span>;
}
const coalescedValue = getLogLevelCoalescedValue(value);
const color = coalescedValue ? getLogLevelColor(coalescedValue, euiTheme) : undefined;
if (!color || !coalescedValue) {
return <span data-test-subj="logLevelBadgeCell-unknown">{value}</span>;
}
return (
<EuiBadge color={color} data-test-subj={`logLevelBadgeCell-${coalescedValue}`} css={badgeCss}>
{value}
</EuiBadge>
);
};

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import React from 'react';
import { getDataTableRecords } from '../../__fixtures__/real_hits';
import { dataViewWithTimefieldMock } from '../../__mocks__/data_view_with_timefield';
import {
@ -30,7 +31,7 @@ export const createContextAwarenessMocks = ({
profile: {
getCellRenderers: jest.fn((prev) => () => ({
...prev(),
rootProfile: () => 'root-profile',
rootProfile: () => <>root-profile</>,
})),
},
resolve: jest.fn(() => ({
@ -46,7 +47,7 @@ export const createContextAwarenessMocks = ({
profile: {
getCellRenderers: jest.fn((prev) => () => ({
...prev(),
rootProfile: () => 'data-source-profile',
rootProfile: () => <>data-source-profile</>,
})),
},
resolve: jest.fn(() => ({

View file

@ -0,0 +1,24 @@
/*
* 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 { LOG_LEVEL_FIELDS } from '../../../../../common/data_types/logs/constants';
import { getLogLevelBadgeCell } from '../../../../components/data_types/logs/log_level_badge_cell';
import type { DataSourceProfileProvider } from '../../../profiles';
export const getCellRenderers: DataSourceProfileProvider['profile']['getCellRenderers'] =
(prev) => () => ({
...prev(),
...LOG_LEVEL_FIELDS.reduce(
(acc, field) => ({
...acc,
[field]: getLogLevelBadgeCell(field),
[`${field}.keyword`]: getLogLevelBadgeCell(`${field}.keyword`),
}),
{}
),
});

View file

@ -12,10 +12,9 @@ import {
getLogLevelColor,
} from '@kbn/discover-utils';
import type { UnifiedDataTableProps } from '@kbn/unified-data-table';
import { LOG_LEVEL_FIELDS } from '../../../../../common/data_types/logs/constants';
import type { DataSourceProfileProvider } from '../../../profiles';
const LOG_LEVEL_FIELDS = ['log.level', 'log_level'];
export const getRowIndicatorProvider: DataSourceProfileProvider['profile']['getRowIndicatorProvider'] =
() =>

View file

@ -7,3 +7,4 @@
*/
export { getRowIndicatorProvider } from './get_row_indicator_provider';
export { getCellRenderers } from './get_cell_renderers';

View file

@ -78,32 +78,32 @@ describe('logsDataSourceProfileProvider', () => {
).toEqual(RESOLUTION_MISMATCH);
});
describe('getRowIndicator', () => {
const dataViewWithLogLevel = createStubIndexPattern({
spec: {
title: VALID_INDEX_PATTERN,
fields: {
'log.level': {
name: 'log.level',
type: 'string',
esTypes: ['keyword'],
aggregatable: true,
searchable: true,
count: 0,
readFromDocValues: false,
scripted: false,
isMapped: true,
},
const dataViewWithLogLevel = createStubIndexPattern({
spec: {
title: VALID_INDEX_PATTERN,
fields: {
'log.level': {
name: 'log.level',
type: 'string',
esTypes: ['keyword'],
aggregatable: true,
searchable: true,
count: 0,
readFromDocValues: false,
scripted: false,
isMapped: true,
},
},
});
},
});
const dataViewWithoutLogLevel = createStubIndexPattern({
spec: {
title: VALID_INDEX_PATTERN,
},
});
const dataViewWithoutLogLevel = createStubIndexPattern({
spec: {
title: VALID_INDEX_PATTERN,
},
});
describe('getRowIndicator', () => {
it('should return the correct color for a given log level', () => {
const row = buildDataTableRecord({ fields: { 'log.level': 'info' } });
const euiTheme = { euiTheme: { colors: {} } } as unknown as EuiThemeComputed;
@ -140,4 +140,17 @@ describe('logsDataSourceProfileProvider', () => {
expect(getRowIndicator).toBeUndefined();
});
});
describe('getCellRenderers', () => {
it('should return cell renderers for log level fields', () => {
const getCellRenderers = logsDataSourceProfileProvider.profile.getCellRenderers?.(() => ({}));
const cellRenderers = getCellRenderers?.();
expect(cellRenderers).toBeDefined();
expect(cellRenderers?.['log.level']).toBeDefined();
expect(cellRenderers?.['log.level.keyword']).toBeDefined();
expect(cellRenderers?.log_level).toBeDefined();
expect(cellRenderers?.['log_level.keyword']).toBeDefined();
});
});
});

View file

@ -16,6 +16,7 @@ import {
} from '../../profiles';
import { ProfileProviderServices } from '../profile_provider_services';
import { getRowIndicatorProvider } from './accessors';
import { getCellRenderers } from './accessors';
export const createLogsDataSourceProfileProvider = (
services: ProfileProviderServices
@ -23,6 +24,7 @@ export const createLogsDataSourceProfileProvider = (
profileId: 'logs-data-source-profile',
profile: {
getRowIndicatorProvider,
getCellRenderers,
},
resolve: (params) => {
const indexPattern = extractIndexPatternFrom(params);

View file

@ -0,0 +1,98 @@
/*
* 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 kbnRison from '@kbn/rison';
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'discover', 'unifiedFieldList']);
const esArchiver = getService('esArchiver');
const testSubjects = getService('testSubjects');
const dataGrid = getService('dataGrid');
const dataViews = getService('dataViews');
const queryBar = getService('queryBar');
describe('extension getCellRenderers', () => {
before(async () => {
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
});
after(async () => {
await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional');
});
describe('ES|QL mode', () => {
it('should render log.level badge cell', async () => {
const state = kbnRison.encode({
dataSource: { type: 'esql' },
query: {
esql: 'from my-example-logs,logstash* | sort @timestamp desc | where `log.level` is not null',
},
});
await PageObjects.common.navigateToApp('discover', {
hash: `/?_a=${state}`,
});
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemAdd('log.level');
const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 0);
const logLevelBadge = await firstCell.findByTestSubject('*logLevelBadgeCell-');
expect(await logLevelBadge.getVisibleText()).to.be('debug');
expect(await logLevelBadge.getComputedStyle('background-color')).to.be(
'rgba(190, 207, 227, 1)'
);
});
it("should not render log.level badge cell if it's not a logs data source", async () => {
const state = kbnRison.encode({
dataSource: { type: 'esql' },
query: {
esql: 'from my-example* | sort @timestamp desc | where `log.level` is not null',
},
});
await PageObjects.common.navigateToApp('discover', {
hash: `/?_a=${state}`,
});
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemAdd('log.level');
const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 0);
expect(await firstCell.getVisibleText()).to.be('debug');
await testSubjects.missingOrFail('*logLevelBadgeCell-');
});
});
describe('data view mode', () => {
it('should render log.level badge cell', async () => {
await PageObjects.common.navigateToApp('discover');
await dataViews.switchTo('my-example-logs,logstash*');
await queryBar.setQuery('log.level:*');
await queryBar.submitQuery();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemAdd('log.level');
const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 1);
const logLevelBadge = await firstCell.findByTestSubject('*logLevelBadgeCell-');
expect(await logLevelBadge.getVisibleText()).to.be('debug');
expect(await logLevelBadge.getComputedStyle('background-color')).to.be(
'rgba(190, 207, 227, 1)'
);
});
it("should not render log.level badge cell if it's not a logs data source", async () => {
await PageObjects.common.navigateToApp('discover');
await dataViews.switchTo('my-example-*');
await queryBar.setQuery('log.level:*');
await queryBar.submitQuery();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemAdd('log.level');
const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 1);
expect(await firstCell.getVisibleText()).to.be('debug');
await testSubjects.missingOrFail('*logLevelBadgeCell-');
});
});
});
}

View file

@ -37,5 +37,6 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid
loadTestFile(require.resolve('./_data_source_profile'));
loadTestFile(require.resolve('./extensions/_get_row_indicator_provider'));
loadTestFile(require.resolve('./extensions/_get_doc_viewer'));
loadTestFile(require.resolve('./extensions/_get_cell_renderers'));
});
}

View file

@ -71,4 +71,29 @@
"updated_at": "2024-06-12T22:23:21.331Z",
"updated_by": "u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0",
"version": "WzEwLDFc"
}
{
"attributes": {
"allowHidden": false,
"fieldAttrs": "{}",
"fieldFormatMap": "{}",
"fields": "[]",
"name": "my-example-logs,logstash*",
"runtimeFieldMap": "{}",
"sourceFilters": "[]",
"timeFieldName": "@timestamp",
"title": "my-example-logs,logstash*"
},
"coreMigrationVersion": "8.8.0",
"created_at": "2024-06-12T22:23:41.331Z",
"created_by": "u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0",
"id": "795df528-add1-491a-8e25-72a862c4bf4e",
"managed": false,
"references": [],
"type": "index-pattern",
"typeMigrationVersion": "8.0.0",
"updated_at": "2024-06-12T22:23:41.331Z",
"updated_by": "u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0",
"version": "WzEwLDFd"
}

View file

@ -6,6 +6,7 @@
*/
import type { EuiDataGridCellValueElementProps } from '@elastic/eui';
import type { ReactElement } from 'react';
import React from 'react';
import type { ColumnHeaderOptions, TimelineItem } from '@kbn/timelines-plugin/common';
@ -23,7 +24,7 @@ export const getFormattedFields = ({
}) => {
return headers.reduce(
(
obj: Record<string, (props: EuiDataGridCellValueElementProps) => React.ReactNode>,
obj: Record<string, (props: EuiDataGridCellValueElementProps) => ReactElement>,
header: ColumnHeaderOptions
) => {
obj[header.id] = function UnifiedFieldRender(props: EuiDataGridCellValueElementProps) {

View file

@ -0,0 +1,97 @@
/*
* 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 kbnRison from '@kbn/rison';
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'discover', 'unifiedFieldList']);
const esArchiver = getService('esArchiver');
const testSubjects = getService('testSubjects');
const dataGrid = getService('dataGrid');
const dataViews = getService('dataViews');
const queryBar = getService('queryBar');
describe('extension getCellRenderers', () => {
before(async () => {
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
});
after(async () => {
await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional');
});
describe('ES|QL mode', () => {
it('should render log.level badge cell', async () => {
const state = kbnRison.encode({
dataSource: { type: 'esql' },
query: {
esql: 'from my-example-logs,logstash* | sort @timestamp desc | where `log.level` is not null',
},
});
await PageObjects.common.navigateToApp('discover', {
hash: `/?_a=${state}`,
});
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemAdd('log.level');
const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 0);
const logLevelBadge = await firstCell.findByTestSubject('*logLevelBadgeCell-');
expect(await logLevelBadge.getVisibleText()).to.be('debug');
expect(await logLevelBadge.getComputedStyle('background-color')).to.be(
'rgba(190, 207, 227, 1)'
);
});
it("should not render log.level badge cell if it's not a logs data source", async () => {
const state = kbnRison.encode({
dataSource: { type: 'esql' },
query: {
esql: 'from my-example* | sort @timestamp desc | where `log.level` is not null',
},
});
await PageObjects.common.navigateToApp('discover', {
hash: `/?_a=${state}`,
});
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemAdd('log.level');
const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 0);
expect(await firstCell.getVisibleText()).to.be('debug');
await testSubjects.missingOrFail('*logLevelBadgeCell-');
});
});
describe('data view mode', () => {
it('should render log.level badge cell', async () => {
await PageObjects.common.navigateToApp('discover');
await dataViews.switchTo('my-example-logs,logstash*');
await queryBar.setQuery('log.level:*');
await queryBar.submitQuery();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemAdd('log.level');
const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 1);
const logLevelBadge = await firstCell.findByTestSubject('*logLevelBadgeCell-');
expect(await logLevelBadge.getVisibleText()).to.be('debug');
expect(await logLevelBadge.getComputedStyle('background-color')).to.be(
'rgba(190, 207, 227, 1)'
);
});
it("should not render log.level badge cell if it's not a logs data source", async () => {
await PageObjects.common.navigateToApp('discover');
await dataViews.switchTo('my-example-*');
await queryBar.setQuery('log.level:*');
await queryBar.submitQuery();
await PageObjects.discover.waitUntilSearchingHasFinished();
await PageObjects.unifiedFieldList.clickFieldListItemAdd('log.level');
const firstCell = await dataGrid.getCellElementExcludingControlColumns(0, 1);
expect(await firstCell.getVisibleText()).to.be('debug');
await testSubjects.missingOrFail('*logLevelBadgeCell-');
});
});
});
}

View file

@ -40,5 +40,6 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid
loadTestFile(require.resolve('./_data_source_profile'));
loadTestFile(require.resolve('./extensions/_get_row_indicator_provider'));
loadTestFile(require.resolve('./extensions/_get_doc_viewer'));
loadTestFile(require.resolve('./extensions/_get_cell_renderers'));
});
}