[Metrics UI] Add additional functional test for Inventory View (#116868) (#118407)

* [Metrics UI] Add functional test for group by and sorting for Inventory View

* refactor groupByCustomField

* Adding search test

* Adding colors getNodesWithValues; adding test for changing palette;

* Adding basic test for timeline controls

* Closing the popover after clicking apply

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Chris Cowan <chris@elastic.co>
This commit is contained in:
Kibana Machine 2021-11-11 14:30:34 -05:00 committed by GitHub
parent 706446ff09
commit 7667ca42a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 190 additions and 10 deletions

View file

@ -73,6 +73,7 @@ export class AutocompleteField extends React.Component<
placeholder={placeholder}
value={value}
aria-label={ariaLabel}
data-test-subj="infraSearchField"
/>
{areSuggestionsVisible && !isLoadingSuggestions && suggestions.length > 0 ? (
<SuggestionsPanel>

View file

@ -52,6 +52,7 @@ export const BottomDrawer: React.FC<{
aria-expanded={isOpen}
iconType={isOpen ? 'arrowDown' : 'arrowRight'}
onClick={onClick}
data-test-subj="toggleTimelineButton"
>
{isOpen ? hideHistory : showHistory}
</ShowHideButton>

View file

@ -210,7 +210,9 @@ export const Timeline: React.FC<Props> = ({ interval, yAxisFormatter, isVisible
}
return (
<TimelineContainer>
<TimelineContainer
data-test-subj={isVisible ? 'timelineContainerOpen' : 'timelineContainerClosed'}
>
<TimelineHeader>
<EuiFlexItem grow={true}>
<EuiText>

View file

@ -55,6 +55,7 @@ export class CustomFieldPanel extends React.PureComponent<Props, State> {
fullWidth
>
<EuiComboBox
data-test-subj="groupByCustomField"
placeholder={i18n.translate('xpack.infra.waffle.customGroupByDropdownPlacehoder', {
defaultMessage: 'Select one',
})}
@ -67,6 +68,7 @@ export class CustomFieldPanel extends React.PureComponent<Props, State> {
/>
</EuiFormRow>
<EuiButton
data-test-subj="groupByCustomFieldAddButton"
disabled={isSubmitDisabled}
type="submit"
size="s"

View file

@ -29,7 +29,11 @@ export class GroupName extends React.PureComponent<Props, {}> {
<Inner isChild={isChild}>
<Name>
<EuiToolTip position="top" content={group.name}>
<EuiLink style={linkStyle} onClickCapture={this.handleClick}>
<EuiLink
style={linkStyle}
onClickCapture={this.handleClick}
data-test-subj="groupNameLink"
>
{group.name}
</EuiLink>
</EuiToolTip>

View file

@ -84,6 +84,7 @@ export const LegendControls = ({
defaultMessage: 'configure legend',
})}
onClick={() => setPopoverState(true)}
data-test-subj="openLegendControlsButton"
/>
);
@ -131,6 +132,7 @@ export const LegendControls = ({
bounds: { min: draftBounds.min / 100, max: draftBounds.max / 100 },
legend: draftLegend,
});
setPopoverState(false);
}, [onChange, draftAuto, draftBounds, draftLegend]);
const handleCancelClick = useCallback(() => {
@ -179,7 +181,7 @@ export const LegendControls = ({
: [];
return (
<ControlContainer>
<ControlContainer data-test-subj="legendControls">
<EuiPopover
isOpen={isPopoverOpen}
closePopover={handleCancelClick}
@ -201,6 +203,7 @@ export const LegendControls = ({
id="palette"
onChange={handlePaletteChange}
compressed
data-test-subj="legendControlsPalette"
/>
<EuiSpacer size="m" />
<PalettePreview
@ -338,6 +341,7 @@ export const LegendControls = ({
fill
disabled={commited || !boundsValidRange}
onClick={handleApplyClick}
data-test-subj="applyLegendControlsButton"
>
<FormattedMessage
id="xpack.infra.legendControls.applyButton"

View file

@ -78,8 +78,12 @@ export class Node extends React.PureComponent<Props, State> {
<SquareInner color={color}>
{valueMode ? (
<ValueInner aria-label={nodeAriaLabel}>
<Label color={color}>{node.name}</Label>
<Value color={color}>{value}</Value>
<Label data-test-subj="nodeName" color={color}>
{node.name}
</Label>
<Value data-test-subj="nodeValue" color={color}>
{value}
</Value>
</ValueInner>
) : (
ellipsisMode && (

View file

@ -134,6 +134,7 @@ export class WaffleGroupByControls extends React.PureComponent<Props, State> {
<DropdownButton
label={i18n.translate('xpack.infra.waffle.groupByLabel', { defaultMessage: 'Group by' })}
onClick={this.handleToggle}
data-test-subj={'waffleGroupByDropdown'}
>
{buttonBody}
</DropdownButton>
@ -147,7 +148,11 @@ export class WaffleGroupByControls extends React.PureComponent<Props, State> {
panelPaddingSize="none"
closePopover={this.handleClose}
>
<StyledContextMenu initialPanelId="firstPanel" panels={panels} />
<StyledContextMenu
initialPanelId="firstPanel"
panels={panels}
data-test-subj="groupByContextMenu"
/>
</EuiPopover>
);
}

View file

@ -39,6 +39,7 @@ export const WaffleSortControls = ({ sort, onChange }: Props) => {
<DropdownButton
label={i18n.translate('xpack.infra.waffle.sortLabel', { defaultMessage: 'Sort by' })}
onClick={showPopover}
data-test-subj={'waffleSortByDropdown'}
>
{label}
</DropdownButton>
@ -59,7 +60,8 @@ export const WaffleSortControls = ({ sort, onChange }: Props) => {
...sort,
direction: sort.direction === 'asc' ? 'desc' : 'asc',
});
}, [sort, onChange]);
closePopover();
}, [closePopover, sort, onChange]);
const panels = useMemo<EuiContextMenuPanelDescriptor[]>(
() => [
@ -71,11 +73,13 @@ export const WaffleSortControls = ({ sort, onChange }: Props) => {
name: LABELS.name,
icon: sort.by === 'name' ? 'check' : 'empty',
onClick: selectName,
'data-test-subj': 'waffleSortByName',
},
{
name: LABELS.value,
icon: sort.by === 'value' ? 'check' : 'empty',
onClick: selectValue,
'data-test-subj': 'waffleSortByValue',
},
],
},
@ -101,6 +105,7 @@ export const WaffleSortControls = ({ sort, onChange }: Props) => {
})}
checked={sort.direction === 'desc'}
onChange={toggleSort}
data-test-subj={'waffleSortByDirection'}
/>
</SwitchContainer>
</EuiPopover>

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
import { DATES } from './constants';
@ -44,15 +45,86 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs')
);
it('renders an empty data prompt for dates with no data', async () => {
await pageObjects.infraHome.goToTime(DATE_WITHOUT_DATA);
await pageObjects.infraHome.getNoMetricsDataPrompt();
});
it('renders the waffle map and tooltips for dates with data', async () => {
await pageObjects.infraHome.goToTime(DATE_WITH_DATA);
await pageObjects.infraHome.getWaffleMap();
await pageObjects.infraHome.getWaffleMapTooltips();
});
it('renders an empty data prompt for dates with no data', async () => {
await pageObjects.infraHome.goToTime(DATE_WITHOUT_DATA);
await pageObjects.infraHome.getNoMetricsDataPrompt();
it('sort nodes by descending value', async () => {
await pageObjects.infraHome.goToTime(DATE_WITH_DATA);
await pageObjects.infraHome.getWaffleMap();
await pageObjects.infraHome.sortNodesBy('value');
const nodesWithValue = await pageObjects.infraHome.getNodesWithValues();
expect(nodesWithValue).to.eql([
{ name: 'demo-stack-apache-01', value: 1.4, color: '#6092c0' },
{ name: 'demo-stack-mysql-01', value: 1.2, color: '#82a7cd' },
{ name: 'demo-stack-nginx-01', value: 1.1, color: '#93b1d3' },
{ name: 'demo-stack-redis-01', value: 1, color: '#a2bcd9' },
{ name: 'demo-stack-haproxy-01', value: 0.8, color: '#c2d2e6' },
{ name: 'demo-stack-client-01', value: 0.6, color: '#f0f4f9' },
]);
});
it('sort nodes by ascending value', async () => {
await pageObjects.infraHome.goToTime(DATE_WITH_DATA);
await pageObjects.infraHome.getWaffleMap();
await pageObjects.infraHome.sortNodesBy('value');
await pageObjects.infraHome.toggleReverseSort();
const nodesWithValue = await pageObjects.infraHome.getNodesWithValues();
expect(nodesWithValue).to.eql([
{ name: 'demo-stack-client-01', value: 0.6, color: '#f0f4f9' },
{ name: 'demo-stack-haproxy-01', value: 0.8, color: '#c2d2e6' },
{ name: 'demo-stack-redis-01', value: 1, color: '#a2bcd9' },
{ name: 'demo-stack-nginx-01', value: 1.1, color: '#93b1d3' },
{ name: 'demo-stack-mysql-01', value: 1.2, color: '#82a7cd' },
{ name: 'demo-stack-apache-01', value: 1.4, color: '#6092c0' },
]);
});
it('group nodes by custom field', async () => {
await pageObjects.infraHome.goToTime(DATE_WITH_DATA);
await pageObjects.infraHome.getWaffleMap();
const groups = await pageObjects.infraHome.groupByCustomField('host.os.platform');
expect(groups).to.eql(['ubuntu']);
});
it('filter nodes by search term', async () => {
await pageObjects.infraHome.goToTime(DATE_WITH_DATA);
await pageObjects.infraHome.getWaffleMap();
await pageObjects.infraHome.enterSearchTerm('host.name: "demo-stack-apache-01"');
const nodesWithValue = await pageObjects.infraHome.getNodesWithValues();
expect(nodesWithValue).to.eql([
{ name: 'demo-stack-apache-01', value: 1.4, color: '#6092c0' },
]);
await pageObjects.infraHome.clearSearchTerm();
});
it('change color palette', async () => {
await pageObjects.infraHome.openLegendControls();
await pageObjects.infraHome.changePalette('temperature');
await pageObjects.infraHome.applyLegendControls();
const nodesWithValue = await pageObjects.infraHome.getNodesWithValues();
expect(nodesWithValue).to.eql([
{ name: 'demo-stack-client-01', value: 0.6, color: '#6092c0' },
{ name: 'demo-stack-haproxy-01', value: 0.8, color: '#b5c9df' },
{ name: 'demo-stack-redis-01', value: 1, color: '#f1d9b9' },
{ name: 'demo-stack-nginx-01', value: 1.1, color: '#eec096' },
{ name: 'demo-stack-mysql-01', value: 1.2, color: '#eba47a' },
{ name: 'demo-stack-apache-01', value: 1.4, color: '#e7664c' },
]);
});
it('toggle the timeline', async () => {
await pageObjects.infraHome.goToTime(DATE_WITH_DATA);
await pageObjects.infraHome.getWaffleMap();
await pageObjects.infraHome.openTimeline();
await pageObjects.infraHome.closeTimeline();
});
});

View file

@ -16,6 +16,7 @@ export function InfraHomePageProvider({ getService, getPageObjects }: FtrProvide
const find = getService('find');
const browser = getService('browser');
const pageObjects = getPageObjects(['common']);
const comboBox = getService('comboBox');
return {
async goToTime(time: string) {
@ -65,6 +66,85 @@ export function InfraHomePageProvider({ getService, getPageObjects }: FtrProvide
expect(await values2[3].getVisibleText()).to.be('255.1kbit/s');
},
async getNodesWithValues() {
const nodes = await testSubjects.findAll('nodeContainer');
const promises = nodes.map(async (node) => {
const nodeName = await node.findByTestSubject('nodeName');
const name = await nodeName.getVisibleText();
const nodeValue = await node.findByTestSubject('nodeValue');
const value = await nodeValue.getVisibleText();
const color = await nodeValue.getAttribute('color');
return { name, value: parseFloat(value), color };
});
return await Promise.all(promises);
},
async sortNodesBy(sort: string) {
await testSubjects.click('waffleSortByDropdown');
if (sort === 'value') {
await testSubjects.find('waffleSortByValue');
await testSubjects.click('waffleSortByValue');
} else {
await testSubjects.find('waffleSortByName');
await testSubjects.click('waffleSortByName');
}
},
async groupByCustomField(field: string) {
await testSubjects.click('waffleGroupByDropdown');
const contextMenu = await testSubjects.find('groupByContextMenu');
const menuItems = await contextMenu.findAllByCssSelector('button.euiContextMenuItem');
await menuItems[0].click();
const groupByCustomField = await testSubjects.find('groupByCustomField');
await comboBox.setElement(groupByCustomField, field);
await testSubjects.click('groupByCustomFieldAddButton');
await this.waitForLoading();
const groupNameLinks = await testSubjects.findAll('groupNameLink');
return Promise.all(groupNameLinks.map(async (link) => link.getVisibleText()));
},
async enterSearchTerm(query: string) {
const input = await this.clearSearchTerm();
await input.type([query, browser.keys.RETURN]);
await this.waitForLoading();
},
async clearSearchTerm() {
const input = await testSubjects.find('infraSearchField');
await input.clearValueWithKeyboard({ charByChar: true });
return input;
},
async openLegendControls() {
await testSubjects.click('openLegendControlsButton');
await testSubjects.find('legendControls');
},
async changePalette(paletteId: string) {
await testSubjects.find('legendControlsPalette');
await testSubjects.selectValue('legendControlsPalette', paletteId);
},
async applyLegendControls() {
await testSubjects.click('applyLegendControlsButton');
},
async toggleReverseSort() {
await testSubjects.click('waffleSortByDropdown');
await testSubjects.find('waffleSortByDirection');
await testSubjects.click('waffleSortByDirection');
},
async openTimeline() {
await testSubjects.click('toggleTimelineButton');
await testSubjects.existOrFail('timelineContainerOpen');
},
async closeTimeline() {
await testSubjects.click('toggleTimelineButton');
await testSubjects.existOrFail('timelineContainerClosed');
},
async openInvenotrySwitcher() {
await testSubjects.click('openInventorySwitcher');
return await testSubjects.find('goToHost');