[ENDPOINT] Hide the Timeline Flyout while on the Management Pages (#69998)

* hide timeline on Management pages
* adjust managment page view styles
* Added additional tests for validating no timeline button on management views
* centralize API Path responses and reuse across some tests
* Fix state being reset incorrectly
This commit is contained in:
Paul Tavares 2020-06-26 14:59:13 -04:00 committed by GitHub
parent 6808903d57
commit 0bdff15297
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 175 additions and 183 deletions

View file

@ -2,11 +2,11 @@
exports[`PageView component should display body header custom element 1`] = `
.c0.endpoint--isListView {
padding: 0 70px 0 24px;
padding: 0 24px;
}
.c0.endpoint--isListView .endpoint-header {
padding: 24px 0;
padding: 24px;
margin-bottom: 0;
}
@ -22,7 +22,7 @@ exports[`PageView component should display body header custom element 1`] = `
}
.c0 .endpoint-navTabs {
margin-left: 24px;
margin-left: 12px;
}
<PageView
@ -97,11 +97,11 @@ exports[`PageView component should display body header custom element 1`] = `
exports[`PageView component should display body header wrapped in EuiTitle 1`] = `
.c0.endpoint--isListView {
padding: 0 70px 0 24px;
padding: 0 24px;
}
.c0.endpoint--isListView .endpoint-header {
padding: 24px 0;
padding: 24px;
margin-bottom: 0;
}
@ -117,7 +117,7 @@ exports[`PageView component should display body header wrapped in EuiTitle 1`] =
}
.c0 .endpoint-navTabs {
margin-left: 24px;
margin-left: 12px;
}
<PageView
@ -195,11 +195,11 @@ exports[`PageView component should display body header wrapped in EuiTitle 1`] =
exports[`PageView component should display header left and right 1`] = `
.c0.endpoint--isListView {
padding: 0 70px 0 24px;
padding: 0 24px;
}
.c0.endpoint--isListView .endpoint-header {
padding: 24px 0;
padding: 24px;
margin-bottom: 0;
}
@ -215,7 +215,7 @@ exports[`PageView component should display header left and right 1`] = `
}
.c0 .endpoint-navTabs {
margin-left: 24px;
margin-left: 12px;
}
<PageView
@ -308,11 +308,11 @@ exports[`PageView component should display header left and right 1`] = `
exports[`PageView component should display only body if not header props used 1`] = `
.c0.endpoint--isListView {
padding: 0 70px 0 24px;
padding: 0 24px;
}
.c0.endpoint--isListView .endpoint-header {
padding: 24px 0;
padding: 24px;
margin-bottom: 0;
}
@ -328,7 +328,7 @@ exports[`PageView component should display only body if not header props used 1`
}
.c0 .endpoint-navTabs {
margin-left: 24px;
margin-left: 12px;
}
<PageView
@ -380,11 +380,11 @@ exports[`PageView component should display only body if not header props used 1`
exports[`PageView component should display only header left 1`] = `
.c0.endpoint--isListView {
padding: 0 70px 0 24px;
padding: 0 24px;
}
.c0.endpoint--isListView .endpoint-header {
padding: 24px 0;
padding: 24px;
margin-bottom: 0;
}
@ -400,7 +400,7 @@ exports[`PageView component should display only header left 1`] = `
}
.c0 .endpoint-navTabs {
margin-left: 24px;
margin-left: 12px;
}
<PageView
@ -482,11 +482,11 @@ exports[`PageView component should display only header left 1`] = `
exports[`PageView component should display only header right but include an empty left side 1`] = `
.c0.endpoint--isListView {
padding: 0 70px 0 24px;
padding: 0 24px;
}
.c0.endpoint--isListView .endpoint-header {
padding: 24px 0;
padding: 24px;
margin-bottom: 0;
}
@ -502,7 +502,7 @@ exports[`PageView component should display only header right but include an empt
}
.c0 .endpoint-navTabs {
margin-left: 24px;
margin-left: 12px;
}
<PageView
@ -581,11 +581,11 @@ exports[`PageView component should display only header right but include an empt
exports[`PageView component should pass through EuiPage props 1`] = `
.c0.endpoint--isListView {
padding: 0 70px 0 24px;
padding: 0 24px;
}
.c0.endpoint--isListView .endpoint-header {
padding: 24px 0;
padding: 24px;
margin-bottom: 0;
}
@ -601,7 +601,7 @@ exports[`PageView component should pass through EuiPage props 1`] = `
}
.c0 .endpoint-navTabs {
margin-left: 24px;
margin-left: 12px;
}
<PageView
@ -670,11 +670,11 @@ exports[`PageView component should pass through EuiPage props 1`] = `
exports[`PageView component should use custom element for header left and not wrap in EuiTitle 1`] = `
.c0.endpoint--isListView {
padding: 0 70px 0 24px;
padding: 0 24px;
}
.c0.endpoint--isListView .endpoint-header {
padding: 24px 0;
padding: 24px;
margin-bottom: 0;
}
@ -690,7 +690,7 @@ exports[`PageView component should use custom element for header left and not wr
}
.c0 .endpoint-navTabs {
margin-left: 24px;
margin-left: 12px;
}
<PageView

View file

@ -21,14 +21,13 @@ import {
import React, { memo, MouseEventHandler, ReactNode, useMemo } from 'react';
import styled from 'styled-components';
import { EuiTabProps } from '@elastic/eui/src/components/tabs/tab';
import { gutterTimeline } from '../../lib/helpers';
const StyledEuiPage = styled(EuiPage)`
&.endpoint--isListView {
padding: 0 ${gutterTimeline} 0 ${(props) => props.theme.eui.euiSizeL};
padding: 0 ${(props) => props.theme.eui.euiSizeL};
.endpoint-header {
padding: ${(props) => props.theme.eui.euiSizeL} 0;
padding: ${(props) => props.theme.eui.euiSizeL};
margin-bottom: 0;
}
.endpoint-page-content {
@ -44,7 +43,7 @@ const StyledEuiPage = styled(EuiPage)`
}
}
.endpoint-navTabs {
margin-left: ${(props) => props.theme.eui.euiSizeL};
margin-left: ${(props) => props.theme.eui.euiSizeM};
}
`;

View file

@ -7,7 +7,7 @@
import { useState, useEffect } from 'react';
import { useRouteSpy } from '../route/use_route_spy';
const hideTimelineForRoutes = [`/cases/configure`];
const hideTimelineForRoutes = [`/cases/configure`, '/management'];
export const useShowTimeline = () => {
const [{ pageName, pathName }] = useRouteSpy();

View file

@ -34,6 +34,12 @@ describe('when on the hosts page', () => {
render = () => mockedContext.render(<HostList />);
});
it('should NOT display timeline', async () => {
const renderResult = render();
const timelineFlyout = await renderResult.queryByTestId('flyoutOverlay');
expect(timelineFlyout).toBeNull();
});
it('should show a table', async () => {
const renderResult = render();
const table = await renderResult.findByTestId('hostListTable');

View file

@ -78,13 +78,20 @@ export const policyDetailsReducer: ImmutableReducer<PolicyDetailsState, AppActio
const isCurrentlyOnDetailsPage = isOnPolicyDetailsPage(newState);
const wasPreviouslyOnDetailsPage = isOnPolicyDetailsPage(state);
// Did user just enter the Detail page? if so, then set the loading indicator and return new state
if (isCurrentlyOnDetailsPage && !wasPreviouslyOnDetailsPage) {
return {
...newState,
isLoading: true,
};
if (isCurrentlyOnDetailsPage) {
// Did user just enter the Detail page? if so, then
// set the loading indicator and return new state
if (!wasPreviouslyOnDetailsPage) {
return {
...newState,
isLoading: true,
};
}
// Else, user was already on the details page,
// just return the updated state with new location data
return newState;
}
return {
...initialPolicyDetailsState(),
location: action.payload,

View file

@ -5,12 +5,14 @@
*/
import {
INGEST_API_EPM_PACKAGES,
sendGetDatasource,
sendGetEndpointSecurityPackage,
sendGetEndpointSpecificDatasources,
} from './ingest';
import { httpServiceMock } from '../../../../../../../../../../src/core/public/mocks';
import { DATASOURCE_SAVED_OBJECT_TYPE } from '../../../../../../../../ingest_manager/common';
import { apiPathMockResponseProviders } from '../test_mock_utils';
describe('ingest service', () => {
let http: ReturnType<typeof httpServiceMock.createStartContract>;
@ -59,76 +61,7 @@ describe('ingest service', () => {
describe('sendGetEndpointSecurityPackage()', () => {
it('should query EPM with category=security', async () => {
http.get.mockResolvedValue({
response: [
{
name: 'endpoint',
title: 'Elastic Endpoint',
version: '0.5.0',
description: 'This is the Elastic Endpoint package.',
type: 'solution',
download: '/epr/endpoint/endpoint-0.5.0.tar.gz',
path: '/package/endpoint/0.5.0',
icons: [
{
src: '/package/endpoint/0.5.0/img/logo-endpoint-64-color.svg',
size: '16x16',
type: 'image/svg+xml',
},
],
status: 'installed',
savedObject: {
type: 'epm-packages',
id: 'endpoint',
attributes: {
installed: [
{ id: '826759f0-7074-11ea-9bc8-6b38f4d29a16', type: 'dashboard' },
{ id: '1cfceda0-728b-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: '1e525190-7074-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: '55387750-729c-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: '92b1edc0-706a-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: 'a3a3bd10-706b-11ea-9bc8-6b38f4d29a16', type: 'map' },
{ id: 'logs-endpoint.alerts', type: 'index-template' },
{ id: 'events-endpoint', type: 'index-template' },
{ id: 'logs-endpoint.events.file', type: 'index-template' },
{ id: 'logs-endpoint.events.library', type: 'index-template' },
{ id: 'metrics-endpoint.metadata', type: 'index-template' },
{ id: 'metrics-endpoint.metadata_mirror', type: 'index-template' },
{ id: 'logs-endpoint.events.network', type: 'index-template' },
{ id: 'metrics-endpoint.policy', type: 'index-template' },
{ id: 'logs-endpoint.events.process', type: 'index-template' },
{ id: 'logs-endpoint.events.registry', type: 'index-template' },
{ id: 'logs-endpoint.events.security', type: 'index-template' },
{ id: 'metrics-endpoint.telemetry', type: 'index-template' },
],
es_index_patterns: {
alerts: 'logs-endpoint.alerts-*',
events: 'events-endpoint-*',
file: 'logs-endpoint.events.file-*',
library: 'logs-endpoint.events.library-*',
metadata: 'metrics-endpoint.metadata-*',
metadata_mirror: 'metrics-endpoint.metadata_mirror-*',
network: 'logs-endpoint.events.network-*',
policy: 'metrics-endpoint.policy-*',
process: 'logs-endpoint.events.process-*',
registry: 'logs-endpoint.events.registry-*',
security: 'logs-endpoint.events.security-*',
telemetry: 'metrics-endpoint.telemetry-*',
},
name: 'endpoint',
version: '0.5.0',
internal: false,
removable: false,
},
references: [],
updated_at: '2020-06-24T14:41:23.098Z',
version: 'Wzc0LDFd',
score: 0,
},
},
],
success: true,
});
http.get.mockReturnValue(apiPathMockResponseProviders[INGEST_API_EPM_PACKAGES]());
await sendGetEndpointSecurityPackage(http);
expect(http.get).toHaveBeenCalledWith('/api/ingest_manager/epm/packages', {
query: { category: 'security' },

View file

@ -16,6 +16,82 @@ import {
const generator = new EndpointDocGenerator('policy-list');
/**
* a list of API paths response mock providers
*/
export const apiPathMockResponseProviders = {
[INGEST_API_EPM_PACKAGES]: () =>
Promise.resolve<GetPackagesResponse>({
response: [
{
name: 'endpoint',
title: 'Elastic Endpoint',
version: '0.5.0',
description: 'This is the Elastic Endpoint package.',
type: 'solution',
download: '/epr/endpoint/endpoint-0.5.0.tar.gz',
path: '/package/endpoint/0.5.0',
icons: [
{
src: '/package/endpoint/0.5.0/img/logo-endpoint-64-color.svg',
size: '16x16',
type: 'image/svg+xml',
},
],
status: 'installed' as InstallationStatus,
savedObject: {
type: 'epm-packages',
id: 'endpoint',
attributes: {
installed: [
{ id: '826759f0-7074-11ea-9bc8-6b38f4d29a16', type: 'dashboard' },
{ id: '1cfceda0-728b-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: '1e525190-7074-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: '55387750-729c-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: '92b1edc0-706a-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: 'a3a3bd10-706b-11ea-9bc8-6b38f4d29a16', type: 'map' },
{ id: 'logs-endpoint.alerts', type: 'index-template' },
{ id: 'events-endpoint', type: 'index-template' },
{ id: 'logs-endpoint.events.file', type: 'index-template' },
{ id: 'logs-endpoint.events.library', type: 'index-template' },
{ id: 'metrics-endpoint.metadata', type: 'index-template' },
{ id: 'metrics-endpoint.metadata_mirror', type: 'index-template' },
{ id: 'logs-endpoint.events.network', type: 'index-template' },
{ id: 'metrics-endpoint.policy', type: 'index-template' },
{ id: 'logs-endpoint.events.process', type: 'index-template' },
{ id: 'logs-endpoint.events.registry', type: 'index-template' },
{ id: 'logs-endpoint.events.security', type: 'index-template' },
{ id: 'metrics-endpoint.telemetry', type: 'index-template' },
] as AssetReference[],
es_index_patterns: {
alerts: 'logs-endpoint.alerts-*',
events: 'events-endpoint-*',
file: 'logs-endpoint.events.file-*',
library: 'logs-endpoint.events.library-*',
metadata: 'metrics-endpoint.metadata-*',
metadata_mirror: 'metrics-endpoint.metadata_mirror-*',
network: 'logs-endpoint.events.network-*',
policy: 'metrics-endpoint.policy-*',
process: 'logs-endpoint.events.process-*',
registry: 'logs-endpoint.events.registry-*',
security: 'logs-endpoint.events.security-*',
telemetry: 'metrics-endpoint.telemetry-*',
},
name: 'endpoint',
version: '0.5.0',
internal: false,
removable: false,
},
references: [],
updated_at: '2020-06-24T14:41:23.098Z',
version: 'Wzc0LDFd',
},
},
],
success: true,
}),
};
/**
* It sets the mock implementation on the necessary http methods to support the policy list view
* @param mockedHttpService
@ -38,76 +114,8 @@ export const setPolicyListApiMockImplementation = (
});
}
if (path === INGEST_API_EPM_PACKAGES) {
return Promise.resolve<GetPackagesResponse>({
response: [
{
name: 'endpoint',
title: 'Elastic Endpoint',
version: '0.5.0',
description: 'This is the Elastic Endpoint package.',
type: 'solution',
download: '/epr/endpoint/endpoint-0.5.0.tar.gz',
path: '/package/endpoint/0.5.0',
icons: [
{
src: '/package/endpoint/0.5.0/img/logo-endpoint-64-color.svg',
size: '16x16',
type: 'image/svg+xml',
},
],
status: 'installed' as InstallationStatus,
savedObject: {
type: 'epm-packages',
id: 'endpoint',
attributes: {
installed: [
{ id: '826759f0-7074-11ea-9bc8-6b38f4d29a16', type: 'dashboard' },
{ id: '1cfceda0-728b-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: '1e525190-7074-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: '55387750-729c-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: '92b1edc0-706a-11ea-9bc8-6b38f4d29a16', type: 'visualization' },
{ id: 'a3a3bd10-706b-11ea-9bc8-6b38f4d29a16', type: 'map' },
{ id: 'logs-endpoint.alerts', type: 'index-template' },
{ id: 'events-endpoint', type: 'index-template' },
{ id: 'logs-endpoint.events.file', type: 'index-template' },
{ id: 'logs-endpoint.events.library', type: 'index-template' },
{ id: 'metrics-endpoint.metadata', type: 'index-template' },
{ id: 'metrics-endpoint.metadata_mirror', type: 'index-template' },
{ id: 'logs-endpoint.events.network', type: 'index-template' },
{ id: 'metrics-endpoint.policy', type: 'index-template' },
{ id: 'logs-endpoint.events.process', type: 'index-template' },
{ id: 'logs-endpoint.events.registry', type: 'index-template' },
{ id: 'logs-endpoint.events.security', type: 'index-template' },
{ id: 'metrics-endpoint.telemetry', type: 'index-template' },
] as AssetReference[],
es_index_patterns: {
alerts: 'logs-endpoint.alerts-*',
events: 'events-endpoint-*',
file: 'logs-endpoint.events.file-*',
library: 'logs-endpoint.events.library-*',
metadata: 'metrics-endpoint.metadata-*',
metadata_mirror: 'metrics-endpoint.metadata_mirror-*',
network: 'logs-endpoint.events.network-*',
policy: 'metrics-endpoint.policy-*',
process: 'logs-endpoint.events.process-*',
registry: 'logs-endpoint.events.registry-*',
security: 'logs-endpoint.events.security-*',
telemetry: 'metrics-endpoint.telemetry-*',
},
name: 'endpoint',
version: '0.5.0',
internal: false,
removable: false,
},
references: [],
updated_at: '2020-06-24T14:41:23.098Z',
version: 'Wzc0LDFd',
},
},
],
success: true,
});
if (apiPathMockResponseProviders[path]) {
return apiPathMockResponseProviders[path]();
}
}
return Promise.reject(new Error(`MOCK: unknown policy list api: ${path}`));

View file

@ -9,8 +9,9 @@ import { mount } from 'enzyme';
import { PolicyDetails } from './policy_details';
import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data';
import { createAppRootMockRenderer } from '../../../../common/mock/endpoint';
import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint';
import { getPolicyDetailPath, getPoliciesPath } from '../../../common/routing';
import { apiPathMockResponseProviders } from '../store/policy_list/test_mock_utils';
describe('Policy Details', () => {
type FindReactWrapperResponse = ReturnType<ReturnType<typeof render>['find']>;
@ -19,29 +20,50 @@ describe('Policy Details', () => {
const policyListPathUrl = getPoliciesPath();
const sleep = (ms = 100) => new Promise((wakeup) => setTimeout(wakeup, ms));
const generator = new EndpointDocGenerator();
const { history, AppWrapper, coreStart } = createAppRootMockRenderer();
const http = coreStart.http;
const render = (ui: Parameters<typeof mount>[0]) => mount(ui, { wrappingComponent: AppWrapper });
let history: AppContextTestRender['history'];
let coreStart: AppContextTestRender['coreStart'];
let middlewareSpy: AppContextTestRender['middlewareSpy'];
let http: typeof coreStart.http;
let render: (ui: Parameters<typeof mount>[0]) => ReturnType<typeof mount>;
let policyDatasource: ReturnType<typeof generator.generatePolicyDatasource>;
let policyView: ReturnType<typeof render>;
beforeEach(() => jest.clearAllMocks());
beforeEach(() => {
const appContextMockRenderer = createAppRootMockRenderer();
const AppWrapper = appContextMockRenderer.AppWrapper;
({ history, coreStart, middlewareSpy } = appContextMockRenderer);
render = (ui) => mount(ui, { wrappingComponent: AppWrapper });
http = coreStart.http;
});
afterEach(() => {
if (policyView) {
policyView.unmount();
}
jest.clearAllMocks();
});
describe('when displayed with invalid id', () => {
let releaseApiFailure: () => void;
beforeEach(() => {
http.get.mockReturnValue(Promise.reject(new Error('policy not found')));
http.get.mockImplementationOnce(async () => {
await new Promise((_, reject) => {
releaseApiFailure = reject.bind(null, new Error('policy not found'));
});
});
history.push(policyDetailsPathUrl);
policyView = render(<PolicyDetails />);
});
it('should show loader followed by error message', () => {
it('should NOT display timeline', async () => {
expect(policyView.find('flyoutOverlay')).toHaveLength(0);
});
it('should show loader followed by error message', async () => {
expect(policyView.find('EuiLoadingSpinner').length).toBe(1);
releaseApiFailure();
await middlewareSpy.waitForAction('serverFailedToReturnPolicyDetailsData');
policyView.update();
const callout = policyView.find('EuiCallOut');
expect(callout).toHaveLength(1);
@ -76,14 +98,25 @@ describe('Policy Details', () => {
success: true,
});
}
// Get package data
// Used in tests that route back to the list
if (apiPathMockResponseProviders[path]) {
asyncActions = asyncActions.then(async () => sleep());
return apiPathMockResponseProviders[path]();
}
}
return Promise.reject(new Error('unknown API call!'));
return Promise.reject(new Error(`unknown API call (not MOCKED): ${path}`));
});
history.push(policyDetailsPathUrl);
policyView = render(<PolicyDetails />);
});
it('should NOT display timeline', async () => {
expect(policyView.find('flyoutOverlay')).toHaveLength(0);
});
it('should display back to list button and policy title', () => {
policyView.update();
const pageHeaderLeft = policyView.find(

View file

@ -23,6 +23,12 @@ describe('when on the policies page', () => {
render = () => mockedContext.render(<PolicyList />);
});
it('should NOT display timeline', async () => {
const renderResult = render();
const timelineFlyout = await renderResult.queryByTestId('flyoutOverlay');
expect(timelineFlyout).toBeNull();
});
it('should show the empty state', async () => {
const renderResult = render();
const table = await renderResult.findByTestId('emptyPolicyTable');