Elasticsearch: fix a11y issues in datasource settings (#43706)

pull/43948/head
Giordano Ricci 3 years ago committed by GitHub
parent c8ef541c02
commit afd110309c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      packages/grafana-ui/src/components/Forms/InlineField.tsx
  2. 2
      public/app/core/components/TransformersUI/configFromQuery/ConfigFromQueryTransformerEditor.test.tsx
  3. 121
      public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.test.tsx
  4. 207
      public/app/plugins/datasource/elasticsearch/configuration/ElasticDetails.tsx
  5. 30
      public/app/plugins/datasource/elasticsearch/configuration/LogsConfig.test.tsx
  6. 44
      public/app/plugins/datasource/elasticsearch/configuration/LogsConfig.tsx
  7. 5
      public/app/plugins/datasource/elasticsearch/configuration/mocks.ts
  8. 2
      public/app/plugins/datasource/testdata/QueryEditor.test.tsx

@ -1,11 +1,12 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { cx, css } from '@emotion/css'; import { cx, css } from '@emotion/css';
import { GrafanaTheme } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { useTheme } from '../../themes'; import { useTheme2 } from '../../themes';
import { InlineLabel } from './InlineLabel'; import { InlineLabel } from './InlineLabel';
import { PopoverContent } from '../Tooltip/Tooltip'; import { PopoverContent } from '../Tooltip/Tooltip';
import { FieldProps } from './Field'; import { FieldProps } from './Field';
import { getChildId } from '../../utils/reactUtils'; import { getChildId } from '../../utils/reactUtils';
import { FieldValidationMessage } from './FieldValidationMessage';
export interface Props extends Omit<FieldProps, 'css' | 'horizontal' | 'description' | 'error'> { export interface Props extends Omit<FieldProps, 'css' | 'horizontal' | 'description' | 'error'> {
/** Content for the label's tooltip */ /** Content for the label's tooltip */
@ -16,6 +17,8 @@ export interface Props extends Omit<FieldProps, 'css' | 'horizontal' | 'descript
grow?: boolean; grow?: boolean;
/** Make field's background transparent */ /** Make field's background transparent */
transparent?: boolean; transparent?: boolean;
/** Error message to display */
error?: string | null;
htmlFor?: string; htmlFor?: string;
} }
@ -30,10 +33,11 @@ export const InlineField: FC<Props> = ({
className, className,
htmlFor, htmlFor,
grow, grow,
error,
transparent, transparent,
...htmlProps ...htmlProps
}) => { }) => {
const theme = useTheme(); const theme = useTheme2();
const styles = getStyles(theme, grow); const styles = getStyles(theme, grow);
const inputId = htmlFor ?? getChildId(children); const inputId = htmlFor ?? getChildId(children);
@ -49,14 +53,21 @@ export const InlineField: FC<Props> = ({
return ( return (
<div className={cx(styles.container, className)} {...htmlProps}> <div className={cx(styles.container, className)} {...htmlProps}>
{labelElement} {labelElement}
{React.cloneElement(children, { invalid, disabled, loading })} <div className={styles.childContainer}>
{React.cloneElement(children, { invalid, disabled, loading })}
{invalid && error && (
<div className={cx(styles.fieldValidationWrapper)}>
<FieldValidationMessage>{error}</FieldValidationMessage>
</div>
)}
</div>
</div> </div>
); );
}; };
InlineField.displayName = 'InlineField'; InlineField.displayName = 'InlineField';
const getStyles = (theme: GrafanaTheme, grow?: boolean) => { const getStyles = (theme: GrafanaTheme2, grow?: boolean) => {
return { return {
container: css` container: css`
display: flex; display: flex;
@ -65,7 +76,13 @@ const getStyles = (theme: GrafanaTheme, grow?: boolean) => {
text-align: left; text-align: left;
position: relative; position: relative;
flex: ${grow ? 1 : 0} 0 auto; flex: ${grow ? 1 : 0} 0 auto;
margin: 0 ${theme.spacing.xs} ${theme.spacing.xs} 0; margin: 0 ${theme.spacing(0.5)} ${theme.spacing(0.5)} 0;
`,
childContainer: css`
flex: ${grow ? 1 : 0} 0 auto;
`,
fieldValidationWrapper: css`
margin-top: ${theme.spacing(0.5)};
`, `,
}; };
}; };

@ -38,7 +38,7 @@ describe('ConfigFromQueryTransformerEditor', () => {
it('Should be able to select config frame by refId', async () => { it('Should be able to select config frame by refId', async () => {
setup(); setup();
let select = (await screen.findByText('Config query')).nextSibling!; let select = (await screen.findByText('Config query')).nextSibling!.firstChild!;
await fireEvent.keyDown(select, { keyCode: 40 }); await fireEvent.keyDown(select, { keyCode: 40 });
await selectOptionInTest(select as HTMLElement, 'A'); await selectOptionInTest(select as HTMLElement, 'A');

@ -1,92 +1,89 @@
import React from 'react'; import React from 'react';
import { last } from 'lodash';
import { mount } from 'enzyme';
import { ElasticDetails } from './ElasticDetails'; import { ElasticDetails } from './ElasticDetails';
import { createDefaultConfigOptions } from './mocks'; import { createDefaultConfigOptions } from './mocks';
import { LegacyForms } from '@grafana/ui'; import { render, screen } from '@testing-library/react';
const { Select } = LegacyForms; import selectEvent from 'react-select-event';
describe('ElasticDetails', () => { describe('ElasticDetails', () => {
it('should render without error', () => { describe('Max concurrent Shard Requests', () => {
mount(<ElasticDetails onChange={() => {}} value={createDefaultConfigOptions()} />); it('should render "Max concurrent Shard Requests" if version >= 5.6.0', () => {
}); render(<ElasticDetails onChange={() => {}} value={createDefaultConfigOptions({ esVersion: '5.6.0' })} />);
expect(screen.getByLabelText('Max concurrent Shard Requests')).toBeInTheDocument();
it('should render "Max concurrent Shard Requests" if version high enough', () => { });
const wrapper = mount(<ElasticDetails onChange={() => {}} value={createDefaultConfigOptions()} />);
expect(wrapper.find('input[aria-label="Max concurrent Shard Requests input"]').length).toBe(1);
});
it('should not render "Max concurrent Shard Requests" if version is low', () => { it('should not render "Max concurrent Shard Requests" if version < 5.6.0', () => {
const options = createDefaultConfigOptions(); render(<ElasticDetails onChange={() => {}} value={createDefaultConfigOptions({ esVersion: '5.0.0' })} />);
options.jsonData.esVersion = '5.0.0'; expect(screen.queryByLabelText('Max concurrent Shard Requests')).not.toBeInTheDocument();
const wrapper = mount(<ElasticDetails onChange={() => {}} value={options} />); });
expect(wrapper.find('input[aria-label="Max concurrent Shard Requests input"]').length).toBe(0);
}); });
it('should change database on interval change when not set explicitly', () => { it('should change database on interval change when not set explicitly', async () => {
const onChangeMock = jest.fn(); const onChangeMock = jest.fn();
const wrapper = mount(<ElasticDetails onChange={onChangeMock} value={createDefaultConfigOptions()} />); render(<ElasticDetails onChange={onChangeMock} value={createDefaultConfigOptions()} />);
const selectEl = wrapper.find({ label: 'Pattern' }).find(Select); const selectEl = screen.getByLabelText('Pattern');
selectEl.props().onChange({ value: 'Daily', label: 'Daily' }, { action: 'select-option', option: undefined });
expect(onChangeMock.mock.calls[0][0].jsonData.interval).toBe('Daily'); await selectEvent.select(selectEl, 'Daily', { container: document.body });
expect(onChangeMock.mock.calls[0][0].database).toBe('[logstash-]YYYY.MM.DD');
expect(onChangeMock).toHaveBeenLastCalledWith(
expect.objectContaining({
database: '[logstash-]YYYY.MM.DD',
jsonData: expect.objectContaining({ interval: 'Daily' }),
})
);
}); });
it('should change database on interval change if pattern is from example', () => { it('should change database on interval change if pattern is from example', async () => {
const onChangeMock = jest.fn(); const onChangeMock = jest.fn();
const options = createDefaultConfigOptions(); const options = createDefaultConfigOptions();
options.database = '[logstash-]YYYY.MM.DD.HH'; options.database = '[logstash-]YYYY.MM.DD.HH';
const wrapper = mount(<ElasticDetails onChange={onChangeMock} value={options} />); render(<ElasticDetails onChange={onChangeMock} value={options} />);
const selectEl = screen.getByLabelText('Pattern');
const selectEl = wrapper.find({ label: 'Pattern' }).find(Select); await selectEvent.select(selectEl, 'Monthly', { container: document.body });
selectEl.props().onChange({ value: 'Monthly', label: 'Monthly' }, { action: 'select-option', option: undefined });
expect(onChangeMock.mock.calls[0][0].jsonData.interval).toBe('Monthly'); expect(onChangeMock).toHaveBeenLastCalledWith(
expect(onChangeMock.mock.calls[0][0].database).toBe('[logstash-]YYYY.MM'); expect.objectContaining({
database: '[logstash-]YYYY.MM',
jsonData: expect.objectContaining({ interval: 'Monthly' }),
})
);
}); });
describe('version change', () => { describe('version change', () => {
const testCases = [ const testCases = [
{ version: '5.0.0', expectedMaxConcurrentShardRequests: 256 }, { version: '5.x', expectedMaxConcurrentShardRequests: 256 },
{ version: '5.0.0', maxConcurrentShardRequests: 50, expectedMaxConcurrentShardRequests: 50 }, { version: '5.x', maxConcurrentShardRequests: 50, expectedMaxConcurrentShardRequests: 50 },
{ version: '5.6.0', expectedMaxConcurrentShardRequests: 256 }, { version: '5.6+', expectedMaxConcurrentShardRequests: 256 },
{ version: '5.6.0', maxConcurrentShardRequests: 256, expectedMaxConcurrentShardRequests: 256 }, { version: '5.6+', maxConcurrentShardRequests: 256, expectedMaxConcurrentShardRequests: 256 },
{ version: '5.6.0', maxConcurrentShardRequests: 5, expectedMaxConcurrentShardRequests: 256 }, { version: '5.6+', maxConcurrentShardRequests: 5, expectedMaxConcurrentShardRequests: 256 },
{ version: '5.6.0', maxConcurrentShardRequests: 200, expectedMaxConcurrentShardRequests: 200 }, { version: '5.6+', maxConcurrentShardRequests: 200, expectedMaxConcurrentShardRequests: 200 },
{ version: '7.0.0', expectedMaxConcurrentShardRequests: 5 }, { version: '7.0+', expectedMaxConcurrentShardRequests: 5 },
{ version: '7.0.0', maxConcurrentShardRequests: 256, expectedMaxConcurrentShardRequests: 5 }, { version: '7.0+', maxConcurrentShardRequests: 256, expectedMaxConcurrentShardRequests: 5 },
{ version: '7.0.0', maxConcurrentShardRequests: 5, expectedMaxConcurrentShardRequests: 5 }, { version: '7.0+', maxConcurrentShardRequests: 5, expectedMaxConcurrentShardRequests: 5 },
{ version: '7.0.0', maxConcurrentShardRequests: 6, expectedMaxConcurrentShardRequests: 6 }, { version: '7.0+', maxConcurrentShardRequests: 6, expectedMaxConcurrentShardRequests: 6 },
]; ];
const onChangeMock = jest.fn();
const options = createDefaultConfigOptions();
const wrapper = mount(<ElasticDetails onChange={onChangeMock} value={options} />);
testCases.forEach((tc) => { testCases.forEach((tc) => {
it(`sets maxConcurrentShardRequests = ${tc.maxConcurrentShardRequests} if version = ${tc.version},`, () => { const onChangeMock = jest.fn();
wrapper.setProps({ it(`sets maxConcurrentShardRequests=${tc.expectedMaxConcurrentShardRequests} if version=${tc.version},`, async () => {
onChange: onChangeMock, render(
value: { <ElasticDetails
...options, onChange={onChangeMock}
jsonData: { value={createDefaultConfigOptions({
...options.jsonData,
maxConcurrentShardRequests: tc.maxConcurrentShardRequests, maxConcurrentShardRequests: tc.maxConcurrentShardRequests,
}, esVersion: '2.0.0',
}, })}
}); />
);
const selectEl = screen.getByLabelText('ElasticSearch version');
const selectEl = wrapper.find({ label: 'Version' }).find(Select); await selectEvent.select(selectEl, tc.version, { container: document.body });
selectEl
.props()
.onChange(
{ value: tc.version, label: tc.version.toString() },
{ action: 'select-option', option: undefined }
);
expect(last(onChangeMock.mock.calls)[0].jsonData.maxConcurrentShardRequests).toBe( expect(onChangeMock).toHaveBeenCalledWith(
tc.expectedMaxConcurrentShardRequests expect.objectContaining({
jsonData: expect.objectContaining({ maxConcurrentShardRequests: tc.expectedMaxConcurrentShardRequests }),
})
); );
}); });
}); });

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { EventsWithValidation, regexValidation, LegacyForms } from '@grafana/ui'; import { FieldSet, InlineField, Input, Select, InlineSwitch } from '@grafana/ui';
const { Switch, Select, Input, FormField } = LegacyForms;
import { ElasticsearchOptions, Interval } from '../types'; import { ElasticsearchOptions, Interval } from '../types';
import { DataSourceSettings, SelectableValue } from '@grafana/data'; import { DataSourceSettings, SelectableValue } from '@grafana/data';
import { gte, lt, valid } from 'semver'; import { gte, lt, valid } from 'semver';
@ -45,140 +44,116 @@ export const ElasticDetails = ({ value, onChange }: Props) => {
: undefined; : undefined;
return ( return (
<> <>
<h3 className="page-heading">Elasticsearch details</h3> <FieldSet label="Elasticsearch details">
<InlineField label="Index name" labelWidth={26}>
<div className="gf-form-group"> <Input
<div className="gf-form-inline"> id="es_config_indexName"
<div className="gf-form"> value={value.database || ''}
<FormField onChange={changeHandler('database', value, onChange)}
labelWidth={10} width={24}
inputWidth={15} placeholder="es-index-name"
label="Index name" required
value={value.database || ''} />
onChange={changeHandler('database', value, onChange)} </InlineField>
placeholder={'es-index-name'}
required <InlineField label="Pattern" labelWidth={26}>
/> <Select
</div> inputId="es_config_indexPattern"
value={indexPatternTypes.find(
<div className="gf-form"> (pattern) => pattern.value === (value.jsonData.interval === undefined ? 'none' : value.jsonData.interval)
<FormField )}
labelWidth={10} options={indexPatternTypes}
label="Pattern" onChange={intervalHandler(value, onChange)}
inputEl={ width={24}
<Select menuShouldPortal
menuShouldPortal />
options={indexPatternTypes} </InlineField>
onChange={intervalHandler(value, onChange)}
value={indexPatternTypes.find(
(pattern) =>
pattern.value === (value.jsonData.interval === undefined ? 'none' : value.jsonData.interval)
)}
/>
}
/>
</div>
</div>
<div className="gf-form max-width-25"> <InlineField label="Time field name" labelWidth={26}>
<FormField <Input
labelWidth={10} id="es_config_timeField"
inputWidth={15}
label="Time field name"
value={value.jsonData.timeField || ''} value={value.jsonData.timeField || ''}
onChange={jsonDataChangeHandler('timeField', value, onChange)} onChange={jsonDataChangeHandler('timeField', value, onChange)}
width={24}
placeholder="@timestamp"
required required
/> />
</div> </InlineField>
<div className="gf-form"> <InlineField label="ElasticSearch version" labelWidth={26}>
<FormField <Select
labelWidth={10} inputId="es_config_version"
label="Version" options={[customOption, ...esVersions].filter(isTruthy)}
inputEl={ onChange={(option) => {
<Select const maxConcurrentShardRequests = getMaxConcurrenShardRequestOrDefault(
menuShouldPortal value.jsonData.maxConcurrentShardRequests,
options={[customOption, ...esVersions].filter(isTruthy)} option.value!
onChange={(option) => { );
const maxConcurrentShardRequests = getMaxConcurrenShardRequestOrDefault( onChange({
value.jsonData.maxConcurrentShardRequests, ...value,
option.value! jsonData: {
); ...value.jsonData,
onChange({ esVersion: option.value!,
...value, maxConcurrentShardRequests,
jsonData: { },
...value.jsonData, });
esVersion: option.value!, }}
maxConcurrentShardRequests, value={currentVersion || customOption}
}, width={24}
}); menuShouldPortal
}}
value={currentVersion || customOption}
/>
}
/> />
</div> </InlineField>
{gte(value.jsonData.esVersion, '5.6.0') && ( {gte(value.jsonData.esVersion, '5.6.0') && (
<div className="gf-form max-width-30"> <InlineField label="Max concurrent Shard Requests" labelWidth={26}>
<FormField <Input
aria-label={'Max concurrent Shard Requests input'} id="es_config_shardRequests"
labelWidth={15}
label="Max concurrent Shard Requests"
value={value.jsonData.maxConcurrentShardRequests || ''} value={value.jsonData.maxConcurrentShardRequests || ''}
onChange={jsonDataChangeHandler('maxConcurrentShardRequests', value, onChange)} onChange={jsonDataChangeHandler('maxConcurrentShardRequests', value, onChange)}
width={24}
/> />
</div> </InlineField>
)} )}
<div className="gf-form-inline">
<div className="gf-form"> <InlineField
<FormField label="Min time interval"
labelWidth={10} labelWidth={26}
label="Min time interval" tooltip={
inputEl={ <>
<Input A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example{' '}
className={'width-6'} <code>1m</code> if your data is written every minute.
value={value.jsonData.timeInterval || ''} </>
onChange={jsonDataChangeHandler('timeInterval', value, onChange)} }
placeholder="10s" error="Value is not valid, you can use number with time unit specifier: y, M, w, d, h, m, s"
validationEvents={{ invalid={!!value.jsonData.timeInterval && !/^\d+(ms|[Mwdhmsy])$/.test(value.jsonData.timeInterval)}
[EventsWithValidation.onBlur]: [ >
regexValidation( <Input
/^\d+(ms|[Mwdhmsy])$/, id="es_config_minTimeInterval"
'Value is not valid, you can use number with time unit specifier: y, M, w, d, h, m, s' value={value.jsonData.timeInterval || ''}
), onChange={jsonDataChangeHandler('timeInterval', value, onChange)}
], width={24}
}} placeholder="10s"
/> />
} </InlineField>
tooltip={
<> <InlineField label="X-Pack enabled" labelWidth={26}>
A lower limit for the auto group by time interval. Recommended to be set to write frequency, for <InlineSwitch
example <code>1m</code> if your data is written every minute. id="es_config_xpackEnabled"
</>
}
/>
</div>
</div>
<div className="gf-form-inline">
<Switch
label="X-Pack enabled"
labelClass="width-10"
checked={value.jsonData.xpack || false} checked={value.jsonData.xpack || false}
onChange={jsonDataSwitchChangeHandler('xpack', value, onChange)} onChange={jsonDataSwitchChangeHandler('xpack', value, onChange)}
/> />
</div> </InlineField>
{gte(value.jsonData.esVersion, '6.6.0') && value.jsonData.xpack && ( {gte(value.jsonData.esVersion, '6.6.0') && value.jsonData.xpack && (
<div className="gf-form-inline"> <InlineField label="Include Frozen Indices" labelWidth={26}>
<Switch <InlineSwitch
label="Include frozen indices" id="es_config_frozenIndices"
labelClass="width-10"
checked={value.jsonData.includeFrozen ?? false} checked={value.jsonData.includeFrozen ?? false}
onChange={jsonDataSwitchChangeHandler('includeFrozen', value, onChange)} onChange={jsonDataSwitchChangeHandler('includeFrozen', value, onChange)}
/> />
</div> </InlineField>
)} )}
</div> </FieldSet>
</> </>
); );
}; };

@ -1,26 +1,22 @@
import React from 'react'; import React from 'react';
import { mount, shallow } from 'enzyme'; import { render, screen, fireEvent } from '@testing-library/react';
import { LogsConfig } from './LogsConfig'; import { LogsConfig } from './LogsConfig';
import { createDefaultConfigOptions } from './mocks'; import { createDefaultConfigOptions } from './mocks';
import { LegacyForms } from '@grafana/ui';
const { FormField } = LegacyForms;
describe('ElasticDetails', () => { describe('ElasticDetails', () => {
it('should render without error', () => {
mount(<LogsConfig onChange={() => {}} value={createDefaultConfigOptions().jsonData} />);
});
it('should render fields', () => {
const wrapper = shallow(<LogsConfig onChange={() => {}} value={createDefaultConfigOptions().jsonData} />);
expect(wrapper.find(FormField).length).toBe(2);
});
it('should pass correct data to onChange', () => { it('should pass correct data to onChange', () => {
const onChangeMock = jest.fn(); const onChangeMock = jest.fn();
const wrapper = mount(<LogsConfig onChange={onChangeMock} value={createDefaultConfigOptions().jsonData} />); const expectedMessageField = '@message';
const inputEl = wrapper.find(FormField).at(0).find('input'); const expectedLevelField = '@level';
(inputEl.getDOMNode() as any).value = 'test_field';
inputEl.simulate('change'); render(<LogsConfig onChange={onChangeMock} value={createDefaultConfigOptions().jsonData} />);
expect(onChangeMock.mock.calls[0][0].logMessageField).toBe('test_field'); const messageField = screen.getByLabelText('Message field name');
const levelField = screen.getByLabelText('Level field name');
fireEvent.change(messageField, { target: { value: expectedMessageField } });
expect(onChangeMock).toHaveBeenLastCalledWith(expect.objectContaining({ logMessageField: expectedMessageField }));
fireEvent.change(levelField, { target: { value: expectedLevelField } });
expect(onChangeMock).toHaveBeenLastCalledWith(expect.objectContaining({ logLevelField: expectedLevelField }));
}); });
}); });

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { LegacyForms } from '@grafana/ui'; import { Input, InlineField, FieldSet } from '@grafana/ui';
const { FormField } = LegacyForms;
import { ElasticsearchOptions } from '../types'; import { ElasticsearchOptions } from '../types';
type Props = { type Props = {
@ -19,28 +18,25 @@ export const LogsConfig = (props: Props) => {
}; };
return ( return (
<> <FieldSet label="Logs">
<h3 className="page-heading">Logs</h3> <InlineField label="Message field name" labelWidth={22}>
<Input
id="es_logs-config_logMessageField"
value={value.logMessageField}
onChange={changeHandler('logMessageField')}
placeholder="_source"
width={24}
/>
</InlineField>
<div className="gf-form-group"> <InlineField label="Level field name" labelWidth={22}>
<div className="gf-form max-width-30"> <Input
<FormField id="es_logs-config_logLevelField"
labelWidth={11} value={value.logLevelField}
label="Message field name" onChange={changeHandler('logLevelField')}
value={value.logMessageField} width={24}
onChange={changeHandler('logMessageField')} />
placeholder="_source" </InlineField>
/> </FieldSet>
</div>
<div className="gf-form max-width-30">
<FormField
labelWidth={11}
label="Level field name"
value={value.logLevelField}
onChange={changeHandler('logLevelField')}
/>
</div>
</div>
</>
); );
}; };

@ -2,7 +2,9 @@ import { DataSourceSettings } from '@grafana/data';
import { ElasticsearchOptions } from '../types'; import { ElasticsearchOptions } from '../types';
import { createDatasourceSettings } from '../../../../features/datasources/mocks'; import { createDatasourceSettings } from '../../../../features/datasources/mocks';
export function createDefaultConfigOptions(): DataSourceSettings<ElasticsearchOptions> { export function createDefaultConfigOptions(
options?: Partial<ElasticsearchOptions>
): DataSourceSettings<ElasticsearchOptions> {
return createDatasourceSettings<ElasticsearchOptions>({ return createDatasourceSettings<ElasticsearchOptions>({
timeField: '@time', timeField: '@time',
esVersion: '7.0.0', esVersion: '7.0.0',
@ -11,5 +13,6 @@ export function createDefaultConfigOptions(): DataSourceSettings<ElasticsearchOp
maxConcurrentShardRequests: 300, maxConcurrentShardRequests: 300,
logMessageField: 'test.message', logMessageField: 'test.message',
logLevelField: 'test.level', logLevelField: 'test.level',
...options,
}); });
} }

@ -37,7 +37,7 @@ describe('Test Datasource Query Editor', () => {
it('should switch scenario and display its default values', async () => { it('should switch scenario and display its default values', async () => {
const { rerender } = setup(); const { rerender } = setup();
let select = (await screen.findByText('Scenario')).nextSibling!; let select = (await screen.findByText('Scenario')).nextSibling!.firstChild!;
await fireEvent.keyDown(select, { keyCode: 40 }); await fireEvent.keyDown(select, { keyCode: 40 });
const scs = screen.getAllByLabelText('Select option'); const scs = screen.getAllByLabelText('Select option');

Loading…
Cancel
Save