[Infrastructure UI] Hosts technical preview discovery (#149351)

Closes #148235

## Summary

This PR adds an option for the user to enable the hosts view and makes
the hosts navigation link always visible.

### Code Review
I know that this PR looks big (it actually is 😅 ) and not "fun" to
review but I will try to make it easier by splitting and pointing out
the different parts here:
- [Change in the
Navigation](https://github.com/elastic/kibana/pull/149351/files#diff-1f9338fabab6f332f6b4b11dbcec00f10b89097720c94b6087c53b186659c976):
Contains also a
[component](https://github.com/elastic/kibana/pull/149351/files#diff-9f8d59f3f98855c2cc6c4bd8ad93f69ad06181809a8769121fc1a3714c848f5e)
to show the icon next to the name
- [Hosts view navigation link enabled by
default](https://github.com/elastic/kibana/pull/149351/files#diff-f66e54f3274b7dd4c72b54011c1b4a1d2e4d91aa47341e14b048377437574655)
- [The Hosts View Landing
page](https://github.com/elastic/kibana/pull/149351/files#diff-c3aa95031634008b7384dd670ca12c8266bead5f2cd461fdc6070fe84ae9e4e4)
: Using the
[EnableHostViewPage](https://github.com/elastic/kibana/pull/149351/files#diff-a0704adf74ea9605858160dfbe6ee04021b8161a63a54852009847fb5caeb5e8)
component passing different `actions` based on the use case.
- [Link on the inventory
page](https://github.com/elastic/kibana/pull/149351/files#diff-181e8eeab11591c40565d6636c1b0fc6099bc555af71efe612d1da3f406d7101)
- [Functional
Tests](https://github.com/elastic/kibana/pull/149351/files#diff-cdf2ef63d7f0db051d569b09fbd2ba39eb852d3d75bf3b29d85e57de2da3af35)
### Flow
#### How to navigate to Hosts View
- The link from the inventory page which will navigate the user to a
separate page where he will have the option to enable hosts view (if the
user has permission to enable it otherwise he will see a message to
contact his administrator to do it for him). In order to enable the
hosts view the user should have permission to modify advanced settings.
- The navigation link will be always visible. After enabling the host
view the user will see the hosts view page
(links highlighted in green): <img width="2055" alt="image"
src="https://user-images.githubusercontent.com/14139027/214123141-e3d8a156-839f-4dbf-ae6f-b564eccb16f7.png">

#### After navigating to host view
- When hosts view is disabled

| Admin User  | User with read-only permission |
| ------------ | ------------------------------- |
|
![image](https://user-images.githubusercontent.com/14139027/214124509-3286fd62-7e17-409a-8407-19499485b704.png)
|
![image](https://user-images.githubusercontent.com/14139027/214123646-68540663-1525-4998-babd-172999a546a5.png)
|

- When hosts view is enabled (Both Admin User and User with read-only
permission ) <img width="1713" alt="image"
src="https://user-images.githubusercontent.com/14139027/214125673-8976047c-7653-4345-bfee-41413f080e05.png">

## Testing

- Remove the override (if any) in the`kibana.yml` for
`observability:enableInfrastructureHostsView`
- Use `elastic` user (or user with permission to modify advanced
settings) to enable the host view (navigating from the inventory page or
from the nav menu)
- to disable the host view you should set
`observability:enableInfrastructureHostsView` to `false` in Stack
Management > Advanced Settings: <img width="1724" alt="image"
src="https://user-images.githubusercontent.com/14139027/214127034-fc245d12-0e56-4aa3-9b35-bdfeb7760960.png">
- create a new role and set the `Management` permissions to `none` and
observability permissions to `read`: <img width="1726" alt="image"
src="https://user-images.githubusercontent.com/14139027/214127744-27e3014f-c181-4cd0-9b91-8868580a909b.png">
- Assign this role to a user 
- Open hosts view (for both cases
`observability:enableInfrastructureHostsView` set to `false`/`true`)

 Testing telemetry

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
jennypavlova 2023-01-26 17:30:17 +01:00 committed by GitHub
parent 088a6bb5af
commit 0e3ae210d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 1420 additions and 39 deletions

View file

@ -0,0 +1,76 @@
/*
* 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 React, { ReactNode } from 'react';
import { EuiPageTemplate, EuiImage, EuiSpacer } from '@elastic/eui';
import { css } from '@emotion/react';
import { useEuiBackgroundColor } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useTrackPageview } from '@kbn/observability-plugin/public';
import { MetricsPageTemplate } from '../../../page_template';
import hostsLandingBeta from './hosts_landing_beta.svg';
import { ExperimentalBadge } from '../experimental_badge';
interface Props {
actions?: ReactNode;
}
export const EnableHostsViewPage = ({ actions }: Props) => {
const backgroundColor = useEuiBackgroundColor('subdued');
useTrackPageview({ app: 'infra_metrics', path: 'hosts_feature_enable_landing_page' });
useTrackPageview({
app: 'infra_metrics',
path: 'hosts_feature_enable_landing_page',
delay: 15000,
});
return (
<MetricsPageTemplate isEmptyState>
<EuiPageTemplate.EmptyPrompt
data-test-subj="hostsLandingPage"
title={
<h2>
{i18n.translate('xpack.infra.hostsViewPage.landing.introTitle', {
defaultMessage: 'Introducing: Host Analysis',
})}
</h2>
}
alignment="center"
icon={<EuiImage size="fullWidth" src={hostsLandingBeta} alt="" />}
color="plain"
layout="horizontal"
body={
<>
<ExperimentalBadge />
<EuiSpacer />
<p>
{i18n.translate('xpack.infra.hostsViewPage.landing.introMessage', {
defaultMessage: `Introducing our new 'Hosts' feature, now available in technical preview!
With this powerful tool, you can easily view and analyse your hosts and identify any
issues so you address them quickly. Get a detailed view of metrics for your hosts, see
which ones are triggering the most alerts and filter the hosts you want to analyse
using any KQL filter and easy breakdowns such as cloud provider and operating system.`,
})}
</p>
<p>
{i18n.translate('xpack.infra.hostsViewPage.landing.tryTheFeatureMessage', {
defaultMessage: `This is an early version of the feature and we would love your feedback as we continue
to develop and improve it. To access the feature, simply enable below. Don't miss
out on this powerful new addition to our platform - try it out today!`,
})}
</p>
</>
}
css={css`
background-color: ${backgroundColor};
`}
actions={actions}
/>
</MetricsPageTemplate>
);
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 695 KiB

View file

@ -6,16 +6,16 @@
*/
import { EuiBetaBadge } from '@elastic/eui';
import { css } from '@emotion/react';
import type { IconType, ToolTipPositions } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
export const ExperimentalBadge = () => (
interface Props {
iconType?: IconType;
tooltipPosition?: ToolTipPositions;
}
export const ExperimentalBadge = ({ iconType, tooltipPosition }: Props) => (
<EuiBetaBadge
css={css`
display: flex;
justify-content: center;
`}
label={i18n.translate('xpack.infra.hostsViewPage.experimentalBadgeLabel', {
defaultMessage: 'Technical preview',
})}
@ -23,5 +23,7 @@ export const ExperimentalBadge = () => (
defaultMessage:
'This functionality is in technical preview and may be changed or removed completely in a future release. Elastic will take a best effort approach to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.',
})}
iconType={iconType}
tooltipPosition={tooltipPosition}
/>
);

View file

@ -0,0 +1,92 @@
/*
* 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 React from 'react';
import { EuiButton, EuiCallOut } from '@elastic/eui';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { enableInfrastructureHostsView } from '@kbn/observability-plugin/public';
import useObservable from 'react-use/lib/useObservable';
import { Observable } from 'rxjs';
import { InfraClientStartDeps } from '../../../types';
import { EnableHostsViewPage } from './components/enable_hosts_view_page/enable_hosts_view_page';
import { HostsPage } from '.';
export const HostsLandingPage = () => {
const {
services: { uiSettings, application },
} = useKibana<InfraClientStartDeps>();
const canEditAdvancedSettings = application?.capabilities.advancedSettings.save;
const isHostViewEnabled = useObservable<boolean>(
uiSettings?.get$<boolean>(enableInfrastructureHostsView) ??
new Observable((subs) => subs.next(false))
);
if (isHostViewEnabled) {
return <HostsPage />;
}
if (canEditAdvancedSettings) {
return (
<EnableHostsViewPage
actions={
<EuiButton
iconType="check"
color="primary"
data-test-subj="hostsView-enable-feature-button"
onClick={() => {
uiSettings?.set(enableInfrastructureHostsView, true);
}}
>
{i18n.translate('xpack.infra.hostsViewPage.landing.enableHostsView', {
defaultMessage: 'Enable hosts view',
})}
</EuiButton>
}
/>
);
}
return (
<EnableHostsViewPage
actions={
<EuiCallOut data-test-subj="hostView-no-enable-access" size="s" color="warning">
<p>
{i18n.translate(
'xpack.infra.hostsViewPage.landing.calloutReachOutToYourKibanaAdministrator',
{
defaultMessage: `Your user role doesnt have sufficient privileges to enable this feature - please
reach out to your Kibana Administrator and ask them to visit this page to enable this feature.`,
}
)}
</p>
<p>
<FormattedMessage
id="xpack.infra.hostsViewPage.landing.calloutRoleClarificationWithDocsLink"
defaultMessage="They will need a role with access to Advanced settings in Kibana. {docsLink}"
values={{
docsLink: (
<EuiLink
target="_blank"
data-test-subj="hostsView-docs-link"
href="https://www.elastic.co/guide/en/kibana/current/kibana-privileges.html#kibana-feature-privileges"
>
{i18n.translate('xpack.infra.hostsViewPage.landing.learnMore', {
defaultMessage: 'Learn more',
})}
</EuiLink>
),
}}
/>
</p>
</EuiCallOut>
}
/>
);
};

View file

@ -29,7 +29,7 @@ import { MetricsExplorerPage } from './metrics_explorer';
import { SnapshotPage } from './inventory_view';
import { MetricDetail } from './metric_detail';
import { MetricsSettingsPage } from './settings';
import { HostsPage } from './hosts';
import { HostsLandingPage } from './hosts/hosts_landing_page';
import { SourceLoadingPage } from '../../components/source_loading_page';
import { WaffleOptionsProvider } from './inventory_view/hooks/use_waffle_options';
import { WaffleTimeProvider } from './inventory_view/hooks/use_waffle_time';
@ -120,7 +120,7 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => {
)}
/>
<Route path="/detail/:type/:node" component={MetricDetail} />
<Route path={'/hosts'} component={HostsPage} />
<Route path={'/hosts'} component={HostsLandingPage} />
<Route path={'/settings'} component={MetricsSettingsPage} />
</Switch>
</InfraMLCapabilitiesProvider>

View file

@ -0,0 +1,53 @@
/*
* 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 React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiBetaBadge, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useLinkProps } from '@kbn/observability-plugin/public';
import { css } from '@emotion/react';
import { ExperimentalBadge } from '../../hosts/components/experimental_badge';
export const HostViewIntroPanel = () => {
const link = useLinkProps({
app: 'metrics',
pathname: '/hosts',
});
return (
<EuiFlexGroup
responsive={false}
alignItems="center"
gutterSize="m"
css={css`
position: relative;
z-index: 100;
`}
>
<EuiFlexItem grow={false}>
<EuiBetaBadge
color={'accent'}
href={link.href ?? ''}
data-test-subj="inventory-hostsView-badge"
label={i18n.translate('xpack.infra.layout.tryIt', {
defaultMessage: 'Try it',
})}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<ExperimentalBadge iconType="beaker" tooltipPosition="top" />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink data-test-subj="inventory-hostsView-link" {...link}>
{i18n.translate('xpack.infra.layout.hostsLandingPageLink', {
defaultMessage: 'Introducing a new Hosts analysis experience',
})}
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -31,6 +31,7 @@ import { createLegend } from '../lib/create_legend';
import { useWaffleViewState } from '../hooks/use_waffle_view_state';
import { BottomDrawer } from './bottom_drawer';
import { LegendControls } from './waffle/legend_controls';
import { HostViewIntroPanel } from './hosts_view_intro_panel';
interface Props {
shouldLoadDefault: boolean;
@ -178,6 +179,7 @@ export const Layout = React.memo(
</EuiFlexGroup>
</EuiFlexGroup>
</TopActionContainer>
<HostViewIntroPanel />
<AutoSizer bounds>
{({ measureRef, bounds: { height = 0 } }) => (
<>

View file

@ -92,8 +92,8 @@ export class Plugin implements InfraClientPluginClass {
const infraEntries = [
{ label: 'Inventory', app: 'metrics', path: '/inventory' },
{ label: 'Metrics Explorer', app: 'metrics', path: '/explorer' },
{ label: 'Hosts', isBeta: true, app: 'metrics', path: '/hosts' },
];
const hostInfraEntry = { label: 'Hosts', app: 'metrics', path: '/hosts' };
pluginsSetup.observability.navigation.registerSections(
startDep$AndHostViewFlag$.pipe(
map(
@ -103,7 +103,6 @@ export class Plugin implements InfraClientPluginClass {
application: { capabilities },
},
],
isInfrastructureHostsViewEnabled,
]) => [
...(capabilities.logs.show
? [
@ -123,9 +122,7 @@ export class Plugin implements InfraClientPluginClass {
{
label: 'Infrastructure',
sortKey: 300,
entries: isInfrastructureHostsViewEnabled
? [hostInfraEntry, ...infraEntries]
: infraEntries,
entries: infraEntries,
},
]
: []),
@ -197,6 +194,13 @@ export class Plugin implements InfraClientPluginClass {
}),
path: '/inventory',
},
{
id: 'metrics-hosts',
title: i18n.translate('xpack.infra.homePage.metricsHostsTabTitle', {
defaultMessage: 'Hosts',
}),
path: '/hosts',
},
{
id: 'metrics-explorer',
title: i18n.translate('xpack.infra.homePage.metricsExplorerTabTitle', {
@ -212,13 +216,6 @@ export class Plugin implements InfraClientPluginClass {
path: '/settings',
},
];
const hostInfraDeepLink = {
id: 'metrics-hosts',
title: i18n.translate('xpack.infra.homePage.metricsHostsTabTitle', {
defaultMessage: 'Hosts',
}),
path: '/hosts',
};
core.application.register({
id: 'metrics',
title: i18n.translate('xpack.infra.metrics.pluginTitle', {
@ -229,9 +226,7 @@ export class Plugin implements InfraClientPluginClass {
appRoute: '/app/metrics',
category: DEFAULT_APP_CATEGORIES.observability,
updater$: this.appUpdater$,
deepLinks: core.uiSettings.get<boolean>(enableInfrastructureHostsView)
? [hostInfraDeepLink, ...infraDeepLinks]
: infraDeepLinks,
deepLinks: infraDeepLinks,
mount: async (params: AppMountParameters) => {
// mount callback should not use setup dependencies, get start dependencies instead
const [coreStart, pluginsStart, pluginStart] = await core.getStartServices();
@ -242,14 +237,9 @@ export class Plugin implements InfraClientPluginClass {
});
startDep$AndHostViewFlag$.subscribe(
([_startServices, isInfrastructureHostsViewEnabled]: [
[CoreStart, InfraClientStartDeps, InfraClientStartExports],
boolean
]) => {
([_startServices]: [[CoreStart, InfraClientStartDeps, InfraClientStartExports], boolean]) => {
this.appUpdater$.next(() => ({
deepLinks: isInfrastructureHostsViewEnabled
? [hostInfraDeepLink, ...infraDeepLinks]
: infraDeepLinks,
deepLinks: infraDeepLinks,
}));
}
);

View file

@ -46,6 +46,8 @@ export interface NavigationEntry {
ignoreTrailingSlash?: boolean;
// shows NEW badge besides the navigation label, which will automatically disappear when menu item is clicked.
isNewFeature?: boolean;
// shows beta badge lab icon if the feature is still beta besides the navigation label
isBeta?: boolean;
}
```

View file

@ -0,0 +1,48 @@
/*
* 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 { EuiBetaBadge } from '@elastic/eui';
import type { IconType } from '@elastic/eui/src/components/icon/icon';
import { i18n } from '@kbn/i18n';
import React from 'react';
import styled from 'styled-components';
interface Props {
iconType: IconType;
label: string;
}
const LabelContainer = styled.span`
max-width: 72%;
float: left;
&:hover,
&:focus-within,
&:focus {
text-decoration: underline;
}
`;
const StyledBetaBadge = styled(EuiBetaBadge)`
margin-left: 5px;
margin-bottom: -6px;
`;
export function NavNameWithBetaBadge({ label, iconType }: Props) {
return (
<>
<LabelContainer className="eui-textTruncate">
<span>{label}</span>
</LabelContainer>
<StyledBetaBadge
label={i18n.translate('xpack.observability.navigation.experimentalBadgeLabel', {
defaultMessage: 'Technical preview',
})}
size="s"
iconType={iconType}
/>
</>
);
}

View file

@ -27,6 +27,7 @@ import { ObservabilityAppServices } from '../../../application/types';
import type { NavigationSection } from '../../../services/navigation_registry';
import { ObservabilityTour } from '../tour';
import { NavNameWithBadge, hideBadge } from './nav_name_with_badge';
import { NavNameWithBetaBadge } from './nav_name_with_beta_badge';
export type WrappedPageTemplateProps = Pick<
KibanaPageTemplateProps,
@ -103,6 +104,8 @@ export function ObservabilityPageTemplate({
id: `${sectionIndex}.${entryIndex}`,
name: entry.isNewFeature ? (
<NavNameWithBadge label={entry.label} localStorageId={badgeLocalStorageId} />
) : entry.isBeta ? (
<NavNameWithBetaBadge label={entry.label} iconType="beaker" />
) : (
entry.label
),

View file

@ -32,6 +32,8 @@ export interface NavigationEntry {
onClick?: (event: React.MouseEvent<HTMLElement | HTMLButtonElement, MouseEvent>) => void;
// shows NEW badge besides the navigation label, which will automatically disappear when menu item is clicked.
isNewFeature?: boolean;
// shows beta badge lab icon if the feature is still beta besides the navigation label
isBeta?: boolean;
// override default path matching logic to determine if nav entry is selected
matchPath?: (path: string) => boolean;
}

View file

@ -15,10 +15,88 @@ const END_DATE = moment.utc(DATES.metricsAndLogs.hosts.max);
const timepickerFormat = 'MMM D, YYYY @ HH:mm:ss.SSS';
export default ({ getPageObjects, getService }: FtrProviderContext) => {
const kibanaServer = getService('kibanaServer');
const esArchiver = getService('esArchiver');
const browser = getService('browser');
const pageObjects = getPageObjects(['common', 'infraHome', 'timePicker', 'infraHostsView']);
const kibanaServer = getService('kibanaServer');
const security = getService('security');
const pageObjects = getPageObjects([
'common',
'infraHome',
'timePicker',
'infraHostsView',
'security',
'settings',
]);
// Helpers
const loginWithReadOnlyUserAndNavigateToInfra = async () => {
await security.role.create('global_hosts_read_privileges_role', {
elasticsearch: {
indices: [{ names: ['metricbeat-*'], privileges: ['read', 'view_index_metadata'] }],
},
kibana: [
{
feature: {
infrastructure: ['read'],
advancedSettings: ['read'],
},
spaces: ['*'],
},
],
});
await security.user.create('global_hosts_read_privileges_user', {
password: 'global_hosts_read_privileges_user-password',
roles: ['global_hosts_read_privileges_role'],
full_name: 'test user',
});
await pageObjects.security.forceLogout();
await pageObjects.security.login(
'global_hosts_read_privileges_user',
'global_hosts_read_privileges_user-password',
{
expectSpaceSelector: false,
}
);
await pageObjects.common.navigateToApp('infraOps');
};
const logoutAndDeleteReadOnlyUser = async () => {
await pageObjects.security.forceLogout();
await Promise.all([
security.role.delete('global_hosts_read_privileges_role'),
security.user.delete('global_hosts_read_privileges_user'),
]);
};
const navigateAndDisableHostView = async () => {
await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs');
await pageObjects.common.navigateToApp('infraOps');
await pageObjects.common.navigateToUrl('management', 'kibana/settings', {
basePath: `/s/default`,
ensureCurrentUrl: false,
shouldLoginIfPrompted: false,
shouldUseHashForSubUrl: false,
});
await pageObjects.settings.toggleAdvancedSettingCheckbox(
'observability:enableInfrastructureHostsView',
false
);
return esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs');
};
const navigateAndEnableHostView = async () => {
await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs');
await pageObjects.common.navigateToApp('infraOps');
await pageObjects.infraHostsView.clickTryHostViewLink();
await pageObjects.infraHostsView.clickEnableHostViewButton();
};
// Tests
describe('Hosts view', function () {
this.tags('includeFirefox');
@ -26,21 +104,106 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await kibanaServer.savedObjects.cleanStandardList();
});
describe('Basic functionality', () => {
describe('shows hosts view landing page for admin', () => {
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs');
await pageObjects.common.navigateToApp('infraOps');
await pageObjects.infraHome.goToHostsView();
await pageObjects.infraHostsView.clickTryHostViewBadge();
});
after(async () => {
return esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs');
});
it('should show hosts landing page with enable button when the hosts view is disabled', async () => {
const landingPageEnableButton =
await pageObjects.infraHostsView.getHostsLandingPageEnableButton();
const landingPageEnableButtonText = await landingPageEnableButton.getVisibleText();
expect(landingPageEnableButtonText).to.eql('Enable hosts view');
});
});
describe('should show hosts view landing page for user with read permission', () => {
before(async () => {
await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs');
await loginWithReadOnlyUserAndNavigateToInfra();
await pageObjects.infraHostsView.clickTryHostViewBadge();
});
after(async () => {
// NOTE: Logout needs to happen before anything else to avoid flaky behavior
await logoutAndDeleteReadOnlyUser();
return esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs');
});
it('should show hosts landing page with callout when the hosts view is disabled', async () => {
const landingPageDisabled = await pageObjects.infraHostsView.getHostsLandingPageDisabled();
const learnMoreDocsUrl = await pageObjects.infraHostsView.getHostsLandingPageDocsLink();
const parsedUrl = new URL(learnMoreDocsUrl);
expect(parsedUrl.host).to.be('www.elastic.co');
expect(parsedUrl.pathname).to.be('/guide/en/kibana/current/kibana-privileges.html');
expect(landingPageDisabled).to.contain(
'Your user role doesnt have sufficient privileges to enable this feature'
);
});
});
describe('enables hosts view page and checks content', () => {
before(async () => {
await navigateAndEnableHostView();
await pageObjects.timePicker.setAbsoluteRange(
START_DATE.format(timepickerFormat),
END_DATE.format(timepickerFormat)
);
});
after(() => esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'));
after(async () => {
await navigateAndDisableHostView();
});
it('should render the correct page title', async () => {
const documentTitle = await browser.getTitle();
expect(documentTitle).to.contain('Hosts - Infrastructure - Observability - Elastic');
describe('should show hosts page for admin user and see the page content', async () => {
it('should render the correct page title', async () => {
const documentTitle = await browser.getTitle();
expect(documentTitle).to.contain('Hosts - Infrastructure - Observability - Elastic');
});
it('should have six hosts', async () => {
const hosts = await pageObjects.infraHostsView.getHostsTableData();
expect(hosts.length).to.equal(6);
});
it('should load 5 metrics trend tiles', async () => {
const hosts = await pageObjects.infraHostsView.getAllMetricsTrendTiles();
expect(hosts.length).to.equal(5);
});
[
{ metric: 'hosts', value: '6' },
{ metric: 'cpu', value: '0.8%' },
{ metric: 'memory', value: '16.8%' },
{ metric: 'tx', value: '0 bit/s' },
{ metric: 'rx', value: '0 bit/s' },
].forEach(({ metric, value }) => {
it(`${metric} tile should show ${value}`, async () => {
const tileValue = await pageObjects.infraHostsView.getMetricsTrendTileValue(metric);
expect(tileValue).to.eql(value);
});
});
});
});
describe('should show hosts page for read only user and see the page content', async () => {
before(async () => {
await navigateAndEnableHostView();
await loginWithReadOnlyUserAndNavigateToInfra();
await pageObjects.infraHostsView.clickTryHostViewLink();
await pageObjects.timePicker.setAbsoluteRange(
START_DATE.format(timepickerFormat),
END_DATE.format(timepickerFormat)
);
});
after(async () => {
// NOTE: Logout needs to happen before anything else to avoid flaky behavior
await logoutAndDeleteReadOnlyUser();
await navigateAndDisableHostView();
});
it('should have six hosts', async () => {

View file

@ -55,7 +55,6 @@ export default async function ({ readConfigFile }) {
'--xpack.discoverEnhanced.actions.exploreDataInContextMenu.enabled=true',
'--savedObjects.maxImportPayloadBytes=10485760', // for OSS test management/_import_objects,
'--uiSettings.overrides.observability:enableNewSyntheticsView=true', // for OSS test management/_import_objects,
'--uiSettings.overrides.observability:enableInfrastructureHostsView=true', // for Infra/Hosts View test,
],
},
uiSettings: {

View file

@ -11,6 +11,35 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
return {
async clickTryHostViewLink() {
return await testSubjects.click('inventory-hostsView-link');
},
async clickTryHostViewBadge() {
return await testSubjects.click('inventory-hostsView-badge');
},
async getHostsLandingPageDisabled() {
const container = await testSubjects.find('hostView-no-enable-access');
const containerText = await container.getVisibleText();
return containerText;
},
async getHostsLandingPageDocsLink() {
const container = await testSubjects.find('hostsView-docs-link');
const containerText = await container.getAttribute('href');
return containerText;
},
async getHostsLandingPageEnableButton() {
const container = await testSubjects.find('hostsView-enable-feature-button');
return container;
},
async clickEnableHostViewButton() {
return await testSubjects.click('hostsView-enable-feature-button');
},
async getHostsTableData() {
const table = await testSubjects.find('hostsView-table');
return table.findAllByTestSubject('hostsView-tableRow');