[Lens] Add accessibility and some functional tests for annotations (#140417)

*  Add accessibility tests for more layer types

* ♻️ Add index number to layer removal button test id

*  Add more missing dataviews tests

*  Add annotation helpers + refactor combobox option selection code

*  Re-align test id using lens prefix

*  Add basic functional test for query annotation

* ♻️ Always use index for remove layer test id

* ♻️ Small refactor

* 🐛 Fix new queryinput changes
This commit is contained in:
Marco Liberati 2022-09-15 17:51:26 +02:00 committed by GitHub
parent e5bc7be278
commit c8db4f2b6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 322 additions and 36 deletions

View file

@ -166,7 +166,7 @@ describe('ConfigPanel', () => {
.first()
.instance();
act(() => {
instance.find('[data-test-subj="lnsLayerRemove"]').first().simulate('click');
instance.find('[data-test-subj="lnsLayerRemove--0"]').first().simulate('click');
});
instance.update();
act(() => {
@ -193,7 +193,7 @@ describe('ConfigPanel', () => {
.first()
.instance();
act(() => {
instance.find('[data-test-subj="lnsLayerRemove"]').at(0).simulate('click');
instance.find('[data-test-subj="lnsLayerRemove--0"]').first().simulate('click');
});
instance.update();
act(() => {
@ -219,7 +219,7 @@ describe('ConfigPanel', () => {
.first()
.instance();
act(() => {
instance.find('[data-test-subj="lnsLayerRemove"]').at(2).simulate('click');
instance.find('[data-test-subj="lnsLayerRemove--1"]').first().simulate('click');
});
instance.update();
act(() => {

View file

@ -137,7 +137,7 @@ describe('LayerPanel', () => {
it('should show the reset button when single layer', async () => {
const { instance } = await mountWithProvider(<LayerPanel {...getDefaultProps()} />);
expect(
instance.find('[data-test-subj="lnsLayerRemove"]').first().props()['aria-label']
instance.find('[data-test-subj="lnsLayerRemove--0"]').first().props()['aria-label']
).toContain('Reset layer');
});
@ -146,7 +146,7 @@ describe('LayerPanel', () => {
<LayerPanel {...getDefaultProps()} isOnlyLayer={false} />
);
expect(
instance.find('[data-test-subj="lnsLayerRemove"]').first().props()['aria-label']
instance.find('[data-test-subj="lnsLayerRemove--0"]').first().props()['aria-label']
).toContain('Delete layer');
});
@ -155,7 +155,7 @@ describe('LayerPanel', () => {
delete layerPanelAttributes.activeVisualization.removeLayer;
const { instance } = await mountWithProvider(<LayerPanel {...getDefaultProps()} />);
expect(
instance.find('[data-test-subj="lnsLayerRemove"]').first().props()['aria-label']
instance.find('[data-test-subj="lnsLayerRemove--0"]').first().props()['aria-label']
).toContain('Reset visualization');
});
@ -165,7 +165,7 @@ describe('LayerPanel', () => {
<LayerPanel {...getDefaultProps()} onRemoveLayer={cb} />
);
act(() => {
instance.find('[data-test-subj="lnsLayerRemove"]').first().simulate('click');
instance.find('[data-test-subj="lnsLayerRemove--0"]').first().simulate('click');
});
instance.update();
act(() => {

View file

@ -158,7 +158,7 @@ export function RemoveLayerButton({
size="xs"
iconType={isOnlyLayer ? 'eraser' : 'trash'}
color="danger"
data-test-subj="lnsLayerRemove"
data-test-subj={`lnsLayerRemove--${layerIndex}`}
aria-label={ariaLabel}
title={ariaLabel}
onClick={() => {

View file

@ -37,6 +37,7 @@ export function FilterQueryInput({
helpMessage,
label = filterByLabel,
initiallyOpen,
['data-test-subj']: dataTestSubj,
}: {
inputFilter: Query | undefined;
onChange: (query: Query) => void;
@ -44,6 +45,7 @@ export function FilterQueryInput({
helpMessage?: string | null;
label?: string;
initiallyOpen?: boolean;
['data-test-subj']?: string;
}) {
const [filterPopoverOpen, setFilterPopoverOpen] = useState(Boolean(initiallyOpen));
const { inputValue: queryInput, handleInputChange: setQueryInput } = useDebouncedValue<Query>({
@ -133,6 +135,7 @@ export function FilterQueryInput({
onChange={setQueryInput}
isInvalid={!isQueryInputValid}
onSubmit={() => {}}
data-test-subj={dataTestSubj}
/>
</EuiFormRow>
</EuiPopover>

View file

@ -264,6 +264,7 @@ export const AnnotationsPanel = (
}
}}
fieldIsInvalid={!fieldIsValid}
data-test-subj="lnsXY-annotation-query-based-text-decoration-field-picker"
autoFocus={!selectedField}
/>
</>

View file

@ -284,16 +284,16 @@ describe('AnnotationsPanel', () => {
);
expect(
component.find('[data-test-subj="annotation-query-based-field-picker"]').exists()
component.find('[data-test-subj="lnsXY-annotation-query-based-field-picker"]').exists()
).toBeTruthy();
expect(
component.find('[data-test-subj="annotation-query-based-query-input"]').exists()
component.find('[data-test-subj="lnsXY-annotation-query-based-query-input"]').exists()
).toBeTruthy();
// The provided indexPattern has 2 date fields
expect(
component
.find('[data-test-subj="annotation-query-based-field-picker"]')
.find('[data-test-subj="lnsXY-annotation-query-based-field-picker"]')
.at(0)
.prop('options')
).toHaveLength(2);

View file

@ -84,6 +84,7 @@ export const ConfigPanelQueryAnnotation = ({
onChange={(query: Query) => {
onChange({ filter: { type: 'kibana_query', ...query } });
}}
data-test-subj="lnsXY-annotation-query-based-query-input"
indexPattern={currentIndexPattern}
/>
</EuiFormRow>
@ -114,7 +115,7 @@ export const ConfigPanelQueryAnnotation = ({
}
}}
fieldIsInvalid={!fieldIsValid}
data-test-subj="annotation-query-based-field-picker"
data-test-subj="lnsXY-annotation-query-based-field-picker"
/>
</EuiFormRow>
</>

View file

@ -198,6 +198,7 @@ export function TooltipSection({
onFieldSelectChange(choice, index);
}}
fieldIsInvalid={!fieldIsValid}
data-test-subj={`lnsXY-annotation-tooltip-field-picker--${index}`}
autoFocus={isNew && value == null}
/>
</EuiFlexItem>

View file

@ -160,6 +160,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await a11y.testAppSnapshot();
});
it('lens XY chart with reference line layer', async () => {
await PageObjects.lens.createLayer('referenceLine');
await a11y.testAppSnapshot();
});
it('lens XY chart with annotations layer', async () => {
await PageObjects.lens.createLayer('annotations');
await a11y.testAppSnapshot();
});
it('saves lens chart', async () => {
await PageObjects.lens.save(lensChartName);
await a11y.testAppSnapshot();

View file

@ -78,5 +78,28 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.lens.closeDimensionEditor();
await testSubjects.existOrFail('xyVisGroupedAnnotationIcon');
});
it('should add query annotation layer and allow edition', async () => {
await PageObjects.lens.removeLayer(1);
await PageObjects.lens.createLayer('annotations');
expect((await find.allByCssSelector(`[data-test-subj^="lns-layerPanel-"]`)).length).to.eql(2);
expect(
await (
await testSubjects.find('lnsXY_xAnnotationsPanel > lns-dimensionTrigger')
).getVisibleText()
).to.eql('Event');
await testSubjects.click('lnsXY_xAnnotationsPanel > lns-dimensionTrigger');
await testSubjects.click('lnsXY_annotation_query');
await PageObjects.lens.configureQueryAnnotation({
queryString: '*',
timeField: 'utc_time',
textDecoration: { type: 'name' },
extraFields: ['clientip'],
});
await PageObjects.lens.closeDimensionEditor();
await testSubjects.existOrFail('xyVisGroupedAnnotationIcon');
});
});
}

View file

@ -64,6 +64,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.lens.waitForMissingDataViewWarningDisappear();
await PageObjects.lens.waitForEmptyWorkspace();
});
it('works fine when the dataViews is missing for referenceLines and annotations', async () => {
await PageObjects.visualize.gotoVisualizationLandingPage();
await listingTable.searchForItemWithName(
'lnsXYWithReferenceLinesAndAnnotationsWithNonExistingDataView'
);
await PageObjects.lens.clickVisualizeListItemTitle(
'lnsXYWithReferenceLinesAndAnnotationsWithNonExistingDataView'
);
await PageObjects.lens.waitForMissingDataViewWarning();
});
});
});
}

View file

@ -75,4 +75,199 @@
"type": "lens",
"updated_at": "2021-10-19T13:41:04.038Z",
"version": "WzU2NjEsMl0="
}
{
"attributes": {
"description": "",
"state": {
"datasourceStates": {
"indexpattern": {
"layers": {
"3c85b7f0-3227-43e7-88ac-9416c6311ebc": {
"columns": {
"951ad12c-8fae-4e81-964d-84827e387515": {
"label": "order_date",
"dataType": "date",
"operationType": "date_histogram",
"sourceField": "order_date",
"isBucketed": true,
"scale": "interval",
"params": {
"interval": "auto",
"includeEmptyRows": true,
"dropPartials": false
}
},
"e311d921-2525-4fb1-9716-94fe787ad623": {
"label": "Count of records",
"dataType": "number",
"operationType": "count",
"isBucketed": false,
"scale": "ratio",
"sourceField": "___records___",
"params": {
"emptyAsNull": true
}
}
},
"columnOrder": [
"951ad12c-8fae-4e81-964d-84827e387515",
"e311d921-2525-4fb1-9716-94fe787ad623"
],
"incompleteColumns": {}
},
"5321ae4b-8f8a-4300-a9bc-ec7245e2cb0f": {
"columns": {
"735cacfd-52af-4ff9-afa5-0e14c1b7c7fd": {
"label": "Static value: 127.5",
"dataType": "number",
"operationType": "static_value",
"isStaticValue": true,
"isBucketed": false,
"scale": "ratio",
"params": {
"value": "127.5"
},
"references": []
}
},
"columnOrder": [
"735cacfd-52af-4ff9-afa5-0e14c1b7c7fd"
],
"incompleteColumns": {}
}
}
}
},
"filters": [],
"query": {
"language": "kuery",
"query": ""
},
"visualization": {
"legend": {
"isVisible": true,
"position": "right"
},
"valueLabels": "hide",
"fittingFunction": "None",
"axisTitlesVisibilitySettings": {
"x": true,
"yLeft": true,
"yRight": true
},
"tickLabelsVisibilitySettings": {
"x": true,
"yLeft": true,
"yRight": true
},
"labelsOrientation": {
"x": 0,
"yLeft": 0,
"yRight": 0
},
"gridlinesVisibilitySettings": {
"x": true,
"yLeft": true,
"yRight": true
},
"preferredSeriesType": "bar_stacked",
"layers": [
{
"layerId": "3c85b7f0-3227-43e7-88ac-9416c6311ebc",
"accessors": [
"e311d921-2525-4fb1-9716-94fe787ad623"
],
"position": "top",
"seriesType": "bar_stacked",
"showGridlines": false,
"layerType": "data",
"xAccessor": "951ad12c-8fae-4e81-964d-84827e387515"
},
{
"layerId": "5321ae4b-8f8a-4300-a9bc-ec7245e2cb0f",
"layerType": "referenceLine",
"accessors": [
"735cacfd-52af-4ff9-afa5-0e14c1b7c7fd"
],
"yConfig": [
{
"forAccessor": "735cacfd-52af-4ff9-afa5-0e14c1b7c7fd",
"axisMode": "left"
}
]
},
{
"layerId": "396c620c-1b6b-4754-a8fa-7f0e830a825c",
"layerType": "annotations",
"annotations": [
{
"label": "Event",
"type": "manual",
"key": {
"type": "point_in_time",
"timestamp": "2022-07-25T22:00:00.000Z"
},
"icon": "triangle",
"id": "13354257-3cd4-46b5-9462-d3fbbab6a433"
},
{
"type": "query",
"id": "06539b10-c487-4aba-bf21-761c014c8d60",
"label": "Event",
"key": {
"type": "point_in_time"
},
"icon": "triangle",
"textVisibility": true,
"textField": "customer_gender",
"filter": {
"type": "kibana_query",
"query": "*",
"language": "kuery"
},
"extraFields": [
"category.keyword"
]
}
]
}
]
}
},
"title": "lnsXYWithReferenceLinesAndAnnotationsWithNonExistingDataView",
"visualizationType": "lnsXY"
},
"coreMigrationVersion": "8.0.0",
"id": "3454af30-30e2-11ec-8dbc-f13e30d4f8ac1",
"migrationVersion": {
"lens": "8.0.0"
},
"references": [
{
"id": "nonExistingDataView",
"name": "indexpattern-datasource-current-indexpattern",
"type": "index-pattern"
},
{
"id": "nonExistingDataView",
"name": "indexpattern-datasource-layer-3c85b7f0-3227-43e7-88ac-9416c6311ebc",
"type": "index-pattern"
},
{
"id": "nonExistingDataView",
"name": "indexpattern-datasource-layer-5321ae4b-8f8a-4300-a9bc-ec7245e2cb0f",
"type": "index-pattern"
},
{
"id": "nonExistingDataView",
"name": "xy-visualization-layer-396c620c-1b6b-4754-a8fa-7f0e830a825c",
"type": "index-pattern"
}
],
"type": "lens",
"updated_at": "2021-10-19T13:41:04.038Z",
"version": "WzU2NjEsMl0="
}

View file

@ -104,6 +104,55 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
});
},
async selectOptionFromComboBox(testTargetId: string, name: string) {
const target = await testSubjects.find(testTargetId, 1000);
await comboBox.openOptionsList(target);
await comboBox.setElement(target, name);
},
async configureQueryAnnotation(opts: {
queryString: string;
timeField: string;
textDecoration?: { type: 'none' | 'name' | 'field'; textField?: string };
extraFields?: string[];
}) {
// type * in the query editor
const queryInput = await testSubjects.find('lnsXY-annotation-query-based-query-input');
await queryInput.type(opts.queryString);
await testSubjects.click('indexPattern-filters-existingFilterTrigger');
await this.selectOptionFromComboBox(
'lnsXY-annotation-query-based-field-picker',
opts.timeField
);
if (opts.textDecoration) {
await testSubjects.click(`lnsXY_textVisibility_${opts.textDecoration.type}`);
if (opts.textDecoration.textField) {
await this.selectOptionFromComboBox(
'lnsXY-annotation-query-based-text-decoration-field-picker',
opts.textDecoration.textField
);
}
}
if (opts.extraFields) {
for (const field of opts.extraFields) {
await this.addFieldToTooltip(field);
}
}
},
async addFieldToTooltip(fieldName: string) {
const lastIndex = (
await find.allByCssSelector('[data-test-subj^="lnsXY-annotation-tooltip-field-picker"]')
).length;
await retry.try(async () => {
await testSubjects.click('lnsXY-annotation-tooltip-add_field');
await this.selectOptionFromComboBox(
`lnsXY-annotation-tooltip-field-picker--${lastIndex}`,
fieldName
);
});
},
/**
* Changes the specified dimension to the specified operation and (optinally) field.
*
@ -150,9 +199,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
});
}
if (opts.field) {
const target = await testSubjects.find('indexPattern-dimension-field');
await comboBox.openOptionsList(target);
await comboBox.setElement(target, opts.field);
await this.selectOptionFromComboBox('indexPattern-dimension-field', opts.field);
}
if (opts.formula) {
@ -188,15 +235,17 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
isPreviousIncompatible?: boolean;
}) {
if (opts.operation) {
const target = await testSubjects.find('indexPattern-subFunction-selection-row');
await comboBox.openOptionsList(target);
await comboBox.setElement(target, opts.operation);
await this.selectOptionFromComboBox(
'indexPattern-subFunction-selection-row',
opts.operation
);
}
if (opts.field) {
const target = await testSubjects.find('indexPattern-reference-field-selection-row');
await comboBox.openOptionsList(target);
await comboBox.setElement(target, opts.field);
await this.selectOptionFromComboBox(
'indexPattern-reference-field-selection-row',
opts.field
);
}
},
@ -588,10 +637,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
).length;
await retry.try(async () => {
await testSubjects.click('indexPattern-terms-add-field');
// count the number of defined terms
const target = await testSubjects.find(`indexPattern-dimension-field-${lastIndex}`, 1000);
await comboBox.openOptionsList(target);
await comboBox.setElement(target, field);
await this.selectOptionFromComboBox(`indexPattern-dimension-field-${lastIndex}`, field);
});
},
@ -608,7 +654,6 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
});
// count the number of defined terms
const target = await testSubjects.find(`indexPattern-dimension-field-${lastIndex}`);
// await comboBox.openOptionsList(target);
for (const field of fields) {
await comboBox.setCustom(`indexPattern-dimension-field-${lastIndex}`, field);
await comboBox.openOptionsList(target);
@ -661,9 +706,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
await testSubjects.setValue('column-label-edit', label, { clearWithKeyboard: true });
},
async editDimensionFormat(format: string) {
const formatInput = await testSubjects.find('indexPattern-dimension-format');
await comboBox.openOptionsList(formatInput);
await comboBox.setElement(formatInput, format);
await this.selectOptionFromComboBox('indexPattern-dimension-format', format);
},
async editDimensionColor(color: string) {
const colorPickerInput = await testSubjects.find('~indexPattern-dimension-colorPicker');
@ -812,17 +855,15 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
* Returns the number of layers visible in the chart configuration
*/
async getLayerCount() {
const elements = await testSubjects.findAll('lnsLayerRemove');
return elements.length;
return (await find.allByCssSelector(`[data-test-subj^="lns-layerPanel-"]`)).length;
},
/**
* Adds a new layer to the chart, fails if the chart does not support new layers
*/
async createLayer(layerType: string = 'data') {
async createLayer(layerType: 'data' | 'referenceLine' | 'annotations' = 'data') {
await testSubjects.click('lnsLayerAddButton');
const layerCount = (await find.allByCssSelector(`[data-test-subj^="lns-layerPanel-"]`))
.length;
const layerCount = await this.getLayerCount();
await retry.waitFor('check for layer type support', async () => {
const fasterChecks = await Promise.all([
@ -1257,9 +1298,9 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
},
/** resets visualization/layer or removes a layer */
async removeLayer() {
async removeLayer(index: number = 0) {
await retry.try(async () => {
await testSubjects.click('lnsLayerRemove');
await testSubjects.click(`lnsLayerRemove--${index}`);
if (await testSubjects.exists('lnsLayerRemoveModal')) {
await testSubjects.exists('lnsLayerRemoveConfirmButton');
await testSubjects.click('lnsLayerRemoveConfirmButton');