mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* [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:
parent
706446ff09
commit
7667ca42a6
11 changed files with 190 additions and 10 deletions
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 && (
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue