mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Use platform history (#74328)
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: cauemarcondes <caue.marcondes@elastic.co>
This commit is contained in:
parent
833d76442f
commit
9b5912203f
67 changed files with 1215 additions and 771 deletions
74
x-pack/plugins/apm/public/application/application.test.tsx
Normal file
74
x-pack/plugins/apm/public/application/application.test.tsx
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { act } from '@testing-library/react';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AppMountParameters, CoreStart, HttpSetup } from 'src/core/public';
|
||||
import { mockApmPluginContextValue } from '../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { ApmPluginSetupDeps } from '../plugin';
|
||||
import { createCallApmApi } from '../services/rest/createCallApmApi';
|
||||
import { renderApp } from './';
|
||||
import { disableConsoleWarning } from '../utils/testHelpers';
|
||||
|
||||
describe('renderApp', () => {
|
||||
let mockConsole: jest.SpyInstance;
|
||||
|
||||
beforeAll(() => {
|
||||
// The RUM agent logs an unnecessary message here. There's a couple open
|
||||
// issues need to be fixed to get the ability to turn off all of the logging:
|
||||
//
|
||||
// * https://github.com/elastic/apm-agent-rum-js/issues/799
|
||||
// * https://github.com/elastic/apm-agent-rum-js/issues/861
|
||||
//
|
||||
// for now, override `console.warn` to filter those messages out.
|
||||
mockConsole = disableConsoleWarning('[Elastic APM]');
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
mockConsole.mockRestore();
|
||||
});
|
||||
|
||||
it('renders the app', () => {
|
||||
const { core, config } = mockApmPluginContextValue;
|
||||
const plugins = {
|
||||
licensing: { license$: new Observable() },
|
||||
triggers_actions_ui: { actionTypeRegistry: {}, alertTypeRegistry: {} },
|
||||
usageCollection: { reportUiStats: () => {} },
|
||||
};
|
||||
const params = {
|
||||
element: document.createElement('div'),
|
||||
history: createMemoryHistory(),
|
||||
};
|
||||
jest.spyOn(window, 'scrollTo').mockReturnValueOnce(undefined);
|
||||
createCallApmApi((core.http as unknown) as HttpSetup);
|
||||
|
||||
jest
|
||||
.spyOn(window.console, 'warn')
|
||||
.mockImplementationOnce((message: string) => {
|
||||
if (message.startsWith('[Elastic APM')) {
|
||||
return;
|
||||
} else {
|
||||
console.warn(message); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
|
||||
let unmount: () => void;
|
||||
|
||||
act(() => {
|
||||
unmount = renderApp(
|
||||
(core as unknown) as CoreStart,
|
||||
(plugins as unknown) as ApmPluginSetupDeps,
|
||||
(params as unknown) as AppMountParameters,
|
||||
config
|
||||
);
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
unmount();
|
||||
}).not.toThrowError();
|
||||
});
|
||||
});
|
|
@ -16,11 +16,11 @@ import { ApmPluginSetupDeps } from '../plugin';
|
|||
import {
|
||||
KibanaContextProvider,
|
||||
useUiSetting$,
|
||||
RedirectAppLinks,
|
||||
} from '../../../../../src/plugins/kibana_react/public';
|
||||
import { px, units } from '../style/variables';
|
||||
import { UpdateBreadcrumbs } from '../components/app/Main/UpdateBreadcrumbs';
|
||||
import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange';
|
||||
import { history, resetHistory } from '../utils/history';
|
||||
import 'react-vis/dist/style.css';
|
||||
import { RumHome } from '../components/app/RumDashboard/RumHome';
|
||||
import { ConfigSchema } from '../index';
|
||||
|
@ -70,12 +70,12 @@ function CsmApp() {
|
|||
export function CsmAppRoot({
|
||||
core,
|
||||
deps,
|
||||
routerHistory,
|
||||
history,
|
||||
config,
|
||||
}: {
|
||||
core: CoreStart;
|
||||
deps: ApmPluginSetupDeps;
|
||||
routerHistory: typeof history;
|
||||
history: AppMountParameters['history'];
|
||||
config: ConfigSchema;
|
||||
}) {
|
||||
const i18nCore = core.i18n;
|
||||
|
@ -86,19 +86,21 @@ export function CsmAppRoot({
|
|||
plugins,
|
||||
};
|
||||
return (
|
||||
<ApmPluginContext.Provider value={apmPluginContextValue}>
|
||||
<KibanaContextProvider services={{ ...core, ...plugins }}>
|
||||
<i18nCore.Context>
|
||||
<Router history={routerHistory}>
|
||||
<UrlParamsProvider>
|
||||
<LoadingIndicatorProvider>
|
||||
<CsmApp />
|
||||
</LoadingIndicatorProvider>
|
||||
</UrlParamsProvider>
|
||||
</Router>
|
||||
</i18nCore.Context>
|
||||
</KibanaContextProvider>
|
||||
</ApmPluginContext.Provider>
|
||||
<RedirectAppLinks application={core.application}>
|
||||
<ApmPluginContext.Provider value={apmPluginContextValue}>
|
||||
<KibanaContextProvider services={{ ...core, ...plugins }}>
|
||||
<i18nCore.Context>
|
||||
<Router history={history}>
|
||||
<UrlParamsProvider>
|
||||
<LoadingIndicatorProvider>
|
||||
<CsmApp />
|
||||
</LoadingIndicatorProvider>
|
||||
</UrlParamsProvider>
|
||||
</Router>
|
||||
</i18nCore.Context>
|
||||
</KibanaContextProvider>
|
||||
</ApmPluginContext.Provider>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -109,19 +111,13 @@ export function CsmAppRoot({
|
|||
export const renderApp = (
|
||||
core: CoreStart,
|
||||
deps: ApmPluginSetupDeps,
|
||||
{ element }: AppMountParameters,
|
||||
{ element, history }: AppMountParameters,
|
||||
config: ConfigSchema
|
||||
) => {
|
||||
createCallApmApi(core.http);
|
||||
|
||||
resetHistory();
|
||||
ReactDOM.render(
|
||||
<CsmAppRoot
|
||||
core={core}
|
||||
deps={deps}
|
||||
routerHistory={history}
|
||||
config={config}
|
||||
/>,
|
||||
<CsmAppRoot core={core} deps={deps} history={history} config={config} />,
|
||||
element
|
||||
);
|
||||
return () => {
|
||||
|
|
|
@ -5,36 +5,36 @@
|
|||
*/
|
||||
|
||||
import { ApmRoute } from '@elastic/apm-rum-react';
|
||||
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
|
||||
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Route, Router, Switch } from 'react-router-dom';
|
||||
import styled, { ThemeProvider, DefaultTheme } from 'styled-components';
|
||||
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
|
||||
import euiLightVars from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import { CoreStart, AppMountParameters } from '../../../../../src/core/public';
|
||||
import { ApmPluginSetupDeps } from '../plugin';
|
||||
import 'react-vis/dist/style.css';
|
||||
import styled, { DefaultTheme, ThemeProvider } from 'styled-components';
|
||||
import { ConfigSchema } from '../';
|
||||
import { AppMountParameters, CoreStart } from '../../../../../src/core/public';
|
||||
import {
|
||||
KibanaContextProvider,
|
||||
RedirectAppLinks,
|
||||
useUiSetting$,
|
||||
} from '../../../../../src/plugins/kibana_react/public';
|
||||
import { AlertsContextProvider } from '../../../triggers_actions_ui/public';
|
||||
import { routes } from '../components/app/Main/route_config';
|
||||
import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange';
|
||||
import { UpdateBreadcrumbs } from '../components/app/Main/UpdateBreadcrumbs';
|
||||
import { ApmPluginContext } from '../context/ApmPluginContext';
|
||||
import { LicenseProvider } from '../context/LicenseContext';
|
||||
import { LoadingIndicatorProvider } from '../context/LoadingIndicatorContext';
|
||||
import { LocationProvider } from '../context/LocationContext';
|
||||
import { MatchedRouteProvider } from '../context/MatchedRouteContext';
|
||||
import { UrlParamsProvider } from '../context/UrlParamsContext';
|
||||
import { AlertsContextProvider } from '../../../triggers_actions_ui/public';
|
||||
import { createStaticIndexPattern } from '../services/rest/index_pattern';
|
||||
import {
|
||||
KibanaContextProvider,
|
||||
useUiSetting$,
|
||||
} from '../../../../../src/plugins/kibana_react/public';
|
||||
import { px, units } from '../style/variables';
|
||||
import { UpdateBreadcrumbs } from '../components/app/Main/UpdateBreadcrumbs';
|
||||
import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange';
|
||||
import { routes } from '../components/app/Main/route_config';
|
||||
import { history, resetHistory } from '../utils/history';
|
||||
import { setHelpExtension } from '../setHelpExtension';
|
||||
import { setReadonlyBadge } from '../updateBadge';
|
||||
import { ApmPluginSetupDeps } from '../plugin';
|
||||
import { createCallApmApi } from '../services/rest/createCallApmApi';
|
||||
import { ConfigSchema } from '..';
|
||||
import 'react-vis/dist/style.css';
|
||||
import { createStaticIndexPattern } from '../services/rest/index_pattern';
|
||||
import { setHelpExtension } from '../setHelpExtension';
|
||||
import { px, units } from '../style/variables';
|
||||
import { setReadonlyBadge } from '../updateBadge';
|
||||
|
||||
const MainContainer = styled.div`
|
||||
padding: ${px(units.plus)};
|
||||
|
@ -68,12 +68,12 @@ function App() {
|
|||
export function ApmAppRoot({
|
||||
core,
|
||||
deps,
|
||||
routerHistory,
|
||||
history,
|
||||
config,
|
||||
}: {
|
||||
core: CoreStart;
|
||||
deps: ApmPluginSetupDeps;
|
||||
routerHistory: typeof history;
|
||||
history: AppMountParameters['history'];
|
||||
config: ConfigSchema;
|
||||
}) {
|
||||
const i18nCore = core.i18n;
|
||||
|
@ -84,36 +84,38 @@ export function ApmAppRoot({
|
|||
plugins,
|
||||
};
|
||||
return (
|
||||
<ApmPluginContext.Provider value={apmPluginContextValue}>
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
http: core.http,
|
||||
docLinks: core.docLinks,
|
||||
capabilities: core.application.capabilities,
|
||||
toastNotifications: core.notifications.toasts,
|
||||
actionTypeRegistry: plugins.triggers_actions_ui.actionTypeRegistry,
|
||||
alertTypeRegistry: plugins.triggers_actions_ui.alertTypeRegistry,
|
||||
}}
|
||||
>
|
||||
<KibanaContextProvider services={{ ...core, ...plugins }}>
|
||||
<i18nCore.Context>
|
||||
<Router history={routerHistory}>
|
||||
<LocationProvider>
|
||||
<MatchedRouteProvider routes={routes}>
|
||||
<UrlParamsProvider>
|
||||
<LoadingIndicatorProvider>
|
||||
<LicenseProvider>
|
||||
<App />
|
||||
</LicenseProvider>
|
||||
</LoadingIndicatorProvider>
|
||||
</UrlParamsProvider>
|
||||
</MatchedRouteProvider>
|
||||
</LocationProvider>
|
||||
</Router>
|
||||
</i18nCore.Context>
|
||||
</KibanaContextProvider>
|
||||
</AlertsContextProvider>
|
||||
</ApmPluginContext.Provider>
|
||||
<RedirectAppLinks application={core.application}>
|
||||
<ApmPluginContext.Provider value={apmPluginContextValue}>
|
||||
<AlertsContextProvider
|
||||
value={{
|
||||
http: core.http,
|
||||
docLinks: core.docLinks,
|
||||
capabilities: core.application.capabilities,
|
||||
toastNotifications: core.notifications.toasts,
|
||||
actionTypeRegistry: plugins.triggers_actions_ui.actionTypeRegistry,
|
||||
alertTypeRegistry: plugins.triggers_actions_ui.alertTypeRegistry,
|
||||
}}
|
||||
>
|
||||
<KibanaContextProvider services={{ ...core, ...plugins }}>
|
||||
<i18nCore.Context>
|
||||
<Router history={history}>
|
||||
<LocationProvider>
|
||||
<MatchedRouteProvider routes={routes}>
|
||||
<UrlParamsProvider>
|
||||
<LoadingIndicatorProvider>
|
||||
<LicenseProvider>
|
||||
<App />
|
||||
</LicenseProvider>
|
||||
</LoadingIndicatorProvider>
|
||||
</UrlParamsProvider>
|
||||
</MatchedRouteProvider>
|
||||
</LocationProvider>
|
||||
</Router>
|
||||
</i18nCore.Context>
|
||||
</KibanaContextProvider>
|
||||
</AlertsContextProvider>
|
||||
</ApmPluginContext.Provider>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -124,7 +126,7 @@ export function ApmAppRoot({
|
|||
export const renderApp = (
|
||||
core: CoreStart,
|
||||
deps: ApmPluginSetupDeps,
|
||||
{ element }: AppMountParameters,
|
||||
{ element, history }: AppMountParameters,
|
||||
config: ConfigSchema
|
||||
) => {
|
||||
// render APM feedback link in global help menu
|
||||
|
@ -133,8 +135,6 @@ export const renderApp = (
|
|||
|
||||
createCallApmApi(core.http);
|
||||
|
||||
resetHistory();
|
||||
|
||||
// Automatically creates static index pattern and stores as saved object
|
||||
createStaticIndexPattern().catch((e) => {
|
||||
// eslint-disable-next-line no-console
|
||||
|
@ -142,12 +142,7 @@ export const renderApp = (
|
|||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<ApmAppRoot
|
||||
core={core}
|
||||
deps={deps}
|
||||
routerHistory={history}
|
||||
config={config}
|
||||
/>,
|
||||
<ApmAppRoot core={core} deps={deps} history={history} config={config} />,
|
||||
element
|
||||
);
|
||||
return () => {
|
||||
|
|
|
@ -6,40 +6,40 @@
|
|||
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiIcon,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiTab,
|
||||
EuiTabs,
|
||||
EuiTitle,
|
||||
EuiIcon,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Location } from 'history';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { first } from 'lodash';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { ErrorGroupAPIResponse } from '../../../../../server/lib/errors/get_error_group';
|
||||
import { APMError } from '../../../../../typings/es_schemas/ui/apm_error';
|
||||
import { IUrlParams } from '../../../../context/UrlParamsContext/types';
|
||||
import { px, unit, units } from '../../../../style/variables';
|
||||
import { TransactionDetailLink } from '../../../shared/Links/apm/TransactionDetailLink';
|
||||
import { DiscoverErrorLink } from '../../../shared/Links/DiscoverLinks/DiscoverErrorLink';
|
||||
import { fromQuery, toQuery } from '../../../shared/Links/url_helpers';
|
||||
import { history } from '../../../../utils/history';
|
||||
import { ErrorMetadata } from '../../../shared/MetadataTable/ErrorMetadata';
|
||||
import { Stacktrace } from '../../../shared/Stacktrace';
|
||||
import { Summary } from '../../../shared/Summary';
|
||||
import { HttpInfoSummaryItem } from '../../../shared/Summary/HttpInfoSummaryItem';
|
||||
import { UserAgentSummaryItem } from '../../../shared/Summary/UserAgentSummaryItem';
|
||||
import { TimestampTooltip } from '../../../shared/TimestampTooltip';
|
||||
import {
|
||||
ErrorTab,
|
||||
exceptionStacktraceTab,
|
||||
getTabs,
|
||||
logStacktraceTab,
|
||||
} from './ErrorTabs';
|
||||
import { Summary } from '../../../shared/Summary';
|
||||
import { TimestampTooltip } from '../../../shared/TimestampTooltip';
|
||||
import { HttpInfoSummaryItem } from '../../../shared/Summary/HttpInfoSummaryItem';
|
||||
import { TransactionDetailLink } from '../../../shared/Links/apm/TransactionDetailLink';
|
||||
import { UserAgentSummaryItem } from '../../../shared/Summary/UserAgentSummaryItem';
|
||||
import { ExceptionStacktrace } from './ExceptionStacktrace';
|
||||
|
||||
const HeaderContainer = styled.div`
|
||||
|
@ -71,6 +71,7 @@ function getCurrentTab(
|
|||
}
|
||||
|
||||
export function DetailView({ errorGroup, urlParams, location }: Props) {
|
||||
const history = useHistory();
|
||||
const { transaction, error, occurrencesCount } = errorGroup;
|
||||
|
||||
if (!error) {
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
|
||||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { MockApmPluginContextWrapper } from '../../../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { MockUrlParamsContextProvider } from '../../../../../context/UrlParamsContext/MockUrlParamsContextProvider';
|
||||
import { mockMoment, toJson } from '../../../../../utils/testHelpers';
|
||||
import { ErrorGroupList } from '../index';
|
||||
import props from './props.json';
|
||||
import { MockUrlParamsContextProvider } from '../../../../../context/UrlParamsContext/MockUrlParamsContextProvider';
|
||||
|
||||
jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => {
|
||||
return {
|
||||
|
@ -36,9 +37,11 @@ describe('ErrorGroupOverview -> List', () => {
|
|||
|
||||
it('should render with data', () => {
|
||||
const wrapper = mount(
|
||||
<MockUrlParamsContextProvider>
|
||||
<ErrorGroupList items={props.items} />
|
||||
</MockUrlParamsContextProvider>
|
||||
<MockApmPluginContextWrapper>
|
||||
<MockUrlParamsContextProvider>
|
||||
<ErrorGroupList items={props.items} />
|
||||
</MockUrlParamsContextProvider>
|
||||
</MockApmPluginContextWrapper>
|
||||
);
|
||||
|
||||
expect(toJson(wrapper)).toMatchSnapshot();
|
||||
|
|
|
@ -784,11 +784,11 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c0"
|
||||
href="#/services/opbeans-python/errors/a0ce2c8978ef92cdf2ff163ae28576ee?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/a0ce2c8978ef92cdf2ff163ae28576ee"
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c0"
|
||||
href="#/services/opbeans-python/errors/a0ce2c8978ef92cdf2ff163ae28576ee?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/a0ce2c8978ef92cdf2ff163ae28576ee"
|
||||
rel="noreferrer"
|
||||
>
|
||||
a0ce2
|
||||
|
@ -829,11 +829,11 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c1"
|
||||
href="#/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c1"
|
||||
href="#/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
rel="noreferrer"
|
||||
/>
|
||||
</EuiLink>
|
||||
|
@ -876,13 +876,13 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c3"
|
||||
href="#/services/opbeans-python/errors/a0ce2c8978ef92cdf2ff163ae28576ee?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/a0ce2c8978ef92cdf2ff163ae28576ee"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c3"
|
||||
href="#/services/opbeans-python/errors/a0ce2c8978ef92cdf2ff163ae28576ee?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/a0ce2c8978ef92cdf2ff163ae28576ee"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
rel="noreferrer"
|
||||
|
@ -1018,11 +1018,11 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c0"
|
||||
href="#/services/opbeans-python/errors/f3ac95493913cc7a3cfec30a19d2120a?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/f3ac95493913cc7a3cfec30a19d2120a"
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c0"
|
||||
href="#/services/opbeans-python/errors/f3ac95493913cc7a3cfec30a19d2120a?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/f3ac95493913cc7a3cfec30a19d2120a"
|
||||
rel="noreferrer"
|
||||
>
|
||||
f3ac9
|
||||
|
@ -1063,11 +1063,11 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c1"
|
||||
href="#/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c1"
|
||||
href="#/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
rel="noreferrer"
|
||||
/>
|
||||
</EuiLink>
|
||||
|
@ -1110,13 +1110,13 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c3"
|
||||
href="#/services/opbeans-python/errors/f3ac95493913cc7a3cfec30a19d2120a?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/f3ac95493913cc7a3cfec30a19d2120a"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c3"
|
||||
href="#/services/opbeans-python/errors/f3ac95493913cc7a3cfec30a19d2120a?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/f3ac95493913cc7a3cfec30a19d2120a"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
rel="noreferrer"
|
||||
|
@ -1252,11 +1252,11 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c0"
|
||||
href="#/services/opbeans-python/errors/e90863d04b7a692435305f09bbe8c840?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/e90863d04b7a692435305f09bbe8c840"
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c0"
|
||||
href="#/services/opbeans-python/errors/e90863d04b7a692435305f09bbe8c840?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/e90863d04b7a692435305f09bbe8c840"
|
||||
rel="noreferrer"
|
||||
>
|
||||
e9086
|
||||
|
@ -1297,11 +1297,11 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c1"
|
||||
href="#/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c1"
|
||||
href="#/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
rel="noreferrer"
|
||||
/>
|
||||
</EuiLink>
|
||||
|
@ -1344,13 +1344,13 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c3"
|
||||
href="#/services/opbeans-python/errors/e90863d04b7a692435305f09bbe8c840?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/e90863d04b7a692435305f09bbe8c840"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c3"
|
||||
href="#/services/opbeans-python/errors/e90863d04b7a692435305f09bbe8c840?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/e90863d04b7a692435305f09bbe8c840"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
rel="noreferrer"
|
||||
|
@ -1486,11 +1486,11 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c0"
|
||||
href="#/services/opbeans-python/errors/8673d8bf7a032e387c101bafbab0d2bc?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/8673d8bf7a032e387c101bafbab0d2bc"
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c0"
|
||||
href="#/services/opbeans-python/errors/8673d8bf7a032e387c101bafbab0d2bc?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/8673d8bf7a032e387c101bafbab0d2bc"
|
||||
rel="noreferrer"
|
||||
>
|
||||
8673d
|
||||
|
@ -1531,11 +1531,11 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c1"
|
||||
href="#/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c1"
|
||||
href="#/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors?page=0&serviceName=opbeans-python&transactionType=request&start=2018-01-10T09:51:41.050Z&end=2018-01-10T10:06:41.050Z&kuery=error.exception.type:undefined"
|
||||
rel="noreferrer"
|
||||
/>
|
||||
</EuiLink>
|
||||
|
@ -1578,13 +1578,13 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
|
|||
>
|
||||
<EuiLink
|
||||
className="c3"
|
||||
href="#/services/opbeans-python/errors/8673d8bf7a032e387c101bafbab0d2bc?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/8673d8bf7a032e387c101bafbab0d2bc"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
>
|
||||
<a
|
||||
className="euiLink euiLink--primary c3"
|
||||
href="#/services/opbeans-python/errors/8673d8bf7a032e387c101bafbab0d2bc?"
|
||||
href="/basepath/app/apm/services/opbeans-python/errors/8673d8bf7a032e387c101bafbab0d2bc"
|
||||
onBlur={[Function]}
|
||||
onFocus={[Function]}
|
||||
rel="noreferrer"
|
||||
|
|
|
@ -11,8 +11,21 @@ exports[`Home component should render services 1`] = `
|
|||
},
|
||||
},
|
||||
"core": Object {
|
||||
"application": Object {
|
||||
"capabilities": Object {
|
||||
"apm": Object {},
|
||||
},
|
||||
"currentAppId$": Observable {
|
||||
"_isScalar": false,
|
||||
},
|
||||
},
|
||||
"chrome": Object {
|
||||
"docTitle": Object {
|
||||
"change": [Function],
|
||||
},
|
||||
"setBadge": [Function],
|
||||
"setBreadcrumbs": [Function],
|
||||
"setHelpExtension": [Function],
|
||||
},
|
||||
"docLinks": Object {
|
||||
"DOC_LINK_VERSION": "0",
|
||||
|
@ -23,6 +36,9 @@ exports[`Home component should render services 1`] = `
|
|||
"prepend": [Function],
|
||||
},
|
||||
},
|
||||
"i18n": Object {
|
||||
"Context": [Function],
|
||||
},
|
||||
"notifications": Object {
|
||||
"toasts": Object {
|
||||
"addDanger": [Function],
|
||||
|
@ -31,6 +47,7 @@ exports[`Home component should render services 1`] = `
|
|||
},
|
||||
"uiSettings": Object {
|
||||
"get": [Function],
|
||||
"get$": [Function],
|
||||
},
|
||||
},
|
||||
"plugins": Object {},
|
||||
|
@ -54,8 +71,21 @@ exports[`Home component should render traces 1`] = `
|
|||
},
|
||||
},
|
||||
"core": Object {
|
||||
"application": Object {
|
||||
"capabilities": Object {
|
||||
"apm": Object {},
|
||||
},
|
||||
"currentAppId$": Observable {
|
||||
"_isScalar": false,
|
||||
},
|
||||
},
|
||||
"chrome": Object {
|
||||
"docTitle": Object {
|
||||
"change": [Function],
|
||||
},
|
||||
"setBadge": [Function],
|
||||
"setBreadcrumbs": [Function],
|
||||
"setHelpExtension": [Function],
|
||||
},
|
||||
"docLinks": Object {
|
||||
"DOC_LINK_VERSION": "0",
|
||||
|
@ -66,6 +96,9 @@ exports[`Home component should render traces 1`] = `
|
|||
"prepend": [Function],
|
||||
},
|
||||
},
|
||||
"i18n": Object {
|
||||
"Context": [Function],
|
||||
},
|
||||
"notifications": Object {
|
||||
"toasts": Object {
|
||||
"addDanger": [Function],
|
||||
|
@ -74,6 +107,7 @@ exports[`Home component should render traces 1`] = `
|
|||
},
|
||||
"uiSettings": Object {
|
||||
"get": [Function],
|
||||
"get$": [Function],
|
||||
},
|
||||
},
|
||||
"plugins": Object {},
|
||||
|
|
|
@ -60,29 +60,32 @@ describe('UpdateBreadcrumbs', () => {
|
|||
'rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0'
|
||||
);
|
||||
const breadcrumbs = setBreadcrumbs.mock.calls[0][0];
|
||||
expect(breadcrumbs).toEqual([
|
||||
{
|
||||
text: 'APM',
|
||||
href:
|
||||
'#/?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0',
|
||||
},
|
||||
{
|
||||
text: 'Services',
|
||||
href:
|
||||
'#/services?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0',
|
||||
},
|
||||
{
|
||||
text: 'opbeans-node',
|
||||
href:
|
||||
'#/services/opbeans-node?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0',
|
||||
},
|
||||
{
|
||||
text: 'Errors',
|
||||
href:
|
||||
'#/services/opbeans-node/errors?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0',
|
||||
},
|
||||
{ text: 'myGroupId', href: undefined },
|
||||
]);
|
||||
expect(breadcrumbs).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
text: 'APM',
|
||||
href:
|
||||
'/basepath/app/apm/?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'Services',
|
||||
href:
|
||||
'/basepath/app/apm/services?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'opbeans-node',
|
||||
href:
|
||||
'/basepath/app/apm/services/opbeans-node?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'Errors',
|
||||
href:
|
||||
'/basepath/app/apm/services/opbeans-node/errors?kuery=myKuery&rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0',
|
||||
}),
|
||||
expect.objectContaining({ text: 'myGroupId', href: undefined }),
|
||||
])
|
||||
);
|
||||
|
||||
expect(changeTitle).toHaveBeenCalledWith([
|
||||
'myGroupId',
|
||||
'Errors',
|
||||
|
@ -95,12 +98,23 @@ describe('UpdateBreadcrumbs', () => {
|
|||
it('/services/:serviceName/errors', () => {
|
||||
mountBreadcrumb('/services/opbeans-node/errors');
|
||||
const breadcrumbs = setBreadcrumbs.mock.calls[0][0];
|
||||
expect(breadcrumbs).toEqual([
|
||||
{ text: 'APM', href: '#/?kuery=myKuery' },
|
||||
{ text: 'Services', href: '#/services?kuery=myKuery' },
|
||||
{ text: 'opbeans-node', href: '#/services/opbeans-node?kuery=myKuery' },
|
||||
{ text: 'Errors', href: undefined },
|
||||
]);
|
||||
expect(breadcrumbs).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
text: 'APM',
|
||||
href: '/basepath/app/apm/?kuery=myKuery',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'Services',
|
||||
href: '/basepath/app/apm/services?kuery=myKuery',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'opbeans-node',
|
||||
href: '/basepath/app/apm/services/opbeans-node?kuery=myKuery',
|
||||
}),
|
||||
expect.objectContaining({ text: 'Errors', href: undefined }),
|
||||
])
|
||||
);
|
||||
expect(changeTitle).toHaveBeenCalledWith([
|
||||
'Errors',
|
||||
'opbeans-node',
|
||||
|
@ -112,12 +126,24 @@ describe('UpdateBreadcrumbs', () => {
|
|||
it('/services/:serviceName/transactions', () => {
|
||||
mountBreadcrumb('/services/opbeans-node/transactions');
|
||||
const breadcrumbs = setBreadcrumbs.mock.calls[0][0];
|
||||
expect(breadcrumbs).toEqual([
|
||||
{ text: 'APM', href: '#/?kuery=myKuery' },
|
||||
{ text: 'Services', href: '#/services?kuery=myKuery' },
|
||||
{ text: 'opbeans-node', href: '#/services/opbeans-node?kuery=myKuery' },
|
||||
{ text: 'Transactions', href: undefined },
|
||||
]);
|
||||
expect(breadcrumbs).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
text: 'APM',
|
||||
href: '/basepath/app/apm/?kuery=myKuery',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'Services',
|
||||
href: '/basepath/app/apm/services?kuery=myKuery',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'opbeans-node',
|
||||
href: '/basepath/app/apm/services/opbeans-node?kuery=myKuery',
|
||||
}),
|
||||
expect.objectContaining({ text: 'Transactions', href: undefined }),
|
||||
])
|
||||
);
|
||||
|
||||
expect(changeTitle).toHaveBeenCalledWith([
|
||||
'Transactions',
|
||||
'opbeans-node',
|
||||
|
@ -132,16 +158,33 @@ describe('UpdateBreadcrumbs', () => {
|
|||
'transactionName=my-transaction-name'
|
||||
);
|
||||
const breadcrumbs = setBreadcrumbs.mock.calls[0][0];
|
||||
expect(breadcrumbs).toEqual([
|
||||
{ text: 'APM', href: '#/?kuery=myKuery' },
|
||||
{ text: 'Services', href: '#/services?kuery=myKuery' },
|
||||
{ text: 'opbeans-node', href: '#/services/opbeans-node?kuery=myKuery' },
|
||||
{
|
||||
text: 'Transactions',
|
||||
href: '#/services/opbeans-node/transactions?kuery=myKuery',
|
||||
},
|
||||
{ text: 'my-transaction-name', href: undefined },
|
||||
]);
|
||||
|
||||
expect(breadcrumbs).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
text: 'APM',
|
||||
href: '/basepath/app/apm/?kuery=myKuery',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'Services',
|
||||
href: '/basepath/app/apm/services?kuery=myKuery',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'opbeans-node',
|
||||
href: '/basepath/app/apm/services/opbeans-node?kuery=myKuery',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'Transactions',
|
||||
href:
|
||||
'/basepath/app/apm/services/opbeans-node/transactions?kuery=myKuery',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
text: 'my-transaction-name',
|
||||
href: undefined,
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
expect(changeTitle).toHaveBeenCalledWith([
|
||||
'my-transaction-name',
|
||||
'Transactions',
|
||||
|
|
|
@ -5,20 +5,20 @@
|
|||
*/
|
||||
|
||||
import { Location } from 'history';
|
||||
import React from 'react';
|
||||
import { AppMountContext } from 'src/core/public';
|
||||
import React, { MouseEvent } from 'react';
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
|
||||
import { getAPMHref } from '../../shared/Links/apm/APMLink';
|
||||
import {
|
||||
Breadcrumb,
|
||||
ProvideBreadcrumbs,
|
||||
BreadcrumbRoute,
|
||||
ProvideBreadcrumbs,
|
||||
} from './ProvideBreadcrumbs';
|
||||
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
|
||||
|
||||
interface Props {
|
||||
location: Location;
|
||||
breadcrumbs: Breadcrumb[];
|
||||
core: AppMountContext['core'];
|
||||
core: CoreStart;
|
||||
}
|
||||
|
||||
function getTitleFromBreadCrumbs(breadcrumbs: Breadcrumb[]) {
|
||||
|
@ -27,15 +27,24 @@ function getTitleFromBreadCrumbs(breadcrumbs: Breadcrumb[]) {
|
|||
|
||||
class UpdateBreadcrumbsComponent extends React.Component<Props> {
|
||||
public updateHeaderBreadcrumbs() {
|
||||
const { basePath } = this.props.core.http;
|
||||
const breadcrumbs = this.props.breadcrumbs.map(
|
||||
({ value, match }, index) => {
|
||||
const { search } = this.props.location;
|
||||
const isLastBreadcrumbItem =
|
||||
index === this.props.breadcrumbs.length - 1;
|
||||
const href = isLastBreadcrumbItem
|
||||
? undefined // makes the breadcrumb item not clickable
|
||||
: getAPMHref({ basePath, path: match.url, search });
|
||||
return {
|
||||
text: value,
|
||||
href: isLastBreadcrumbItem
|
||||
? undefined // makes the breadcrumb item not clickable
|
||||
: getAPMHref(match.url, this.props.location.search),
|
||||
href,
|
||||
onClick: (event: MouseEvent<HTMLAnchorElement>) => {
|
||||
if (href) {
|
||||
event.preventDefault();
|
||||
this.props.core.application.navigateToUrl(href);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
|
@ -38,14 +38,28 @@ interface RouteParams {
|
|||
}
|
||||
|
||||
export const renderAsRedirectTo = (to: string) => {
|
||||
return ({ location }: RouteComponentProps<RouteParams>) => (
|
||||
<Redirect
|
||||
to={{
|
||||
...location,
|
||||
pathname: to,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return ({ location }: RouteComponentProps<RouteParams>) => {
|
||||
let resolvedUrl: URL | undefined;
|
||||
|
||||
// Redirect root URLs with a hash to support backward compatibility with URLs
|
||||
// from before we switched to the non-hash platform history.
|
||||
if (location.pathname === '' && location.hash.length > 0) {
|
||||
// We just want the search and pathname so the host doesn't matter
|
||||
resolvedUrl = new URL(location.hash.slice(1), 'http://localhost');
|
||||
to = resolvedUrl.pathname;
|
||||
}
|
||||
|
||||
return (
|
||||
<Redirect
|
||||
to={{
|
||||
...location,
|
||||
hash: '',
|
||||
pathname: to,
|
||||
search: resolvedUrl ? resolvedUrl.search : location.search,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
export const routes: BreadcrumbRoute[] = [
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { routes } from './';
|
||||
|
||||
describe('routes', () => {
|
||||
describe('/', () => {
|
||||
const route = routes.find((r) => r.path === '/');
|
||||
|
||||
describe('with no hash path', () => {
|
||||
it('redirects to /services', () => {
|
||||
const location = { hash: '', pathname: '/', search: '' };
|
||||
expect(
|
||||
(route as any).render({ location } as any).props.to.pathname
|
||||
).toEqual('/services');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a hash path', () => {
|
||||
it('redirects to the hash path', () => {
|
||||
const location = {
|
||||
hash:
|
||||
'#/services/opbeans-python/transactions/view?rangeFrom=now-24h&rangeTo=now&refreshInterval=10000&refreshPaused=false&traceId=d919c89dc7ca48d84b9dde1fef01d1f8&transactionId=1b542853d787ba7b&transactionName=GET%20opbeans.views.product_customers&transactionType=request&flyoutDetailTab=&waterfallItemId=1b542853d787ba7b',
|
||||
pathname: '',
|
||||
search: '',
|
||||
};
|
||||
|
||||
expect(((route as any).render({ location }) as any).props.to).toEqual({
|
||||
hash: '',
|
||||
pathname: '/services/opbeans-python/transactions/view',
|
||||
search:
|
||||
'?rangeFrom=now-24h&rangeTo=now&refreshInterval=10000&refreshPaused=false&traceId=d919c89dc7ca48d84b9dde1fef01d1f8&transactionId=1b542853d787ba7b&transactionName=GET%20opbeans.views.product_customers&transactionType=request&flyoutDetailTab=&waterfallItemId=1b542853d787ba7b',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,13 +5,14 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useFetcher } from '../../../../../hooks/useFetcher';
|
||||
import { history } from '../../../../../utils/history';
|
||||
import { toQuery } from '../../../../shared/Links/url_helpers';
|
||||
import { Settings } from '../../../Settings';
|
||||
import { AgentConfigurationCreateEdit } from '../../../Settings/AgentConfigurations/AgentConfigurationCreateEdit';
|
||||
import { toQuery } from '../../../../shared/Links/url_helpers';
|
||||
|
||||
export function EditAgentConfigurationRouteHandler() {
|
||||
const history = useHistory();
|
||||
const { search } = history.location;
|
||||
|
||||
// typescript complains because `pageStop` does not exist in `APMQueryParams`
|
||||
|
@ -40,6 +41,7 @@ export function EditAgentConfigurationRouteHandler() {
|
|||
}
|
||||
|
||||
export function CreateAgentConfigurationRouteHandler() {
|
||||
const history = useHistory();
|
||||
const { search } = history.location;
|
||||
|
||||
// Ignoring here because we specifically DO NOT want to add the query params to the global route handler
|
||||
|
|
|
@ -4,33 +4,33 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import numeral from '@elastic/numeral';
|
||||
import {
|
||||
Axis,
|
||||
BarSeries,
|
||||
BrushEndListener,
|
||||
Chart,
|
||||
DARK_THEME,
|
||||
LIGHT_THEME,
|
||||
niceTimeFormatByDay,
|
||||
ScaleType,
|
||||
SeriesNameFn,
|
||||
Settings,
|
||||
timeFormatter,
|
||||
} from '@elastic/charts';
|
||||
import { DARK_THEME, LIGHT_THEME } from '@elastic/charts';
|
||||
|
||||
import { Position } from '@elastic/charts/dist/utils/commons';
|
||||
import {
|
||||
EUI_CHARTS_THEME_DARK,
|
||||
EUI_CHARTS_THEME_LIGHT,
|
||||
} from '@elastic/eui/dist/eui_charts_theme';
|
||||
import numeral from '@elastic/numeral';
|
||||
import moment from 'moment';
|
||||
import { Position } from '@elastic/charts/dist/utils/commons';
|
||||
import { I18LABELS } from '../translations';
|
||||
import { history } from '../../../../utils/history';
|
||||
import { fromQuery, toQuery } from '../../../shared/Links/url_helpers';
|
||||
import { ChartWrapper } from '../ChartWrapper';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { useUrlParams } from '../../../../hooks/useUrlParams';
|
||||
import { fromQuery, toQuery } from '../../../shared/Links/url_helpers';
|
||||
import { ChartWrapper } from '../ChartWrapper';
|
||||
import { I18LABELS } from '../translations';
|
||||
|
||||
interface Props {
|
||||
data?: Array<Record<string, number | null>>;
|
||||
|
@ -38,6 +38,7 @@ interface Props {
|
|||
}
|
||||
|
||||
export function PageViewsChart({ data, loading }: Props) {
|
||||
const history = useHistory();
|
||||
const { urlParams } = useUrlParams();
|
||||
|
||||
const { start, end } = urlParams;
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
|
||||
import { render } from '@testing-library/react';
|
||||
import cytoscape from 'cytoscape';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { ThemeContext } from 'styled-components';
|
||||
import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { Controls } from './Controls';
|
||||
import { CytoscapeContext } from './Cytoscape';
|
||||
|
||||
const cy = cytoscape({
|
||||
elements: [{ classes: 'primary', data: { id: 'test node' } }],
|
||||
});
|
||||
|
||||
function Wrapper({ children }: { children?: ReactNode }) {
|
||||
return (
|
||||
<CytoscapeContext.Provider value={cy}>
|
||||
<MockApmPluginContextWrapper>
|
||||
<ThemeContext.Provider value={{ eui: lightTheme }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
</MockApmPluginContextWrapper>
|
||||
</CytoscapeContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
describe('Controls', () => {
|
||||
describe('with a primary node', () => {
|
||||
it('links to the full map', async () => {
|
||||
const result = render(<Controls />, { wrapper: Wrapper });
|
||||
const { findByTestId } = result;
|
||||
|
||||
const button = await findByTestId('viewFullMapButton');
|
||||
|
||||
expect(button.getAttribute('href')).toEqual(
|
||||
'/basepath/app/apm/service-map'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -4,16 +4,17 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { EuiButtonIcon, EuiPanel, EuiToolTip } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
|
||||
import { useTheme } from '../../../hooks/useTheme';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { getAPMHref } from '../../shared/Links/apm/APMLink';
|
||||
import { APMQueryParams } from '../../shared/Links/url_helpers';
|
||||
import { CytoscapeContext } from './Cytoscape';
|
||||
import { getAnimationOptions, getNodeHeight } from './cytoscapeOptions';
|
||||
import { getAPMHref } from '../../shared/Links/apm/APMLink';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { APMQueryParams } from '../../shared/Links/url_helpers';
|
||||
import { useTheme } from '../../../hooks/useTheme';
|
||||
|
||||
const ControlsContainer = styled('div')`
|
||||
left: ${({ theme }) => theme.eui.gutterTypes.gutterMedium};
|
||||
|
@ -96,6 +97,8 @@ function useDebugDownloadUrl(cy?: cytoscape.Core) {
|
|||
}
|
||||
|
||||
export function Controls() {
|
||||
const { core } = useApmPluginContext();
|
||||
const { basePath } = core.http;
|
||||
const theme = useTheme();
|
||||
const cy = useContext(CytoscapeContext);
|
||||
const { urlParams } = useUrlParams();
|
||||
|
@ -103,6 +106,12 @@ export function Controls() {
|
|||
const [zoom, setZoom] = useState((cy && cy.zoom()) || 1);
|
||||
const duration = parseInt(theme.eui.euiAnimSpeedFast, 10);
|
||||
const downloadUrl = useDebugDownloadUrl(cy);
|
||||
const viewFullMapUrl = getAPMHref({
|
||||
basePath,
|
||||
path: '/service-map',
|
||||
search: currentSearch,
|
||||
query: urlParams as APMQueryParams,
|
||||
});
|
||||
|
||||
// Handle zoom events
|
||||
useEffect(() => {
|
||||
|
@ -209,11 +218,8 @@ export function Controls() {
|
|||
<Button
|
||||
aria-label={viewFullMapLabel}
|
||||
color="text"
|
||||
href={getAPMHref(
|
||||
'/service-map',
|
||||
currentSearch,
|
||||
urlParams as APMQueryParams
|
||||
)}
|
||||
data-test-subj="viewFullMapButton"
|
||||
href={viewFullMapUrl}
|
||||
iconType="apps"
|
||||
/>
|
||||
</EuiToolTip>
|
||||
|
|
|
@ -4,14 +4,21 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Buttons } from './Buttons';
|
||||
import { render } from '@testing-library/react';
|
||||
import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
|
||||
function Wrapper({ children }: { children?: ReactNode }) {
|
||||
return <MockApmPluginContextWrapper>{children}</MockApmPluginContextWrapper>;
|
||||
}
|
||||
|
||||
describe('Popover Buttons', () => {
|
||||
it('renders', () => {
|
||||
expect(() =>
|
||||
render(<Buttons selectedNodeServiceName="test service name" />)
|
||||
render(<Buttons selectedNodeServiceName="test service name" />, {
|
||||
wrapper: Wrapper,
|
||||
})
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
|
@ -21,7 +28,8 @@ describe('Popover Buttons', () => {
|
|||
<Buttons
|
||||
onFocusClick={onFocusClick}
|
||||
selectedNodeServiceName="test service name"
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
const focusButton = await result.findByText('Focus map');
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import { EuiButton, EuiFlexItem } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { MouseEvent } from 'react';
|
||||
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
|
||||
import { useUrlParams } from '../../../../hooks/useUrlParams';
|
||||
import { getAPMHref } from '../../../shared/Links/apm/APMLink';
|
||||
import { APMQueryParams } from '../../../shared/Links/url_helpers';
|
||||
|
@ -22,22 +23,24 @@ export function Buttons({
|
|||
onFocusClick = () => {},
|
||||
selectedNodeServiceName,
|
||||
}: ButtonsProps) {
|
||||
const { core } = useApmPluginContext();
|
||||
const { basePath } = core.http;
|
||||
// The params may contain the service name. We want to use the selected node's
|
||||
// service name in the button URLs, so make a copy and set the
|
||||
// `serviceName` property.
|
||||
const urlParams = { ...useUrlParams().urlParams } as APMQueryParams;
|
||||
urlParams.serviceName = selectedNodeServiceName;
|
||||
|
||||
const detailsUrl = getAPMHref(
|
||||
`/services/${selectedNodeServiceName}/transactions`,
|
||||
'',
|
||||
urlParams
|
||||
);
|
||||
const focusUrl = getAPMHref(
|
||||
`/services/${selectedNodeServiceName}/service-map`,
|
||||
'',
|
||||
urlParams
|
||||
);
|
||||
const detailsUrl = getAPMHref({
|
||||
basePath,
|
||||
path: `/services/${selectedNodeServiceName}/transactions`,
|
||||
query: urlParams,
|
||||
});
|
||||
const focusUrl = getAPMHref({
|
||||
basePath,
|
||||
path: `/services/${selectedNodeServiceName}/service-map`,
|
||||
query: urlParams,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -5,11 +5,17 @@
|
|||
*/
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { createKibanaReactContext } from 'src/plugins/kibana_react/public';
|
||||
import { License } from '../../../../../licensing/common/license';
|
||||
import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { LicenseContext } from '../../../context/LicenseContext';
|
||||
import { ServiceMap } from './';
|
||||
import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
|
||||
const KibanaReactContext = createKibanaReactContext({
|
||||
usageCollection: { reportUiStats: () => {} },
|
||||
} as Partial<CoreStart>);
|
||||
|
||||
const expiredLicense = new License({
|
||||
signature: 'test signature',
|
||||
|
@ -24,9 +30,11 @@ const expiredLicense = new License({
|
|||
|
||||
function Wrapper({ children }: { children?: ReactNode }) {
|
||||
return (
|
||||
<LicenseContext.Provider value={expiredLicense}>
|
||||
<MockApmPluginContextWrapper>{children}</MockApmPluginContextWrapper>
|
||||
</LicenseContext.Provider>
|
||||
<KibanaReactContext.Provider>
|
||||
<LicenseContext.Provider value={expiredLicense}>
|
||||
<MockApmPluginContextWrapper>{children}</MockApmPluginContextWrapper>
|
||||
</LicenseContext.Provider>
|
||||
</KibanaReactContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,43 +4,49 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { ReactChild, FunctionComponent } from 'react';
|
||||
import { render, wait, waitForElement } from '@testing-library/react';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import React, { FunctionComponent, ReactChild } from 'react';
|
||||
import { createKibanaReactContext } from 'src/plugins/kibana_react/public';
|
||||
import { ServiceOverview } from '..';
|
||||
import * as urlParamsHooks from '../../../../hooks/useUrlParams';
|
||||
import * as useLocalUIFilters from '../../../../hooks/useLocalUIFilters';
|
||||
import { FETCH_STATUS } from '../../../../hooks/useFetcher';
|
||||
import { SessionStorageMock } from '../../../../services/__test__/SessionStorageMock';
|
||||
import { ApmPluginContextValue } from '../../../../context/ApmPluginContext';
|
||||
import {
|
||||
MockApmPluginContextWrapper,
|
||||
mockApmPluginContextValue,
|
||||
MockApmPluginContextWrapper,
|
||||
} from '../../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { FETCH_STATUS } from '../../../../hooks/useFetcher';
|
||||
import * as useLocalUIFilters from '../../../../hooks/useLocalUIFilters';
|
||||
import * as urlParamsHooks from '../../../../hooks/useUrlParams';
|
||||
import { SessionStorageMock } from '../../../../services/__test__/SessionStorageMock';
|
||||
|
||||
jest.mock('ui/new_platform');
|
||||
const KibanaReactContext = createKibanaReactContext({
|
||||
usageCollection: { reportUiStats: () => {} },
|
||||
} as Partial<CoreStart>);
|
||||
|
||||
function wrapper({ children }: { children: ReactChild }) {
|
||||
return (
|
||||
<MockApmPluginContextWrapper
|
||||
value={
|
||||
({
|
||||
...mockApmPluginContextValue,
|
||||
core: {
|
||||
...mockApmPluginContextValue.core,
|
||||
http: { ...mockApmPluginContextValue.core.http, get: httpGet },
|
||||
notifications: {
|
||||
...mockApmPluginContextValue.core.notifications,
|
||||
toasts: {
|
||||
...mockApmPluginContextValue.core.notifications.toasts,
|
||||
addWarning,
|
||||
<KibanaReactContext.Provider>
|
||||
<MockApmPluginContextWrapper
|
||||
value={
|
||||
({
|
||||
...mockApmPluginContextValue,
|
||||
core: {
|
||||
...mockApmPluginContextValue.core,
|
||||
http: { ...mockApmPluginContextValue.core.http, get: httpGet },
|
||||
notifications: {
|
||||
...mockApmPluginContextValue.core.notifications,
|
||||
toasts: {
|
||||
...mockApmPluginContextValue.core.notifications.toasts,
|
||||
addWarning,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown) as ApmPluginContextValue
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</MockApmPluginContextWrapper>
|
||||
} as unknown) as ApmPluginContextValue
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</MockApmPluginContextWrapper>
|
||||
</KibanaReactContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ NodeList [
|
|||
>
|
||||
<a
|
||||
class="euiLink euiLink--primary c0"
|
||||
href="#/services/My Go Service/transactions?"
|
||||
href="/basepath/app/apm/services/My Go Service/transactions"
|
||||
rel="noreferrer"
|
||||
>
|
||||
My Go Service
|
||||
|
@ -262,7 +262,7 @@ NodeList [
|
|||
>
|
||||
<a
|
||||
class="euiLink euiLink--primary c0"
|
||||
href="#/services/My Python Service/transactions?"
|
||||
href="/basepath/app/apm/services/My Python Service/transactions"
|
||||
rel="noreferrer"
|
||||
>
|
||||
My Python Service
|
||||
|
|
|
@ -5,37 +5,37 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
EuiBottomBar,
|
||||
EuiButton,
|
||||
EuiForm,
|
||||
EuiTitle,
|
||||
EuiSpacer,
|
||||
EuiPanel,
|
||||
EuiButtonEmpty,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiStat,
|
||||
EuiBottomBar,
|
||||
EuiText,
|
||||
EuiForm,
|
||||
EuiHealth,
|
||||
EuiLoadingSpinner,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiStat,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiButtonEmpty } from '@elastic/eui';
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
import { FETCH_STATUS } from '../../../../../../hooks/useFetcher';
|
||||
import { AgentName } from '../../../../../../../typings/es_schemas/ui/fields/agent';
|
||||
import { history } from '../../../../../../utils/history';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useUiTracker } from '../../../../../../../../observability/public';
|
||||
import { getOptionLabel } from '../../../../../../../common/agent_configuration/all_option';
|
||||
import { AgentConfigurationIntake } from '../../../../../../../common/agent_configuration/configuration_types';
|
||||
import {
|
||||
filterByAgent,
|
||||
settingDefinitions,
|
||||
validateSetting,
|
||||
} from '../../../../../../../common/agent_configuration/setting_definitions';
|
||||
import { saveConfig } from './saveConfig';
|
||||
import { AgentName } from '../../../../../../../typings/es_schemas/ui/fields/agent';
|
||||
import { useApmPluginContext } from '../../../../../../hooks/useApmPluginContext';
|
||||
import { useUiTracker } from '../../../../../../../../observability/public';
|
||||
import { FETCH_STATUS } from '../../../../../../hooks/useFetcher';
|
||||
import { saveConfig } from './saveConfig';
|
||||
import { SettingFormRow } from './SettingFormRow';
|
||||
import { getOptionLabel } from '../../../../../../../common/agent_configuration/all_option';
|
||||
|
||||
function removeEmpty(obj: { [key: string]: any }) {
|
||||
return Object.fromEntries(
|
||||
|
@ -60,6 +60,7 @@ export function SettingsPage({
|
|||
isEditMode: boolean;
|
||||
onClickEdit: () => void;
|
||||
}) {
|
||||
const history = useHistory();
|
||||
// get a telemetry UI event tracker
|
||||
const trackApmEvent = useUiTracker({ app: 'apm' });
|
||||
const { toasts } = useApmPluginContext().core.notifications;
|
||||
|
|
|
@ -4,19 +4,20 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { isEmpty } from 'lodash';
|
||||
import { EuiTitle, EuiText, EuiSpacer } from '@elastic/eui';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FetcherResult } from '../../../../../hooks/useFetcher';
|
||||
import { history } from '../../../../../utils/history';
|
||||
import { History } from 'history';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
AgentConfigurationIntake,
|
||||
AgentConfiguration,
|
||||
AgentConfigurationIntake,
|
||||
} from '../../../../../../common/agent_configuration/configuration_types';
|
||||
import { FetcherResult } from '../../../../../hooks/useFetcher';
|
||||
import { fromQuery, toQuery } from '../../../../shared/Links/url_helpers';
|
||||
import { ServicePage } from './ServicePage/ServicePage';
|
||||
import { SettingsPage } from './SettingsPage/SettingsPage';
|
||||
import { fromQuery, toQuery } from '../../../../shared/Links/url_helpers';
|
||||
|
||||
type PageStep = 'choose-service-step' | 'choose-settings-step' | 'review-step';
|
||||
|
||||
|
@ -30,7 +31,7 @@ function getInitialNewConfig(
|
|||
};
|
||||
}
|
||||
|
||||
function setPage(pageStep: PageStep) {
|
||||
function setPage(pageStep: PageStep, history: History) {
|
||||
history.push({
|
||||
...history.location,
|
||||
search: fromQuery({
|
||||
|
@ -68,6 +69,7 @@ export function AgentConfigurationCreateEdit({
|
|||
pageStep: PageStep;
|
||||
existingConfigResult?: FetcherResult<AgentConfiguration>;
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const existingConfig = existingConfigResult?.data;
|
||||
const isEditMode = Boolean(existingConfigResult);
|
||||
const [newConfig, setNewConfig] = useState<AgentConfigurationIntake>(
|
||||
|
@ -90,7 +92,7 @@ export function AgentConfigurationCreateEdit({
|
|||
useEffect(() => {
|
||||
// the user tried to edit the service of an existing config
|
||||
if (pageStep === 'choose-service-step' && isEditMode) {
|
||||
setPage('choose-settings-step');
|
||||
setPage('choose-settings-step', history);
|
||||
}
|
||||
|
||||
// the user skipped the first step (select service)
|
||||
|
@ -99,9 +101,9 @@ export function AgentConfigurationCreateEdit({
|
|||
!isEditMode &&
|
||||
isEmpty(newConfig.service)
|
||||
) {
|
||||
setPage('choose-service-step');
|
||||
setPage('choose-service-step', history);
|
||||
}
|
||||
}, [isEditMode, newConfig, pageStep]);
|
||||
}, [history, isEditMode, newConfig, pageStep]);
|
||||
|
||||
const unsavedChanges = getUnsavedChanges({ newConfig, existingConfig });
|
||||
|
||||
|
@ -135,7 +137,7 @@ export function AgentConfigurationCreateEdit({
|
|||
setNewConfig={setNewConfig}
|
||||
onClickNext={() => {
|
||||
resetSettings();
|
||||
setPage('choose-settings-step');
|
||||
setPage('choose-settings-step', history);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
@ -144,7 +146,7 @@ export function AgentConfigurationCreateEdit({
|
|||
<SettingsPage
|
||||
status={existingConfigResult?.status}
|
||||
unsavedChanges={unsavedChanges}
|
||||
onClickEdit={() => setPage('choose-service-step')}
|
||||
onClickEdit={() => setPage('choose-service-step', history)}
|
||||
newConfig={newConfig}
|
||||
setNewConfig={setNewConfig}
|
||||
resetSettings={resetSettings}
|
||||
|
|
|
@ -4,30 +4,32 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiEmptyPrompt,
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiButtonIcon,
|
||||
EuiEmptyPrompt,
|
||||
EuiHealth,
|
||||
EuiToolTip,
|
||||
EuiButtonIcon,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useTheme } from '../../../../../hooks/useTheme';
|
||||
import { FETCH_STATUS } from '../../../../../hooks/useFetcher';
|
||||
import { ITableColumn, ManagedTable } from '../../../../shared/ManagedTable';
|
||||
import { LoadingStatePrompt } from '../../../../shared/LoadingStatePrompt';
|
||||
import React, { useState } from 'react';
|
||||
import { getOptionLabel } from '../../../../../../common/agent_configuration/all_option';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { AgentConfigurationListAPIResponse } from '../../../../../../server/lib/settings/agent_configuration/list_configurations';
|
||||
import { TimestampTooltip } from '../../../../shared/TimestampTooltip';
|
||||
import { useApmPluginContext } from '../../../../../hooks/useApmPluginContext';
|
||||
import { FETCH_STATUS } from '../../../../../hooks/useFetcher';
|
||||
import { useLocation } from '../../../../../hooks/useLocation';
|
||||
import { useTheme } from '../../../../../hooks/useTheme';
|
||||
import { px, units } from '../../../../../style/variables';
|
||||
import { getOptionLabel } from '../../../../../../common/agent_configuration/all_option';
|
||||
import {
|
||||
createAgentConfigurationHref,
|
||||
editAgentConfigurationHref,
|
||||
} from '../../../../shared/Links/apm/agentConfigurationLinks';
|
||||
import { LoadingStatePrompt } from '../../../../shared/LoadingStatePrompt';
|
||||
import { ITableColumn, ManagedTable } from '../../../../shared/ManagedTable';
|
||||
import { TimestampTooltip } from '../../../../shared/TimestampTooltip';
|
||||
import { ConfirmDeleteModal } from './ConfirmDeleteModal';
|
||||
|
||||
type Config = AgentConfigurationListAPIResponse[0];
|
||||
|
@ -39,6 +41,9 @@ interface Props {
|
|||
}
|
||||
|
||||
export function AgentConfigurationList({ status, data, refetch }: Props) {
|
||||
const { core } = useApmPluginContext();
|
||||
const { basePath } = core.http;
|
||||
const { search } = useLocation();
|
||||
const theme = useTheme();
|
||||
const [configToBeDeleted, setConfigToBeDeleted] = useState<Config | null>(
|
||||
null
|
||||
|
@ -69,7 +74,11 @@ export function AgentConfigurationList({ status, data, refetch }: Props) {
|
|||
</>
|
||||
}
|
||||
actions={
|
||||
<EuiButton color="primary" fill href={createAgentConfigurationHref()}>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
fill
|
||||
href={createAgentConfigurationHref(search, basePath)}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.apm.agentConfig.configTable.createConfigButtonLabel',
|
||||
{ defaultMessage: 'Create configuration' }
|
||||
|
@ -145,7 +154,7 @@ export function AgentConfigurationList({ status, data, refetch }: Props) {
|
|||
flush="left"
|
||||
size="s"
|
||||
color="primary"
|
||||
href={editAgentConfigurationHref(config.service)}
|
||||
href={editAgentConfigurationHref(config.service, search, basePath)}
|
||||
>
|
||||
{getOptionLabel(config.service.name)}
|
||||
</EuiButtonEmpty>
|
||||
|
@ -179,7 +188,7 @@ export function AgentConfigurationList({ status, data, refetch }: Props) {
|
|||
<EuiButtonIcon
|
||||
aria-label="Edit"
|
||||
iconType="pencil"
|
||||
href={editAgentConfigurationHref(config.service)}
|
||||
href={editAgentConfigurationHref(config.service, search, basePath)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
|
|
@ -4,21 +4,23 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiTitle,
|
||||
EuiButton,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiButton,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { useFetcher } from '../../../../hooks/useFetcher';
|
||||
import { AgentConfigurationList } from './List';
|
||||
import React from 'react';
|
||||
import { useTrackPageview } from '../../../../../../observability/public';
|
||||
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
|
||||
import { useFetcher } from '../../../../hooks/useFetcher';
|
||||
import { useLocation } from '../../../../hooks/useLocation';
|
||||
import { createAgentConfigurationHref } from '../../../shared/Links/apm/agentConfigurationLinks';
|
||||
import { AgentConfigurationList } from './List';
|
||||
|
||||
export function AgentConfigurations() {
|
||||
const { refetch, data = [], status } = useFetcher(
|
||||
|
@ -60,7 +62,10 @@ export function AgentConfigurations() {
|
|||
}
|
||||
|
||||
function CreateConfigurationButton() {
|
||||
const href = createAgentConfigurationHref();
|
||||
const { core } = useApmPluginContext();
|
||||
const { basePath } = core.http;
|
||||
const { search } = useLocation();
|
||||
const href = createAgentConfigurationHref(search, basePath);
|
||||
return (
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup alignItems="center" justifyContent="flexEnd">
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Settings } from './';
|
||||
import { LocationContext } from '../../../context/LocationContext';
|
||||
import { createMemoryHistory } from 'history';
|
||||
|
||||
function Wrapper({ children }: { children?: ReactNode }) {
|
||||
const { location } = createMemoryHistory();
|
||||
return (
|
||||
<LocationContext.Provider value={location}>
|
||||
<MockApmPluginContextWrapper>{children}</MockApmPluginContextWrapper>
|
||||
</LocationContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
describe('Settings', () => {
|
||||
it('renders', async () => {
|
||||
expect(() =>
|
||||
render(
|
||||
<Settings>
|
||||
<div />
|
||||
</Settings>,
|
||||
{ wrapper: Wrapper }
|
||||
)
|
||||
).not.toThrowError();
|
||||
});
|
||||
});
|
|
@ -19,9 +19,15 @@ import { getAPMHref } from '../../shared/Links/apm/APMLink';
|
|||
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
|
||||
|
||||
export function Settings(props: { children: ReactNode }) {
|
||||
const plugin = useApmPluginContext();
|
||||
const canAccessML = !!plugin.core.application.capabilities.ml?.canAccessML;
|
||||
const { core } = useApmPluginContext();
|
||||
const { basePath } = core.http;
|
||||
const canAccessML = !!core.application.capabilities.ml?.canAccessML;
|
||||
const { search, pathname } = useLocation();
|
||||
|
||||
function getSettingsHref(path: string) {
|
||||
return getAPMHref({ basePath, path: `/settings${path}`, search });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HomeLink>
|
||||
|
@ -46,7 +52,7 @@ export function Settings(props: { children: ReactNode }) {
|
|||
defaultMessage: 'Agent Configuration',
|
||||
}),
|
||||
id: '1',
|
||||
href: getAPMHref('/settings/agent-configuration', search),
|
||||
href: getSettingsHref('/agent-configuration'),
|
||||
isSelected: pathname.startsWith(
|
||||
'/settings/agent-configuration'
|
||||
),
|
||||
|
@ -61,10 +67,7 @@ export function Settings(props: { children: ReactNode }) {
|
|||
}
|
||||
),
|
||||
id: '4',
|
||||
href: getAPMHref(
|
||||
'/settings/anomaly-detection',
|
||||
search
|
||||
),
|
||||
href: getSettingsHref('/anomaly-detection'),
|
||||
isSelected:
|
||||
pathname === '/settings/anomaly-detection',
|
||||
},
|
||||
|
@ -75,7 +78,7 @@ export function Settings(props: { children: ReactNode }) {
|
|||
defaultMessage: 'Customize app',
|
||||
}),
|
||||
id: '3',
|
||||
href: getAPMHref('/settings/customize-ui', search),
|
||||
href: getSettingsHref('/customize-ui'),
|
||||
isSelected: pathname === '/settings/customize-ui',
|
||||
},
|
||||
{
|
||||
|
@ -83,7 +86,7 @@ export function Settings(props: { children: ReactNode }) {
|
|||
defaultMessage: 'Indices',
|
||||
}),
|
||||
id: '2',
|
||||
href: getAPMHref('/settings/apm-indices', search),
|
||||
href: getSettingsHref('/apm-indices'),
|
||||
isSelected: pathname === '/settings/apm-indices',
|
||||
},
|
||||
],
|
||||
|
|
|
@ -9,13 +9,13 @@ import { i18n } from '@kbn/i18n';
|
|||
import d3 from 'd3';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { TransactionDistributionAPIResponse } from '../../../../../server/lib/transactions/distribution';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { IBucket } from '../../../../../server/lib/transactions/distribution/get_buckets/transform';
|
||||
import { IUrlParams } from '../../../../context/UrlParamsContext/types';
|
||||
import { getDurationFormatter } from '../../../../utils/formatters';
|
||||
import { history } from '../../../../utils/history';
|
||||
// @ts-ignore
|
||||
import Histogram from '../../../shared/charts/Histogram';
|
||||
import { EmptyMessage } from '../../../shared/EmptyMessage';
|
||||
|
@ -106,6 +106,7 @@ export function TransactionDistribution(props: Props) {
|
|||
isLoading,
|
||||
bucketIndex,
|
||||
} = props;
|
||||
const history = useHistory();
|
||||
|
||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||
const formatYShort = useCallback(getFormatYShort(transactionType), [
|
||||
|
|
|
@ -8,10 +8,10 @@ import { EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { Location } from 'history';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
|
||||
import { IUrlParams } from '../../../../context/UrlParamsContext/types';
|
||||
import { fromQuery, toQuery } from '../../../shared/Links/url_helpers';
|
||||
import { history } from '../../../../utils/history';
|
||||
import { TransactionMetadata } from '../../../shared/MetadataTable/TransactionMetadata';
|
||||
import { WaterfallContainer } from './WaterfallContainer';
|
||||
import { IWaterfall } from './WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers';
|
||||
|
@ -45,6 +45,7 @@ export function TransactionTabs({
|
|||
waterfall,
|
||||
exceedsMax,
|
||||
}: Props) {
|
||||
const history = useHistory();
|
||||
const tabs = [timelineTab, metadataTab];
|
||||
const currentTab =
|
||||
urlParams.detailTab === metadataTab.key ? metadataTab : timelineTab;
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { Location } from 'history';
|
||||
import { History, Location } from 'history';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { SpanFlyout } from './SpanFlyout';
|
||||
import { TransactionFlyout } from './TransactionFlyout';
|
||||
import { IWaterfall } from './waterfall_helpers/waterfall_helpers';
|
||||
|
@ -13,7 +14,13 @@ interface Props {
|
|||
waterfallItemId?: string;
|
||||
waterfall: IWaterfall;
|
||||
location: Location;
|
||||
toggleFlyout: ({ location }: { location: Location }) => void;
|
||||
toggleFlyout: ({
|
||||
history,
|
||||
location,
|
||||
}: {
|
||||
history: History;
|
||||
location: Location;
|
||||
}) => void;
|
||||
}
|
||||
|
||||
export function WaterfallFlyout({
|
||||
|
@ -22,6 +29,7 @@ export function WaterfallFlyout({
|
|||
location,
|
||||
toggleFlyout,
|
||||
}: Props) {
|
||||
const history = useHistory();
|
||||
const currentItem = waterfall.items.find(
|
||||
(item) => item.id === waterfallItemId
|
||||
);
|
||||
|
@ -42,14 +50,14 @@ export function WaterfallFlyout({
|
|||
totalDuration={waterfall.duration}
|
||||
span={currentItem.doc}
|
||||
parentTransaction={parentTransaction}
|
||||
onClose={() => toggleFlyout({ location })}
|
||||
onClose={() => toggleFlyout({ history, location })}
|
||||
/>
|
||||
);
|
||||
case 'transaction':
|
||||
return (
|
||||
<TransactionFlyout
|
||||
transaction={currentItem.doc}
|
||||
onClose={() => toggleFlyout({ location })}
|
||||
onClose={() => toggleFlyout({ history, location })}
|
||||
rootTransactionDuration={
|
||||
waterfall.rootTransaction?.transaction.duration.us
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
import { EuiButtonEmpty, EuiCallOut } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Location } from 'history';
|
||||
import { History, Location } from 'history';
|
||||
import React, { useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
// @ts-ignore
|
||||
import { StickyContainer } from 'react-sticky';
|
||||
import styled from 'styled-components';
|
||||
import { px } from '../../../../../../style/variables';
|
||||
import { history } from '../../../../../../utils/history';
|
||||
import { Timeline } from '../../../../../shared/charts/Timeline';
|
||||
import { HeightRetainer } from '../../../../../shared/HeightRetainer';
|
||||
import { fromQuery, toQuery } from '../../../../../shared/Links/url_helpers';
|
||||
|
@ -39,9 +39,11 @@ const TIMELINE_MARGINS = {
|
|||
};
|
||||
|
||||
const toggleFlyout = ({
|
||||
history,
|
||||
item,
|
||||
location,
|
||||
}: {
|
||||
history: History;
|
||||
item?: IWaterfallItem;
|
||||
location: Location;
|
||||
}) => {
|
||||
|
@ -74,6 +76,7 @@ export function Waterfall({
|
|||
waterfallItemId,
|
||||
location,
|
||||
}: Props) {
|
||||
const history = useHistory();
|
||||
const [isAccordionOpen, setIsAccordionOpen] = useState(true);
|
||||
const itemContainerHeight = 58; // TODO: This is a nasty way to calculate the height of the svg element. A better approach should be found
|
||||
const waterfallHeight = itemContainerHeight * waterfall.items.length;
|
||||
|
@ -105,7 +108,7 @@ export function Waterfall({
|
|||
childrenByParentId={childrenByParentId}
|
||||
timelineMargins={TIMELINE_MARGINS}
|
||||
onClickWaterfallItem={(item: IWaterfallItem) =>
|
||||
toggleFlyout({ item, location })
|
||||
toggleFlyout({ history, item, location })
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -16,10 +16,10 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { Location } from 'history';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { IBucket } from '../../../../../server/lib/transactions/distribution/get_buckets/transform';
|
||||
import { IUrlParams } from '../../../../context/UrlParamsContext/types';
|
||||
import { history } from '../../../../utils/history';
|
||||
import { fromQuery, toQuery } from '../../../shared/Links/url_helpers';
|
||||
import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt';
|
||||
import { TransactionSummary } from '../../../shared/Summary/TransactionSummary';
|
||||
|
@ -45,6 +45,7 @@ export function WaterfallWithSummmary({
|
|||
isLoading,
|
||||
traceSamples,
|
||||
}: Props) {
|
||||
const history = useHistory();
|
||||
const [sampleActivePage, setSampleActivePage] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -10,22 +10,29 @@ import {
|
|||
queryByLabelText,
|
||||
render,
|
||||
} from '@testing-library/react';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { omit } from 'lodash';
|
||||
import React from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { TransactionOverview } from './';
|
||||
import { createKibanaReactContext } from 'src/plugins/kibana_react/public';
|
||||
import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { UrlParamsProvider } from '../../../context/UrlParamsContext';
|
||||
import { IUrlParams } from '../../../context/UrlParamsContext/types';
|
||||
import * as useFetcherHook from '../../../hooks/useFetcher';
|
||||
import * as useServiceTransactionTypesHook from '../../../hooks/useServiceTransactionTypes';
|
||||
import { history } from '../../../utils/history';
|
||||
import { disableConsoleWarning } from '../../../utils/testHelpers';
|
||||
import { fromQuery } from '../../shared/Links/url_helpers';
|
||||
import { TransactionOverview } from './';
|
||||
|
||||
const KibanaReactContext = createKibanaReactContext({
|
||||
usageCollection: { reportUiStats: () => {} },
|
||||
} as Partial<CoreStart>);
|
||||
|
||||
const history = createMemoryHistory();
|
||||
jest.spyOn(history, 'push');
|
||||
jest.spyOn(history, 'replace');
|
||||
|
||||
jest.mock('ui/new_platform');
|
||||
function setup({
|
||||
urlParams,
|
||||
serviceTransactionTypes,
|
||||
|
@ -49,17 +56,29 @@ function setup({
|
|||
jest.spyOn(useFetcherHook, 'useFetcher').mockReturnValue({} as any);
|
||||
|
||||
return render(
|
||||
<MockApmPluginContextWrapper>
|
||||
<Router history={history}>
|
||||
<UrlParamsProvider>
|
||||
<TransactionOverview />
|
||||
</UrlParamsProvider>
|
||||
</Router>
|
||||
</MockApmPluginContextWrapper>
|
||||
<KibanaReactContext.Provider>
|
||||
<MockApmPluginContextWrapper>
|
||||
<Router history={history}>
|
||||
<UrlParamsProvider>
|
||||
<TransactionOverview />
|
||||
</UrlParamsProvider>
|
||||
</Router>
|
||||
</MockApmPluginContextWrapper>
|
||||
</KibanaReactContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
describe('TransactionOverview', () => {
|
||||
let consoleMock: jest.SpyInstance;
|
||||
|
||||
beforeAll(() => {
|
||||
consoleMock = disableConsoleWarning('Warning: componentWillReceiveProps');
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
consoleMock.mockRestore();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
|
|
@ -5,40 +5,39 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
EuiCallOut,
|
||||
EuiCode,
|
||||
EuiFlexGrid,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiHorizontalRule,
|
||||
EuiCallOut,
|
||||
EuiCode,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { Location } from 'history';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { Location } from 'history';
|
||||
import { first } from 'lodash';
|
||||
import React, { useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiFlexGrid } from '@elastic/eui';
|
||||
import { useTransactionList } from '../../../hooks/useTransactionList';
|
||||
import { useTransactionCharts } from '../../../hooks/useTransactionCharts';
|
||||
import { IUrlParams } from '../../../context/UrlParamsContext/types';
|
||||
import { TransactionCharts } from '../../shared/charts/TransactionCharts';
|
||||
import { ErroneousTransactionsRateChart } from '../../shared/charts/ErroneousTransactionsRateChart';
|
||||
import { TransactionBreakdown } from '../../shared/TransactionBreakdown';
|
||||
import { TransactionList } from './List';
|
||||
import { ElasticDocsLink } from '../../shared/Links/ElasticDocsLink';
|
||||
import { useRedirect } from './useRedirect';
|
||||
import { history } from '../../../utils/history';
|
||||
import { useLocation } from '../../../hooks/useLocation';
|
||||
import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
|
||||
import { useTrackPageview } from '../../../../../observability/public';
|
||||
import { Projection } from '../../../../common/projections';
|
||||
import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext';
|
||||
import { IUrlParams } from '../../../context/UrlParamsContext/types';
|
||||
import { useLocation } from '../../../hooks/useLocation';
|
||||
import { useServiceTransactionTypes } from '../../../hooks/useServiceTransactionTypes';
|
||||
import { useTransactionCharts } from '../../../hooks/useTransactionCharts';
|
||||
import { useTransactionList } from '../../../hooks/useTransactionList';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { ErroneousTransactionsRateChart } from '../../shared/charts/ErroneousTransactionsRateChart';
|
||||
import { TransactionCharts } from '../../shared/charts/TransactionCharts';
|
||||
import { ElasticDocsLink } from '../../shared/Links/ElasticDocsLink';
|
||||
import { fromQuery, toQuery } from '../../shared/Links/url_helpers';
|
||||
import { LocalUIFilters } from '../../shared/LocalUIFilters';
|
||||
import { Projection } from '../../../../common/projections';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { useServiceTransactionTypes } from '../../../hooks/useServiceTransactionTypes';
|
||||
import { TransactionTypeFilter } from '../../shared/LocalUIFilters/TransactionTypeFilter';
|
||||
import { TransactionBreakdown } from '../../shared/TransactionBreakdown';
|
||||
import { TransactionList } from './List';
|
||||
import { useRedirect } from './useRedirect';
|
||||
|
||||
function getRedirectLocation({
|
||||
urlParams,
|
||||
|
@ -73,7 +72,6 @@ export function TransactionOverview() {
|
|||
|
||||
// redirect to first transaction type
|
||||
useRedirect(
|
||||
history,
|
||||
getRedirectLocation({
|
||||
urlParams,
|
||||
location,
|
||||
|
|
|
@ -4,13 +4,16 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { History, Location } from 'history';
|
||||
import { Location } from 'history';
|
||||
import { useEffect } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
export function useRedirect(redirectLocation?: Location) {
|
||||
const history = useHistory();
|
||||
|
||||
export function useRedirect(history: History, redirectLocation?: Location) {
|
||||
useEffect(() => {
|
||||
if (redirectLocation) {
|
||||
history.replace(redirectLocation);
|
||||
}
|
||||
});
|
||||
}, [history, redirectLocation]);
|
||||
}
|
||||
|
|
|
@ -4,21 +4,22 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiSuperDatePicker } from '@elastic/eui';
|
||||
import { wait } from '@testing-library/react';
|
||||
import { mount } from 'enzyme';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { LocationProvider } from '../../../../context/LocationContext';
|
||||
import {
|
||||
UrlParamsContext,
|
||||
useUiFilters,
|
||||
} from '../../../../context/UrlParamsContext';
|
||||
import { DatePicker } from '../index';
|
||||
import { IUrlParams } from '../../../../context/UrlParamsContext/types';
|
||||
import { history } from '../../../../utils/history';
|
||||
import { mount } from 'enzyme';
|
||||
import { EuiSuperDatePicker } from '@elastic/eui';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { wait } from '@testing-library/react';
|
||||
import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { DatePicker } from '../index';
|
||||
|
||||
const history = createMemoryHistory();
|
||||
const mockHistoryPush = jest.spyOn(history, 'push');
|
||||
const mockHistoryReplace = jest.spyOn(history, 'replace');
|
||||
const mockRefreshTimeRange = jest.fn();
|
||||
|
@ -44,13 +45,13 @@ function MockUrlParamsProvider({
|
|||
function mountDatePicker(params?: IUrlParams) {
|
||||
return mount(
|
||||
<MockApmPluginContextWrapper>
|
||||
<MemoryRouter initialEntries={[history.location]}>
|
||||
<Router history={history}>
|
||||
<LocationProvider>
|
||||
<MockUrlParamsProvider params={params}>
|
||||
<DatePicker />
|
||||
</MockUrlParamsProvider>
|
||||
</LocationProvider>
|
||||
</MemoryRouter>
|
||||
</Router>
|
||||
</MockApmPluginContextWrapper>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
*/
|
||||
|
||||
import { EuiSuperDatePicker } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { isEmpty, isEqual, pickBy } from 'lodash';
|
||||
import { fromQuery, toQuery } from '../Links/url_helpers';
|
||||
import { history } from '../../../utils/history';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { UI_SETTINGS } from '../../../../../../../src/plugins/data/common';
|
||||
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
|
||||
import { useLocation } from '../../../hooks/useLocation';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { clearCache } from '../../../services/rest/callApi';
|
||||
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
|
||||
import { UI_SETTINGS } from '../../../../../../../src/plugins/data/common';
|
||||
import { fromQuery, toQuery } from '../Links/url_helpers';
|
||||
import { TimePickerQuickRange, TimePickerTimeDefaults } from './typings';
|
||||
|
||||
function removeUndefinedAndEmptyProps<T extends object>(obj: T): Partial<T> {
|
||||
|
@ -21,6 +21,7 @@ function removeUndefinedAndEmptyProps<T extends object>(obj: T): Partial<T> {
|
|||
}
|
||||
|
||||
export function DatePicker() {
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const { core } = useApmPluginContext();
|
||||
|
||||
|
|
|
@ -6,18 +6,20 @@
|
|||
|
||||
import { EuiSelect } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { History } from 'history';
|
||||
import React from 'react';
|
||||
import { useLocation } from '../../../hooks/useLocation';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { history } from '../../../utils/history';
|
||||
import { fromQuery, toQuery } from '../Links/url_helpers';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
ENVIRONMENT_ALL,
|
||||
ENVIRONMENT_NOT_DEFINED,
|
||||
} from '../../../../common/environment_filter_values';
|
||||
import { useEnvironments } from '../../../hooks/useEnvironments';
|
||||
import { useLocation } from '../../../hooks/useLocation';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { fromQuery, toQuery } from '../Links/url_helpers';
|
||||
|
||||
function updateEnvironmentUrl(
|
||||
history: History,
|
||||
location: ReturnType<typeof useLocation>,
|
||||
environment?: string
|
||||
) {
|
||||
|
@ -59,6 +61,7 @@ function getOptions(environments: string[]) {
|
|||
}
|
||||
|
||||
export function EnvironmentFilter() {
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const { uiFilters, urlParams } = useUrlParams();
|
||||
|
||||
|
@ -78,7 +81,7 @@ export function EnvironmentFilter() {
|
|||
options={getOptions(environments)}
|
||||
value={environment || ENVIRONMENT_ALL.value}
|
||||
onChange={(event) => {
|
||||
updateEnvironmentUrl(location, event.target.value);
|
||||
updateEnvironmentUrl(history, location, event.target.value);
|
||||
}}
|
||||
isLoading={status === 'loading'}
|
||||
/>
|
||||
|
|
|
@ -4,24 +4,24 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { uniqueId, startsWith } from 'lodash';
|
||||
import styled from 'styled-components';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { fromQuery, toQuery } from '../Links/url_helpers';
|
||||
// @ts-expect-error
|
||||
import { Typeahead } from './Typeahead';
|
||||
import { getBoolFilter } from './get_bool_filter';
|
||||
import { useLocation } from '../../../hooks/useLocation';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { history } from '../../../utils/history';
|
||||
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
|
||||
import { useDynamicIndexPattern } from '../../../hooks/useDynamicIndexPattern';
|
||||
import { startsWith, uniqueId } from 'lodash';
|
||||
import React, { useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
QuerySuggestion,
|
||||
esKuery,
|
||||
IIndexPattern,
|
||||
QuerySuggestion,
|
||||
} from '../../../../../../../src/plugins/data/public';
|
||||
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
|
||||
import { useDynamicIndexPattern } from '../../../hooks/useDynamicIndexPattern';
|
||||
import { useLocation } from '../../../hooks/useLocation';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { fromQuery, toQuery } from '../Links/url_helpers';
|
||||
import { getBoolFilter } from './get_bool_filter';
|
||||
// @ts-expect-error
|
||||
import { Typeahead } from './Typeahead';
|
||||
|
||||
const Container = styled.div`
|
||||
margin-bottom: 10px;
|
||||
|
@ -38,6 +38,7 @@ function convertKueryToEsQuery(kuery: string, indexPattern: IIndexPattern) {
|
|||
}
|
||||
|
||||
export function KueryBar() {
|
||||
const history = useHistory();
|
||||
const [state, setState] = useState<State>({
|
||||
suggestions: [],
|
||||
isLoadingSuggestions: false,
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { Location } from 'history';
|
||||
import { IBasePath } from 'kibana/public';
|
||||
import React from 'react';
|
||||
import url from 'url';
|
||||
import rison, { RisonValue } from 'rison-node';
|
||||
import { useLocation } from '../../../../hooks/useLocation';
|
||||
import { getTimepickerRisonData } from '../rison_helpers';
|
||||
import url from 'url';
|
||||
import { APM_STATIC_INDEX_PATTERN_ID } from '../../../../../../../../src/plugins/apm_oss/public';
|
||||
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
|
||||
import { AppMountContextBasePath } from '../../../../context/ApmPluginContext';
|
||||
import { useLocation } from '../../../../hooks/useLocation';
|
||||
import { getTimepickerRisonData } from '../rison_helpers';
|
||||
|
||||
interface Props {
|
||||
query: {
|
||||
|
@ -37,7 +37,7 @@ export const getDiscoverHref = ({
|
|||
location,
|
||||
query,
|
||||
}: {
|
||||
basePath: AppMountContextBasePath;
|
||||
basePath: IBasePath;
|
||||
location: Location;
|
||||
query: Props['query'];
|
||||
}) => {
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
*/
|
||||
|
||||
import { EuiLink, EuiLinkAnchorProps } from '@elastic/eui';
|
||||
import { IBasePath } from 'kibana/public';
|
||||
import React from 'react';
|
||||
import url from 'url';
|
||||
import { fromQuery } from './url_helpers';
|
||||
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
|
||||
import { AppMountContextBasePath } from '../../../context/ApmPluginContext';
|
||||
import { InfraAppId } from '../../../../../infra/public';
|
||||
import { useApmPluginContext } from '../../../hooks/useApmPluginContext';
|
||||
import { fromQuery } from './url_helpers';
|
||||
|
||||
interface InfraQueryParams {
|
||||
time?: number;
|
||||
|
@ -33,7 +33,7 @@ export const getInfraHref = ({
|
|||
path,
|
||||
}: {
|
||||
app: InfraAppId;
|
||||
basePath: AppMountContextBasePath;
|
||||
basePath: IBasePath;
|
||||
query: InfraQueryParams;
|
||||
path?: string;
|
||||
}) => {
|
||||
|
|
|
@ -9,49 +9,51 @@ import React from 'react';
|
|||
import { getRenderedHref } from '../../../../utils/testHelpers';
|
||||
import { APMLink } from './APMLink';
|
||||
|
||||
test('APMLink should produce the correct URL', async () => {
|
||||
const href = await getRenderedHref(
|
||||
() => <APMLink path="/some/path" query={{ transactionId: 'blah' }} />,
|
||||
{
|
||||
search:
|
||||
'?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0',
|
||||
} as Location
|
||||
);
|
||||
describe('APMLink', () => {
|
||||
test('APMLink should produce the correct URL', async () => {
|
||||
const href = await getRenderedHref(
|
||||
() => <APMLink path="/some/path" query={{ transactionId: 'blah' }} />,
|
||||
{
|
||||
search:
|
||||
'?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0',
|
||||
} as Location
|
||||
);
|
||||
|
||||
expect(href).toMatchInlineSnapshot(
|
||||
`"#/some/path?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0&transactionId=blah"`
|
||||
);
|
||||
});
|
||||
|
||||
test('APMLink should retain current kuery value if it exists', async () => {
|
||||
const href = await getRenderedHref(
|
||||
() => <APMLink path="/some/path" query={{ transactionId: 'blah' }} />,
|
||||
{
|
||||
search:
|
||||
'?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0',
|
||||
} as Location
|
||||
);
|
||||
|
||||
expect(href).toMatchInlineSnapshot(
|
||||
`"#/some/path?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0&transactionId=blah"`
|
||||
);
|
||||
});
|
||||
|
||||
test('APMLink should overwrite current kuery value if new kuery value is provided', async () => {
|
||||
const href = await getRenderedHref(
|
||||
() => (
|
||||
<APMLink
|
||||
path="/some/path"
|
||||
query={{ kuery: 'host.os~20~3A~20~22linux~22' }}
|
||||
/>
|
||||
),
|
||||
{
|
||||
search:
|
||||
'?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0',
|
||||
} as Location
|
||||
);
|
||||
|
||||
expect(href).toMatchInlineSnapshot(
|
||||
`"#/some/path?kuery=host.os~20~3A~20~22linux~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0"`
|
||||
);
|
||||
expect(href).toMatchInlineSnapshot(
|
||||
`"/basepath/app/apm/some/path?rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0&transactionId=blah"`
|
||||
);
|
||||
});
|
||||
|
||||
test('APMLink should retain current kuery value if it exists', async () => {
|
||||
const href = await getRenderedHref(
|
||||
() => <APMLink path="/some/path" query={{ transactionId: 'blah' }} />,
|
||||
{
|
||||
search:
|
||||
'?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0',
|
||||
} as Location
|
||||
);
|
||||
|
||||
expect(href).toMatchInlineSnapshot(
|
||||
`"/basepath/app/apm/some/path?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0&transactionId=blah"`
|
||||
);
|
||||
});
|
||||
|
||||
test('APMLink should overwrite current kuery value if new kuery value is provided', async () => {
|
||||
const href = await getRenderedHref(
|
||||
() => (
|
||||
<APMLink
|
||||
path="/some/path"
|
||||
query={{ kuery: 'host.os~20~3A~20~22linux~22' }}
|
||||
/>
|
||||
),
|
||||
{
|
||||
search:
|
||||
'?kuery=host.hostname~20~3A~20~22fakehostname~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0',
|
||||
} as Location
|
||||
);
|
||||
|
||||
expect(href).toMatchInlineSnapshot(
|
||||
`"/basepath/app/apm/some/path?kuery=host.os~20~3A~20~22linux~22&rangeFrom=now-5h&rangeTo=now-2h&refreshPaused=true&refreshInterval=0"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
*/
|
||||
|
||||
import { EuiLink, EuiLinkAnchorProps } from '@elastic/eui';
|
||||
import { IBasePath } from 'kibana/public';
|
||||
import { pick } from 'lodash';
|
||||
import React from 'react';
|
||||
import url from 'url';
|
||||
import { pick } from 'lodash';
|
||||
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
|
||||
import { useLocation } from '../../../../hooks/useLocation';
|
||||
import { APMQueryParams, toQuery, fromQuery } from '../url_helpers';
|
||||
import { APMQueryParams, fromQuery, toQuery } from '../url_helpers';
|
||||
|
||||
interface Props extends EuiLinkAnchorProps {
|
||||
path?: string;
|
||||
|
@ -28,12 +30,21 @@ export const PERSISTENT_APM_PARAMS = [
|
|||
'environment',
|
||||
];
|
||||
|
||||
export function getAPMHref(
|
||||
path: string,
|
||||
currentSearch: string,
|
||||
query: APMQueryParams = {}
|
||||
) {
|
||||
const currentQuery = toQuery(currentSearch);
|
||||
/**
|
||||
* Get an APM link for a path.
|
||||
*/
|
||||
export function getAPMHref({
|
||||
basePath,
|
||||
path = '',
|
||||
search,
|
||||
query = {},
|
||||
}: {
|
||||
basePath: IBasePath;
|
||||
path?: string;
|
||||
search?: string;
|
||||
query?: APMQueryParams;
|
||||
}) {
|
||||
const currentQuery = toQuery(search);
|
||||
const nextQuery = {
|
||||
...pick(currentQuery, PERSISTENT_APM_PARAMS),
|
||||
...query,
|
||||
|
@ -41,13 +52,16 @@ export function getAPMHref(
|
|||
const nextSearch = fromQuery(nextQuery);
|
||||
|
||||
return url.format({
|
||||
pathname: '',
|
||||
hash: `${path}?${nextSearch}`,
|
||||
pathname: basePath.prepend(`/app/apm${path}`),
|
||||
search: nextSearch,
|
||||
});
|
||||
}
|
||||
|
||||
export function APMLink({ path = '', query, ...rest }: Props) {
|
||||
const { core } = useApmPluginContext();
|
||||
const { search } = useLocation();
|
||||
const href = getAPMHref(path, search, query);
|
||||
const { basePath } = core.http;
|
||||
const href = getAPMHref({ basePath, path, search, query });
|
||||
|
||||
return <EuiLink {...rest} href={href} />;
|
||||
}
|
||||
|
|
|
@ -4,23 +4,35 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getAPMHref } from './APMLink';
|
||||
import { IBasePath } from 'kibana/public';
|
||||
import { AgentConfigurationIntake } from '../../../../../common/agent_configuration/configuration_types';
|
||||
import { history } from '../../../../utils/history';
|
||||
import { getAPMHref } from './APMLink';
|
||||
|
||||
export function editAgentConfigurationHref(
|
||||
configService: AgentConfigurationIntake['service']
|
||||
configService: AgentConfigurationIntake['service'],
|
||||
search: string,
|
||||
basePath: IBasePath
|
||||
) {
|
||||
const { search } = history.location;
|
||||
return getAPMHref('/settings/agent-configuration/edit', search, {
|
||||
// ignoring because `name` has not been added to url params. Related: https://github.com/elastic/kibana/issues/51963
|
||||
// @ts-ignore
|
||||
name: configService.name,
|
||||
environment: configService.environment,
|
||||
return getAPMHref({
|
||||
basePath,
|
||||
path: '/settings/agent-configuration/edit',
|
||||
search,
|
||||
query: {
|
||||
// ignoring because `name` has not been added to url params. Related: https://github.com/elastic/kibana/issues/51963
|
||||
// @ts-expect-error
|
||||
name: configService.name,
|
||||
environment: configService.environment,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function createAgentConfigurationHref() {
|
||||
const { search } = history.location;
|
||||
return getAPMHref('/settings/agent-configuration/create', search);
|
||||
export function createAgentConfigurationHref(
|
||||
search: string,
|
||||
basePath: IBasePath
|
||||
) {
|
||||
return getAPMHref({
|
||||
basePath,
|
||||
path: '/settings/agent-configuration/create',
|
||||
search,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import {
|
||||
EuiTitle,
|
||||
EuiHorizontalRule,
|
||||
EuiSpacer,
|
||||
EuiSelect,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useUrlParams } from '../../../../hooks/useUrlParams';
|
||||
import { history } from '../../../../utils/history';
|
||||
import { fromQuery, toQuery } from '../../Links/url_helpers';
|
||||
|
||||
interface Props {
|
||||
|
@ -22,6 +22,7 @@ interface Props {
|
|||
}
|
||||
|
||||
function ServiceNameFilter({ loading, serviceNames }: Props) {
|
||||
const history = useHistory();
|
||||
const {
|
||||
urlParams: { serviceName },
|
||||
} = useUrlParams();
|
||||
|
@ -31,22 +32,25 @@ function ServiceNameFilter({ loading, serviceNames }: Props) {
|
|||
value: type,
|
||||
}));
|
||||
|
||||
const updateServiceName = (serviceN: string) => {
|
||||
const newLocation = {
|
||||
...history.location,
|
||||
search: fromQuery({
|
||||
...toQuery(history.location.search),
|
||||
serviceName: serviceN,
|
||||
}),
|
||||
};
|
||||
history.push(newLocation);
|
||||
};
|
||||
const updateServiceName = useCallback(
|
||||
(serviceN: string) => {
|
||||
const newLocation = {
|
||||
...history.location,
|
||||
search: fromQuery({
|
||||
...toQuery(history.location.search),
|
||||
serviceName: serviceN,
|
||||
}),
|
||||
};
|
||||
history.push(newLocation);
|
||||
},
|
||||
[history]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!serviceName && serviceNames.length > 0) {
|
||||
updateServiceName(serviceNames[0]);
|
||||
}
|
||||
}, [serviceNames, serviceName]);
|
||||
}, [serviceNames, serviceName, updateServiceName]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiTitle,
|
||||
EuiHorizontalRule,
|
||||
EuiSpacer,
|
||||
EuiRadioGroup,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useUrlParams } from '../../../../hooks/useUrlParams';
|
||||
import { history } from '../../../../utils/history';
|
||||
import { fromQuery, toQuery } from '../../Links/url_helpers';
|
||||
|
||||
interface Props {
|
||||
|
@ -21,6 +21,7 @@ interface Props {
|
|||
}
|
||||
|
||||
function TransactionTypeFilter({ transactionTypes }: Props) {
|
||||
const history = useHistory();
|
||||
const {
|
||||
urlParams: { transactionType },
|
||||
} = useUrlParams();
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui';
|
||||
import { orderBy } from 'lodash';
|
||||
import React, { useMemo, useCallback, ReactNode } from 'react';
|
||||
import React, { ReactNode, useCallback, useMemo } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { history } from '../../../utils/history';
|
||||
import { fromQuery, toQuery } from '../Links/url_helpers';
|
||||
|
||||
// TODO: this should really be imported from EUI
|
||||
|
@ -37,6 +37,7 @@ interface Props<T> {
|
|||
}
|
||||
|
||||
function UnoptimizedManagedTable<T>(props: Props<T>) {
|
||||
const history = useHistory();
|
||||
const {
|
||||
items,
|
||||
columns,
|
||||
|
@ -92,7 +93,7 @@ function UnoptimizedManagedTable<T>(props: Props<T>) {
|
|||
}),
|
||||
});
|
||||
},
|
||||
[]
|
||||
[history]
|
||||
);
|
||||
|
||||
const paginationProps = useMemo(() => {
|
||||
|
|
|
@ -5,31 +5,32 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
EuiFieldSearch,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiIcon,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
EuiFieldSearch,
|
||||
} from '@elastic/eui';
|
||||
import React, { useCallback } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { EuiText } from '@elastic/eui';
|
||||
import { ElasticDocsLink } from '../../shared/Links/ElasticDocsLink';
|
||||
import { HeightRetainer } from '../HeightRetainer';
|
||||
import { Section } from './Section';
|
||||
import { history } from '../../../utils/history';
|
||||
import { fromQuery, toQuery } from '../Links/url_helpers';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useLocation } from '../../../hooks/useLocation';
|
||||
import { useUrlParams } from '../../../hooks/useUrlParams';
|
||||
import { SectionsWithRows, filterSectionsByTerm } from './helper';
|
||||
import { ElasticDocsLink } from '../../shared/Links/ElasticDocsLink';
|
||||
import { HeightRetainer } from '../HeightRetainer';
|
||||
import { fromQuery, toQuery } from '../Links/url_helpers';
|
||||
import { filterSectionsByTerm, SectionsWithRows } from './helper';
|
||||
import { Section } from './Section';
|
||||
|
||||
interface Props {
|
||||
sections: SectionsWithRows;
|
||||
}
|
||||
|
||||
export function MetadataTable({ sections }: Props) {
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const { urlParams } = useUrlParams();
|
||||
const { searchTerm = '' } = urlParams;
|
||||
|
@ -47,7 +48,7 @@ export function MetadataTable({ sections }: Props) {
|
|||
}),
|
||||
});
|
||||
},
|
||||
[location]
|
||||
[history, location]
|
||||
);
|
||||
const noResultFound = Boolean(searchTerm) && isEmpty(filteredSections);
|
||||
return (
|
||||
|
|
|
@ -3,12 +3,17 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render, act, fireEvent } from '@testing-library/react';
|
||||
import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
|
||||
import { CustomLinkPopover } from './CustomLinkPopover';
|
||||
import { expectTextsInDocument } from '../../../../utils/testHelpers';
|
||||
import { act, fireEvent, render } from '@testing-library/react';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { CustomLink } from '../../../../../common/custom_link/custom_link_types';
|
||||
import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
|
||||
import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { expectTextsInDocument } from '../../../../utils/testHelpers';
|
||||
import { CustomLinkPopover } from './CustomLinkPopover';
|
||||
|
||||
function Wrapper({ children }: { children?: ReactNode }) {
|
||||
return <MockApmPluginContextWrapper>{children}</MockApmPluginContextWrapper>;
|
||||
}
|
||||
|
||||
describe('CustomLinkPopover', () => {
|
||||
const customLinks = [
|
||||
|
@ -29,7 +34,8 @@ describe('CustomLinkPopover', () => {
|
|||
transaction={transaction}
|
||||
onCreateCustomLinkClick={jest.fn()}
|
||||
onClose={jest.fn()}
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
expectTextsInDocument(component, ['CUSTOM LINKS', 'Create', 'foo', 'bar']);
|
||||
});
|
||||
|
@ -42,7 +48,8 @@ describe('CustomLinkPopover', () => {
|
|||
transaction={transaction}
|
||||
onCreateCustomLinkClick={jest.fn()}
|
||||
onClose={handleCloseMock}
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
expect(handleCloseMock).not.toHaveBeenCalled();
|
||||
act(() => {
|
||||
|
@ -59,7 +66,8 @@ describe('CustomLinkPopover', () => {
|
|||
transaction={transaction}
|
||||
onCreateCustomLinkClick={handleCreateCustomLinkClickMock}
|
||||
onClose={jest.fn()}
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
expect(handleCreateCustomLinkClickMock).not.toHaveBeenCalled();
|
||||
act(() => {
|
||||
|
|
|
@ -4,18 +4,24 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { render, act, fireEvent } from '@testing-library/react';
|
||||
import { ManageCustomLink } from './ManageCustomLink';
|
||||
import {
|
||||
expectTextsInDocument,
|
||||
expectTextsNotInDocument,
|
||||
} from '../../../../utils/testHelpers';
|
||||
import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
|
||||
function Wrapper({ children }: { children?: ReactNode }) {
|
||||
return <MockApmPluginContextWrapper>{children}</MockApmPluginContextWrapper>;
|
||||
}
|
||||
|
||||
describe('ManageCustomLink', () => {
|
||||
it('renders with create button', () => {
|
||||
const component = render(
|
||||
<ManageCustomLink onCreateCustomLinkClick={jest.fn()} />
|
||||
<ManageCustomLink onCreateCustomLinkClick={jest.fn()} />,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
expect(
|
||||
component.getByLabelText('Custom links settings page')
|
||||
|
@ -27,7 +33,8 @@ describe('ManageCustomLink', () => {
|
|||
<ManageCustomLink
|
||||
onCreateCustomLinkClick={jest.fn()}
|
||||
showCreateCustomLinkButton={false}
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
expect(
|
||||
component.getByLabelText('Custom links settings page')
|
||||
|
@ -39,7 +46,8 @@ describe('ManageCustomLink', () => {
|
|||
const { getByText } = render(
|
||||
<ManageCustomLink
|
||||
onCreateCustomLinkClick={handleCreateCustomLinkClickMock}
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
expect(handleCreateCustomLinkClickMock).not.toHaveBeenCalled();
|
||||
act(() => {
|
||||
|
|
|
@ -4,16 +4,21 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, act, fireEvent } from '@testing-library/react';
|
||||
import { act, fireEvent, render } from '@testing-library/react';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { CustomLink } from '.';
|
||||
import { CustomLink as CustomLinkType } from '../../../../../common/custom_link/custom_link_types';
|
||||
import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
|
||||
import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { FETCH_STATUS } from '../../../../hooks/useFetcher';
|
||||
import {
|
||||
expectTextsInDocument,
|
||||
expectTextsNotInDocument,
|
||||
} from '../../../../utils/testHelpers';
|
||||
import { CustomLink as CustomLinkType } from '../../../../../common/custom_link/custom_link_types';
|
||||
|
||||
function Wrapper({ children }: { children?: ReactNode }) {
|
||||
return <MockApmPluginContextWrapper>{children}</MockApmPluginContextWrapper>;
|
||||
}
|
||||
|
||||
describe('Custom links', () => {
|
||||
it('shows empty message when no custom link is available', () => {
|
||||
|
@ -24,7 +29,8 @@ describe('Custom links', () => {
|
|||
onCreateCustomLinkClick={jest.fn()}
|
||||
onSeeMoreClick={jest.fn()}
|
||||
status={FETCH_STATUS.SUCCESS}
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
|
||||
expectTextsInDocument(component, [
|
||||
|
@ -41,7 +47,8 @@ describe('Custom links', () => {
|
|||
onCreateCustomLinkClick={jest.fn()}
|
||||
onSeeMoreClick={jest.fn()}
|
||||
status={FETCH_STATUS.LOADING}
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
expect(getByTestId('loading-spinner')).toBeInTheDocument();
|
||||
});
|
||||
|
@ -60,7 +67,8 @@ describe('Custom links', () => {
|
|||
onCreateCustomLinkClick={jest.fn()}
|
||||
onSeeMoreClick={jest.fn()}
|
||||
status={FETCH_STATUS.SUCCESS}
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
expectTextsInDocument(component, ['foo', 'bar', 'baz']);
|
||||
expectTextsNotInDocument(component, ['qux']);
|
||||
|
@ -81,7 +89,8 @@ describe('Custom links', () => {
|
|||
onCreateCustomLinkClick={jest.fn()}
|
||||
onSeeMoreClick={onSeeMoreClickMock}
|
||||
status={FETCH_STATUS.SUCCESS}
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
expect(onSeeMoreClickMock).not.toHaveBeenCalled();
|
||||
act(() => {
|
||||
|
@ -99,7 +108,8 @@ describe('Custom links', () => {
|
|||
onCreateCustomLinkClick={jest.fn()}
|
||||
onSeeMoreClick={jest.fn()}
|
||||
status={FETCH_STATUS.SUCCESS}
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
|
||||
expectTextsInDocument(component, ['Create custom link']);
|
||||
|
@ -119,7 +129,8 @@ describe('Custom links', () => {
|
|||
onCreateCustomLinkClick={jest.fn()}
|
||||
onSeeMoreClick={jest.fn()}
|
||||
status={FETCH_STATUS.SUCCESS}
|
||||
/>
|
||||
/>,
|
||||
{ wrapper: Wrapper }
|
||||
);
|
||||
expectTextsInDocument(component, ['Create']);
|
||||
expectTextsNotInDocument(component, ['Create custom link']);
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
|
||||
import { EuiButtonEmpty } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { MouseEvent, useMemo, useState } from 'react';
|
||||
import url from 'url';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
ActionMenu,
|
||||
ActionMenuDivider,
|
||||
|
@ -84,40 +83,7 @@ export function TransactionActionMenu({ transaction }: Props) {
|
|||
basePath: core.http.basePath,
|
||||
location,
|
||||
urlParams,
|
||||
}).map((sectionList) =>
|
||||
sectionList.map((section) => ({
|
||||
...section,
|
||||
actions: section.actions.map((action) => {
|
||||
const { href } = action;
|
||||
|
||||
// use navigateToApp as a temporary workaround for faster navigation between observability apps.
|
||||
// see https://github.com/elastic/kibana/issues/65682
|
||||
|
||||
return {
|
||||
...action,
|
||||
onClick: (event: MouseEvent) => {
|
||||
const parsed = url.parse(href);
|
||||
|
||||
const appPathname = core.http.basePath.remove(
|
||||
parsed.pathname ?? ''
|
||||
);
|
||||
|
||||
const [, , app, ...rest] = appPathname.split('/');
|
||||
|
||||
if (app === 'uptime' || app === 'metrics' || app === 'logs') {
|
||||
event.preventDefault();
|
||||
const search = parsed.search || '';
|
||||
|
||||
const path = `${rest.join('/')}${search}`;
|
||||
core.application.navigateToApp(app, {
|
||||
path,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}),
|
||||
}))
|
||||
);
|
||||
});
|
||||
|
||||
const closePopover = () => {
|
||||
setIsActionPopoverOpen(false);
|
||||
|
@ -186,7 +152,6 @@ export function TransactionActionMenu({ transaction }: Props) {
|
|||
key={action.key}
|
||||
label={action.label}
|
||||
href={action.href}
|
||||
onClick={action.onClick}
|
||||
/>
|
||||
))}
|
||||
</SectionLinks>
|
||||
|
|
|
@ -4,54 +4,27 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { act, fireEvent, render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { render, fireEvent, act } from '@testing-library/react';
|
||||
import { merge, tail } from 'lodash';
|
||||
import { TransactionActionMenu } from '../TransactionActionMenu';
|
||||
import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
|
||||
import * as Transactions from './mockData';
|
||||
import {
|
||||
expectTextsNotInDocument,
|
||||
expectTextsInDocument,
|
||||
} from '../../../../utils/testHelpers';
|
||||
import * as hooks from '../../../../hooks/useFetcher';
|
||||
import { LicenseContext } from '../../../../context/LicenseContext';
|
||||
import { License } from '../../../../../../licensing/common/license';
|
||||
import {
|
||||
MockApmPluginContextWrapper,
|
||||
mockApmPluginContextValue,
|
||||
} from '../../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
|
||||
import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import { LicenseContext } from '../../../../context/LicenseContext';
|
||||
import * as hooks from '../../../../hooks/useFetcher';
|
||||
import * as apmApi from '../../../../services/rest/createCallApmApi';
|
||||
import { ApmPluginContextValue } from '../../../../context/ApmPluginContext';
|
||||
import {
|
||||
expectTextsInDocument,
|
||||
expectTextsNotInDocument,
|
||||
} from '../../../../utils/testHelpers';
|
||||
import { TransactionActionMenu } from '../TransactionActionMenu';
|
||||
import * as Transactions from './mockData';
|
||||
|
||||
const getMock = () => {
|
||||
return (merge({}, mockApmPluginContextValue, {
|
||||
core: {
|
||||
application: {
|
||||
navigateToApp: jest.fn(),
|
||||
},
|
||||
http: {
|
||||
basePath: {
|
||||
remove: jest.fn((path: string) => {
|
||||
return tail(path.split('/')).join('/');
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}) as unknown) as ApmPluginContextValue;
|
||||
};
|
||||
|
||||
const renderTransaction = async (
|
||||
transaction: Record<string, any>,
|
||||
mock: ApmPluginContextValue = getMock()
|
||||
) => {
|
||||
const renderTransaction = async (transaction: Record<string, any>) => {
|
||||
const rendered = render(
|
||||
<TransactionActionMenu transaction={transaction as Transaction} />,
|
||||
{
|
||||
wrapper: ({ children }: { children?: React.ReactNode }) => (
|
||||
<MockApmPluginContextWrapper value={mock}>
|
||||
{children}
|
||||
</MockApmPluginContextWrapper>
|
||||
<MockApmPluginContextWrapper>{children}</MockApmPluginContextWrapper>
|
||||
),
|
||||
}
|
||||
);
|
||||
|
@ -80,138 +53,148 @@ describe('TransactionActionMenu component', () => {
|
|||
expect(queryByText('View sample document')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should always render the trace logs link', async () => {
|
||||
const mock = getMock();
|
||||
|
||||
const { queryByText, getByText } = await renderTransaction(
|
||||
Transactions.transactionWithMinimalData,
|
||||
mock
|
||||
);
|
||||
|
||||
expect(queryByText('Trace logs')).not.toBeNull();
|
||||
|
||||
fireEvent.click(getByText('Trace logs'));
|
||||
|
||||
expect(mock.core.application.navigateToApp).toHaveBeenCalledWith('logs', {
|
||||
path:
|
||||
'link-to/logs?time=1545092070952&filter=trace.id:%228b60bd32ecc6e1506735a8b6cfcf175c%22%20OR%20%228b60bd32ecc6e1506735a8b6cfcf175c%22',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not render the pod links when there is no pod id', async () => {
|
||||
const { queryByText } = await renderTransaction(
|
||||
it('always renders the trace logs link', async () => {
|
||||
const { getByText } = await renderTransaction(
|
||||
Transactions.transactionWithMinimalData
|
||||
);
|
||||
|
||||
expect(queryByText('Pod logs')).toBeNull();
|
||||
expect(queryByText('Pod metrics')).toBeNull();
|
||||
expect(
|
||||
(getByText('Trace logs').parentElement as HTMLAnchorElement).href
|
||||
).toEqual(
|
||||
'http://localhost/basepath/app/logs/link-to/logs?time=1545092070952&filter=trace.id:%228b60bd32ecc6e1506735a8b6cfcf175c%22%20OR%20%228b60bd32ecc6e1506735a8b6cfcf175c%22'
|
||||
);
|
||||
});
|
||||
|
||||
it('should render the pod links when there is a pod id', async () => {
|
||||
const mock = getMock();
|
||||
describe('when there is no pod id', () => {
|
||||
it('does not render the Pod logs link', async () => {
|
||||
const { queryByText } = await renderTransaction(
|
||||
Transactions.transactionWithMinimalData
|
||||
);
|
||||
|
||||
const { queryByText, getByText } = await renderTransaction(
|
||||
Transactions.transactionWithKubernetesData,
|
||||
mock
|
||||
);
|
||||
|
||||
expect(queryByText('Pod logs')).not.toBeNull();
|
||||
expect(queryByText('Pod metrics')).not.toBeNull();
|
||||
|
||||
fireEvent.click(getByText('Pod logs'));
|
||||
|
||||
expect(mock.core.application.navigateToApp).toHaveBeenCalledWith('logs', {
|
||||
path: 'link-to/pod-logs/pod123456abcdef?time=1545092070952',
|
||||
expect(queryByText('Pod logs')).toBeNull();
|
||||
});
|
||||
|
||||
(mock.core.application.navigateToApp as jest.Mock).mockClear();
|
||||
it('does not render the Pod metrics link', async () => {
|
||||
const { queryByText } = await renderTransaction(
|
||||
Transactions.transactionWithMinimalData
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('Pod metrics'));
|
||||
|
||||
expect(mock.core.application.navigateToApp).toHaveBeenCalledWith(
|
||||
'metrics',
|
||||
{
|
||||
path:
|
||||
'link-to/pod-detail/pod123456abcdef?from=1545091770952&to=1545092370952',
|
||||
}
|
||||
);
|
||||
expect(queryByText('Pod metrics')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not render the container links when there is no container id', async () => {
|
||||
const { queryByText } = await renderTransaction(
|
||||
Transactions.transactionWithMinimalData
|
||||
);
|
||||
describe('when there is a pod id', () => {
|
||||
it('renders the pod logs link', async () => {
|
||||
const { getByText } = await renderTransaction(
|
||||
Transactions.transactionWithKubernetesData
|
||||
);
|
||||
|
||||
expect(queryByText('Container logs')).toBeNull();
|
||||
expect(queryByText('Container metrics')).toBeNull();
|
||||
});
|
||||
|
||||
it('should render the container links when there is a container id', async () => {
|
||||
const mock = getMock();
|
||||
|
||||
const { queryByText, getByText } = await renderTransaction(
|
||||
Transactions.transactionWithContainerData,
|
||||
mock
|
||||
);
|
||||
|
||||
expect(queryByText('Container logs')).not.toBeNull();
|
||||
expect(queryByText('Container metrics')).not.toBeNull();
|
||||
|
||||
fireEvent.click(getByText('Container logs'));
|
||||
|
||||
expect(mock.core.application.navigateToApp).toHaveBeenCalledWith('logs', {
|
||||
path: 'link-to/container-logs/container123456abcdef?time=1545092070952',
|
||||
expect(
|
||||
(getByText('Pod logs').parentElement as HTMLAnchorElement).href
|
||||
).toEqual(
|
||||
'http://localhost/basepath/app/logs/link-to/pod-logs/pod123456abcdef?time=1545092070952'
|
||||
);
|
||||
});
|
||||
|
||||
(mock.core.application.navigateToApp as jest.Mock).mockClear();
|
||||
it('renders the pod metrics link', async () => {
|
||||
const { getByText } = await renderTransaction(
|
||||
Transactions.transactionWithKubernetesData
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('Container metrics'));
|
||||
|
||||
expect(mock.core.application.navigateToApp).toHaveBeenCalledWith(
|
||||
'metrics',
|
||||
{
|
||||
path:
|
||||
'link-to/container-detail/container123456abcdef?from=1545091770952&to=1545092370952',
|
||||
}
|
||||
);
|
||||
expect(
|
||||
(getByText('Pod metrics').parentElement as HTMLAnchorElement).href
|
||||
).toEqual(
|
||||
'http://localhost/basepath/app/metrics/link-to/pod-detail/pod123456abcdef?from=1545091770952&to=1545092370952'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not render the host links when there is no hostname', async () => {
|
||||
const { queryByText } = await renderTransaction(
|
||||
Transactions.transactionWithMinimalData
|
||||
);
|
||||
describe('when there is no container id', () => {
|
||||
it('does not render the Container logs link', async () => {
|
||||
const { queryByText } = await renderTransaction(
|
||||
Transactions.transactionWithMinimalData
|
||||
);
|
||||
|
||||
expect(queryByText('Host logs')).toBeNull();
|
||||
expect(queryByText('Host metrics')).toBeNull();
|
||||
});
|
||||
|
||||
it('should render the host links when there is a hostname', async () => {
|
||||
const mock = getMock();
|
||||
const { queryByText, getByText } = await renderTransaction(
|
||||
Transactions.transactionWithHostData,
|
||||
mock
|
||||
);
|
||||
|
||||
expect(queryByText('Host logs')).not.toBeNull();
|
||||
expect(queryByText('Host metrics')).not.toBeNull();
|
||||
|
||||
fireEvent.click(getByText('Host logs'));
|
||||
|
||||
expect(mock.core.application.navigateToApp).toHaveBeenCalledWith('logs', {
|
||||
path: 'link-to/host-logs/227453131a17?time=1545092070952',
|
||||
expect(queryByText('Container logs')).toBeNull();
|
||||
});
|
||||
|
||||
(mock.core.application.navigateToApp as jest.Mock).mockClear();
|
||||
it('does not render the Container metrics link', async () => {
|
||||
const { queryByText } = await renderTransaction(
|
||||
Transactions.transactionWithMinimalData
|
||||
);
|
||||
|
||||
fireEvent.click(getByText('Host metrics'));
|
||||
expect(queryByText('Container metrics')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
expect(mock.core.application.navigateToApp).toHaveBeenCalledWith(
|
||||
'metrics',
|
||||
{
|
||||
path:
|
||||
'link-to/host-detail/227453131a17?from=1545091770952&to=1545092370952',
|
||||
}
|
||||
);
|
||||
describe('when there is a container id', () => {
|
||||
it('renders the Container logs link', async () => {
|
||||
const { getByText } = await renderTransaction(
|
||||
Transactions.transactionWithContainerData
|
||||
);
|
||||
|
||||
expect(
|
||||
(getByText('Container logs').parentElement as HTMLAnchorElement).href
|
||||
).toEqual(
|
||||
'http://localhost/basepath/app/logs/link-to/container-logs/container123456abcdef?time=1545092070952'
|
||||
);
|
||||
});
|
||||
|
||||
it('renders the Container metrics link', async () => {
|
||||
const { getByText } = await renderTransaction(
|
||||
Transactions.transactionWithContainerData
|
||||
);
|
||||
|
||||
expect(
|
||||
(getByText('Container metrics').parentElement as HTMLAnchorElement).href
|
||||
).toEqual(
|
||||
'http://localhost/basepath/app/metrics/link-to/container-detail/container123456abcdef?from=1545091770952&to=1545092370952'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is no hostname', () => {
|
||||
it('does not render the Host logs link', async () => {
|
||||
const { queryByText } = await renderTransaction(
|
||||
Transactions.transactionWithMinimalData
|
||||
);
|
||||
|
||||
expect(queryByText('Host logs')).toBeNull();
|
||||
});
|
||||
|
||||
it('does not render the Host metrics link', async () => {
|
||||
const { queryByText } = await renderTransaction(
|
||||
Transactions.transactionWithMinimalData
|
||||
);
|
||||
|
||||
expect(queryByText('Host metrics')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is a hostname', () => {
|
||||
it('renders the Host logs link', async () => {
|
||||
const { getByText } = await renderTransaction(
|
||||
Transactions.transactionWithHostData
|
||||
);
|
||||
|
||||
expect(
|
||||
(getByText('Host logs').parentElement as HTMLAnchorElement).href
|
||||
).toEqual(
|
||||
'http://localhost/basepath/app/logs/link-to/host-logs/227453131a17?time=1545092070952'
|
||||
);
|
||||
});
|
||||
|
||||
it('renders the Host metrics link', async () => {
|
||||
const { getByText } = await renderTransaction(
|
||||
Transactions.transactionWithHostData
|
||||
);
|
||||
|
||||
expect(
|
||||
(getByText('Host metrics').parentElement as HTMLAnchorElement).href
|
||||
).toEqual(
|
||||
'http://localhost/basepath/app/metrics/link-to/host-detail/227453131a17?from=1545091770952&to=1545092370952'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not render the uptime link if there is no url available', async () => {
|
||||
|
@ -230,24 +213,21 @@ describe('TransactionActionMenu component', () => {
|
|||
expect(queryByText('Status')).toBeNull();
|
||||
});
|
||||
|
||||
it('should render the uptime link if there is a url with a domain', async () => {
|
||||
const mock = getMock();
|
||||
describe('when there is a url with a domain', () => {
|
||||
it('renders the uptime link', async () => {
|
||||
const { getByText } = await renderTransaction(
|
||||
Transactions.transactionWithUrlAndDomain
|
||||
);
|
||||
|
||||
const { queryByText, getByText } = await renderTransaction(
|
||||
Transactions.transactionWithUrlAndDomain,
|
||||
mock
|
||||
);
|
||||
|
||||
expect(queryByText('Status')).not.toBeNull();
|
||||
|
||||
fireEvent.click(getByText('Status'));
|
||||
|
||||
expect(mock.core.application.navigateToApp).toHaveBeenCalledWith('uptime', {
|
||||
path: '?search=url.domain:%22example.com%22',
|
||||
expect(
|
||||
(getByText('Status').parentElement as HTMLAnchorElement).href
|
||||
).toEqual(
|
||||
'http://localhost/basepath/app/uptime?search=url.domain:%22example.com%22'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should match the snapshot', async () => {
|
||||
it('matches the snapshot', async () => {
|
||||
const { container } = await renderTransaction(
|
||||
Transactions.transactionWithAllData
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TransactionActionMenu component should match the snapshot 1`] = `
|
||||
exports[`TransactionActionMenu component matches the snapshot 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="euiPopover euiPopover--anchorDownRight"
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { Location } from 'history';
|
||||
import { getSections } from '../sections';
|
||||
import { IBasePath } from 'kibana/public';
|
||||
import { Transaction } from '../../../../../typings/es_schemas/ui/transaction';
|
||||
import { AppMountContextBasePath } from '../../../../context/ApmPluginContext';
|
||||
import { getSections } from '../sections';
|
||||
|
||||
describe('Transaction action menu', () => {
|
||||
const basePath = ({
|
||||
prepend: (url: string) => {
|
||||
return `some-basepath${url}`;
|
||||
},
|
||||
} as unknown) as AppMountContextBasePath;
|
||||
} as unknown) as IBasePath;
|
||||
const date = '2020-02-06T11:00:00.000Z';
|
||||
const timestamp = { us: new Date(date).getTime() };
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Location } from 'history';
|
||||
import { pickBy, isEmpty } from 'lodash';
|
||||
import { IBasePath } from 'kibana/public';
|
||||
import { isEmpty, pickBy } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import url from 'url';
|
||||
import { Transaction } from '../../../../typings/es_schemas/ui/transaction';
|
||||
|
@ -14,7 +15,6 @@ import { getDiscoverHref } from '../Links/DiscoverLinks/DiscoverLink';
|
|||
import { getDiscoverQuery } from '../Links/DiscoverLinks/DiscoverTransactionLink';
|
||||
import { getInfraHref } from '../Links/InfraLink';
|
||||
import { fromQuery } from '../Links/url_helpers';
|
||||
import { AppMountContextBasePath } from '../../../context/ApmPluginContext';
|
||||
|
||||
function getInfraMetricsQuery(transaction: Transaction) {
|
||||
const timestamp = new Date(transaction['@timestamp']).getTime();
|
||||
|
@ -49,7 +49,7 @@ export const getSections = ({
|
|||
urlParams,
|
||||
}: {
|
||||
transaction: Transaction;
|
||||
basePath: AppMountContextBasePath;
|
||||
basePath: IBasePath;
|
||||
location: Location;
|
||||
urlParams: IUrlParams;
|
||||
}) => {
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { toJson, mountWithTheme } from '../../../../../utils/testHelpers';
|
||||
import {
|
||||
disableConsoleWarning,
|
||||
toJson,
|
||||
mountWithTheme,
|
||||
} from '../../../../../utils/testHelpers';
|
||||
import { InnerCustomPlot } from '../index';
|
||||
import responseWithData from './responseWithData.json';
|
||||
import VoronoiPlot from '../VoronoiPlot';
|
||||
|
@ -19,11 +23,20 @@ function getXValueByIndex(index) {
|
|||
}
|
||||
|
||||
describe('when response has data', () => {
|
||||
let consoleMock;
|
||||
let wrapper;
|
||||
let onHover;
|
||||
let onMouseLeave;
|
||||
let onSelectionEnd;
|
||||
|
||||
beforeAll(() => {
|
||||
consoleMock = disableConsoleWarning('Warning: componentWillReceiveProps');
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
consoleMock.mockRestore();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
const series = getResponseTimeSeries({ apmTimeseries: responseWithData });
|
||||
onHover = jest.fn();
|
||||
|
|
|
@ -13,13 +13,27 @@ import {
|
|||
getDurationFormatter,
|
||||
asInteger,
|
||||
} from '../../../../../utils/formatters';
|
||||
import { toJson, mountWithTheme } from '../../../../../utils/testHelpers';
|
||||
import {
|
||||
disableConsoleWarning,
|
||||
toJson,
|
||||
mountWithTheme,
|
||||
} from '../../../../../utils/testHelpers';
|
||||
import { getFormattedBuckets } from '../../../../app/TransactionDetails/Distribution/index';
|
||||
|
||||
describe('Histogram', () => {
|
||||
let mockConsole;
|
||||
let wrapper;
|
||||
|
||||
const onClick = jest.fn();
|
||||
|
||||
beforeAll(() => {
|
||||
mockConsole = disableConsoleWarning('Warning: componentWillReceiveProps');
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
mockConsole.mockRestore();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
const buckets = getFormattedBuckets(response.buckets, response.bucketSize);
|
||||
const xMax = d3.max(buckets, (d) => d.x);
|
||||
|
|
|
@ -4,15 +4,20 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { ErrorMarker } from './ErrorMarker';
|
||||
import { ErrorMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { act } from '@testing-library/react-hooks';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { MockApmPluginContextWrapper } from '../../../../../context/ApmPluginContext/MockApmPluginContext';
|
||||
import {
|
||||
expectTextsInDocument,
|
||||
renderWithTheme,
|
||||
} from '../../../../../utils/testHelpers';
|
||||
import { ErrorMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks';
|
||||
import { ErrorMarker } from './ErrorMarker';
|
||||
|
||||
function Wrapper({ children }: { children?: ReactNode }) {
|
||||
return <MockApmPluginContextWrapper>{children}</MockApmPluginContextWrapper>;
|
||||
}
|
||||
|
||||
describe('ErrorMarker', () => {
|
||||
const mark = ({
|
||||
|
@ -36,7 +41,9 @@ describe('ErrorMarker', () => {
|
|||
} as unknown) as ErrorMark;
|
||||
|
||||
function openPopover(errorMark: ErrorMark) {
|
||||
const component = renderWithTheme(<ErrorMarker mark={errorMark} />);
|
||||
const component = renderWithTheme(<ErrorMarker mark={errorMark} />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
act(() => {
|
||||
fireEvent.click(component.getByTestId('popover'));
|
||||
});
|
||||
|
@ -51,7 +58,8 @@ describe('ErrorMarker', () => {
|
|||
it('renders link with trace and transaction', () => {
|
||||
const component = openPopover(mark);
|
||||
const errorLink = component.getByTestId('errorLink') as HTMLAnchorElement;
|
||||
expect(getKueryDecoded(errorLink.hash)).toEqual(
|
||||
|
||||
expect(getKueryDecoded(errorLink.search)).toEqual(
|
||||
'kuery=trace.id : "123" and transaction.id : "456"'
|
||||
);
|
||||
});
|
||||
|
@ -63,7 +71,7 @@ describe('ErrorMarker', () => {
|
|||
} as ErrorMark;
|
||||
const component = openPopover(newMark);
|
||||
const errorLink = component.getByTestId('errorLink') as HTMLAnchorElement;
|
||||
expect(getKueryDecoded(errorLink.hash)).toEqual('kuery=trace.id : "123"');
|
||||
expect(getKueryDecoded(errorLink.search)).toEqual('kuery=trace.id : "123"');
|
||||
});
|
||||
it('renders link with transaction', () => {
|
||||
const { trace, ...withoutTrace } = mark.error;
|
||||
|
@ -73,7 +81,7 @@ describe('ErrorMarker', () => {
|
|||
} as ErrorMark;
|
||||
const component = openPopover(newMark);
|
||||
const errorLink = component.getByTestId('errorLink') as HTMLAnchorElement;
|
||||
expect(getKueryDecoded(errorLink.hash)).toEqual(
|
||||
expect(getKueryDecoded(errorLink.search)).toEqual(
|
||||
'kuery=transaction.id : "456"'
|
||||
);
|
||||
});
|
||||
|
@ -85,7 +93,7 @@ describe('ErrorMarker', () => {
|
|||
} as ErrorMark;
|
||||
const component = openPopover(newMark);
|
||||
const errorLink = component.getByTestId('errorLink') as HTMLAnchorElement;
|
||||
expect(getKueryDecoded(errorLink.hash)).toEqual('kuery=');
|
||||
expect(getKueryDecoded(errorLink.search)).toEqual('kuery=');
|
||||
});
|
||||
it('truncates the error message text', () => {
|
||||
const { trace, transaction, ...withoutTraceAndTransaction } = mark.error;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import React from 'react';
|
||||
import { StickyContainer } from 'react-sticky';
|
||||
import {
|
||||
disableConsoleWarning,
|
||||
mountWithTheme,
|
||||
mockMoment,
|
||||
toJson,
|
||||
|
@ -14,8 +15,15 @@ import {
|
|||
import { Timeline } from '.';
|
||||
|
||||
describe('Timeline', () => {
|
||||
let consoleMock: jest.SpyInstance;
|
||||
|
||||
beforeAll(() => {
|
||||
mockMoment();
|
||||
consoleMock = disableConsoleWarning('Warning: componentWill');
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
consoleMock.mockRestore();
|
||||
});
|
||||
|
||||
it('should render with data', () => {
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { ApmPluginContext, ApmPluginContextValue } from '.';
|
||||
import { createCallApmApi } from '../../services/rest/createCallApmApi';
|
||||
import { ConfigSchema } from '../..';
|
||||
import { UI_SETTINGS } from '../../../../../../src/plugins/data/common';
|
||||
import { createCallApmApi } from '../../services/rest/createCallApmApi';
|
||||
|
||||
const uiSettings: Record<string, unknown> = {
|
||||
[UI_SETTINGS.TIMEPICKER_QUICK_RANGES]: [
|
||||
|
@ -33,8 +34,17 @@ const uiSettings: Record<string, unknown> = {
|
|||
};
|
||||
|
||||
const mockCore = {
|
||||
application: {
|
||||
capabilities: {
|
||||
apm: {},
|
||||
},
|
||||
currentAppId$: new Observable(),
|
||||
},
|
||||
chrome: {
|
||||
docTitle: { change: () => {} },
|
||||
setBreadcrumbs: () => {},
|
||||
setHelpExtension: () => {},
|
||||
setBadge: () => {},
|
||||
},
|
||||
docLinks: {
|
||||
DOC_LINK_VERSION: '0',
|
||||
|
@ -45,6 +55,9 @@ const mockCore = {
|
|||
prepend: (path: string) => `/basepath${path}`,
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
Context: ({ children }: { children: ReactNode }) => children,
|
||||
},
|
||||
notifications: {
|
||||
toasts: {
|
||||
addWarning: () => {},
|
||||
|
@ -53,6 +66,7 @@ const mockCore = {
|
|||
},
|
||||
uiSettings: {
|
||||
get: (key: string) => uiSettings[key],
|
||||
get$: (key: string) => of(mockCore.uiSettings.get(key)),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -4,16 +4,14 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { CoreStart } from 'kibana/public';
|
||||
import { createContext } from 'react';
|
||||
import { AppMountContext } from 'kibana/public';
|
||||
import { ConfigSchema } from '../..';
|
||||
import { ConfigSchema } from '../../';
|
||||
import { ApmPluginSetupDeps } from '../../plugin';
|
||||
|
||||
export type AppMountContextBasePath = AppMountContext['core']['http']['basePath'];
|
||||
|
||||
export interface ApmPluginContextValue {
|
||||
config: ConfigSchema;
|
||||
core: AppMountContext['core'];
|
||||
core: CoreStart;
|
||||
plugins: ApmPluginSetupDeps;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
|
||||
import React, { ReactNode, useMemo, useState } from 'react';
|
||||
import { toQuery, fromQuery } from '../components/shared/Links/url_helpers';
|
||||
import { history } from '../utils/history';
|
||||
import { useUrlParams } from '../hooks/useUrlParams';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { fromQuery, toQuery } from '../components/shared/Links/url_helpers';
|
||||
import { useFetcher } from '../hooks/useFetcher';
|
||||
import { useUrlParams } from '../hooks/useUrlParams';
|
||||
|
||||
const ChartsSyncContext = React.createContext<{
|
||||
hoverX: number | null;
|
||||
|
@ -18,6 +18,7 @@ const ChartsSyncContext = React.createContext<{
|
|||
} | null>(null);
|
||||
|
||||
function ChartsSyncContextProvider({ children }: { children: ReactNode }) {
|
||||
const history = useHistory();
|
||||
const [time, setTime] = useState<number | null>(null);
|
||||
const { urlParams, uiFilters } = useUrlParams();
|
||||
|
||||
|
@ -75,7 +76,7 @@ function ChartsSyncContextProvider({ children }: { children: ReactNode }) {
|
|||
};
|
||||
|
||||
return { ...hoverXHandlers };
|
||||
}, [time, data.annotations]);
|
||||
}, [history, time, data.annotations]);
|
||||
|
||||
return <ChartsSyncContext.Provider value={value} children={children} />;
|
||||
}
|
||||
|
|
|
@ -5,21 +5,21 @@
|
|||
*/
|
||||
|
||||
import { omit } from 'lodash';
|
||||
import { useFetcher } from './useFetcher';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Projection } from '../../common/projections';
|
||||
import { pickKeys } from '../../common/utils/pick_keys';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { LocalUIFiltersAPIResponse } from '../../server/lib/ui_filters/local_ui_filters';
|
||||
import { useUrlParams } from './useUrlParams';
|
||||
import {
|
||||
LocalUIFilterName,
|
||||
localUIFilters,
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
} from '../../server/lib/ui_filters/local_ui_filters/config';
|
||||
import { history } from '../utils/history';
|
||||
import { toQuery, fromQuery } from '../components/shared/Links/url_helpers';
|
||||
import { fromQuery, toQuery } from '../components/shared/Links/url_helpers';
|
||||
import { removeUndefinedProps } from '../context/UrlParamsContext/helpers';
|
||||
import { Projection } from '../../common/projections';
|
||||
import { pickKeys } from '../../common/utils/pick_keys';
|
||||
import { useCallApi } from './useCallApi';
|
||||
import { useFetcher } from './useFetcher';
|
||||
import { useUrlParams } from './useUrlParams';
|
||||
|
||||
const getInitialData = (
|
||||
filterNames: LocalUIFilterName[]
|
||||
|
@ -39,6 +39,7 @@ export function useLocalUIFilters({
|
|||
filterNames: LocalUIFilterName[];
|
||||
params?: Record<string, string | number | boolean | undefined>;
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { uiFilters, urlParams } = useUrlParams();
|
||||
const callApi = useCallApi();
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { createHashHistory } from 'history';
|
||||
|
||||
// Make history singleton available across APM project
|
||||
// TODO: Explore using React context or hook instead?
|
||||
let history = createHashHistory();
|
||||
|
||||
export const resetHistory = () => {
|
||||
history = createHashHistory();
|
||||
};
|
||||
|
||||
export { history };
|
|
@ -26,6 +26,20 @@ import {
|
|||
} from '../../typings/elasticsearch';
|
||||
import { MockApmPluginContextWrapper } from '../context/ApmPluginContext/MockApmPluginContext';
|
||||
|
||||
const originalConsoleWarn = console.warn; // eslint-disable-line no-console
|
||||
/**
|
||||
* A dependency we're using is using deprecated react methods. Override the
|
||||
* console to hide the warnings. These should go away when we switch to
|
||||
* Elastic Charts
|
||||
*/
|
||||
export function disableConsoleWarning(messageToDisable: string) {
|
||||
return jest.spyOn(console, 'warn').mockImplementation((message) => {
|
||||
if (!message.startsWith(messageToDisable)) {
|
||||
originalConsoleWarn(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function toJson(wrapper: ReactWrapper) {
|
||||
return enzymeToJson(wrapper, {
|
||||
noKey: true,
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { createMemoryHistory } from 'history';
|
||||
import React from 'react';
|
||||
import { renderApp } from './';
|
||||
import { Observable } from 'rxjs';
|
||||
import { CoreStart, AppMountParameters } from 'src/core/public';
|
||||
import { AppMountParameters, CoreStart } from 'src/core/public';
|
||||
import { renderApp } from './';
|
||||
|
||||
describe('renderApp', () => {
|
||||
it('renders', () => {
|
||||
|
@ -19,6 +20,7 @@ describe('renderApp', () => {
|
|||
} as unknown) as CoreStart;
|
||||
const params = ({
|
||||
element: window.document.createElement('div'),
|
||||
history: createMemoryHistory(),
|
||||
} as unknown) as AppMountParameters;
|
||||
|
||||
expect(() => {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { createHashHistory } from 'history';
|
||||
import React, { useEffect } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Route, Router, Switch } from 'react-router-dom';
|
||||
|
@ -52,10 +51,10 @@ function App() {
|
|||
);
|
||||
}
|
||||
|
||||
export const renderApp = (core: CoreStart, { element }: AppMountParameters) => {
|
||||
export const renderApp = (core: CoreStart, { element, history }: AppMountParameters) => {
|
||||
const i18nCore = core.i18n;
|
||||
const isDarkMode = core.uiSettings.get('theme:darkMode');
|
||||
const history = createHashHistory();
|
||||
|
||||
ReactDOM.render(
|
||||
<PluginContext.Provider value={{ core }}>
|
||||
<Router history={history}>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue