diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/navigation.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/navigation.cy.ts new file mode 100644 index 0000000000000..0b060bcafdbf2 --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/read_only_user/navigation.cy.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import url from 'url'; +import { synthtrace } from '../../../synthtrace'; +import { opbeans } from '../../fixtures/synthtrace/opbeans'; + +const start = '2021-10-10T00:00:00.000Z'; +const end = '2021-10-10T00:15:00.000Z'; + +const serviceOverview = url.format({ + pathname: '/app/apm/services/opbeans-java/overview', + query: { + comparisonEnabled: 'true', + environment: 'ENVIRONMENT_ALL', + rangeFrom: start, + rangeTo: end, + offset: '1d', + }, +}); + +describe('When navigating between pages', () => { + before(() => { + synthtrace.index( + opbeans({ + from: new Date(start).getTime(), + to: new Date(end).getTime(), + }) + ); + }); + + after(() => { + synthtrace.clean(); + }); + + beforeEach(() => { + cy.loginAsViewerUser(); + }); + + it('should only load certain resources once', () => { + cy.intercept('**/internal/apm/has_data').as('hasDataRequest'); + cy.intercept('**/internal/apm/services/opbeans-java/metadata/icons**').as( + 'serviceIconsRequest' + ); + cy.intercept('**/apm/fleet/has_apm_policies').as('apmPoliciesRequest'); + + // Overview page + cy.visitKibana(serviceOverview); + cy.get('.euiTab-isSelected').should('have.text', 'Overview'); + + // it should load resources once + cy.get('@hasDataRequest.all').should('have.length', 1); + cy.get('@serviceIconsRequest.all').should('have.length', 1); + cy.get('@apmPoliciesRequest.all').should('have.length', 1); + + // Navigate to errors page + cy.contains('.euiTab', 'Errors').click(); + cy.get('.euiTab-isSelected').should('have.text', 'Errors'); + + // page have loaded correctly + cy.get('.euiLoadingChart').should('not.exist'); + cy.get('[data-test-subj="errorDistribution"]').should('exist'); + + // it should not load resources again + cy.get('@hasDataRequest.all').should('have.length', 1); + cy.get('@serviceIconsRequest.all').should('have.length', 1); + cy.get('@apmPoliciesRequest.all').should('have.length', 1); + }); +}); diff --git a/x-pack/plugins/apm/public/application/routes/index.tsx b/x-pack/plugins/apm/public/application/routes/index.tsx deleted file mode 100644 index 8ae6e5efad747..0000000000000 --- a/x-pack/plugins/apm/public/application/routes/index.tsx +++ /dev/null @@ -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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { RouteComponentProps, RouteProps } from 'react-router-dom'; - -export type BreadcrumbTitle = - | string - | ((props: RouteComponentProps) => string) - | null; - -export interface APMRouteDefinition extends RouteProps { - breadcrumb: BreadcrumbTitle; -} diff --git a/x-pack/plugins/apm/public/components/routing/apm_error_boundary.tsx b/x-pack/plugins/apm/public/components/routing/apm_error_boundary.tsx index 3b7849ddacf30..c825f4ac266a1 100644 --- a/x-pack/plugins/apm/public/components/routing/apm_error_boundary.tsx +++ b/x-pack/plugins/apm/public/components/routing/apm_error_boundary.tsx @@ -14,11 +14,11 @@ import { ApmPluginStartDeps } from '../../plugin'; export function ApmErrorBoundary({ children }: { children?: React.ReactNode }) { const location = useLocation(); - return {children}; + return {children}; } class ErrorBoundary extends React.Component< - { children?: React.ReactNode }, + { children?: React.ReactNode; pathname: string }, { error?: Error }, {} > { @@ -26,6 +26,15 @@ class ErrorBoundary extends React.Component< error: undefined, }; + componentDidUpdate(prevProps: { pathname: string }) { + if ( + this.props.pathname !== prevProps.pathname && + this.state.error !== undefined + ) { + this.setState({ error: undefined }); + } + } + static getDerivedStateFromError(error: Error) { return { error }; } @@ -66,7 +75,6 @@ function ErrorWithTemplate({ error }: { error: Error }) { ); } -function DummyComponent({ error }: { error: Error }) { +function DummyComponent({ error }: { error: Error }): any { throw error; - return
; } diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index cfa3bc4ac660e..e6cada76ab52f 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -76,7 +76,6 @@ "@kbn/babel-register", "@kbn/core-saved-objects-migration-server-internal", "@kbn/core-elasticsearch-server", - "@kbn/shared-ux-prompt-not-found", "@kbn/core-saved-objects-api-server", "@kbn/safer-lodash-set", "@kbn/shared-ux-router", @@ -85,6 +84,7 @@ "@kbn/logging-mocks", "@kbn/chart-icons", "@kbn/observability-shared-plugin", + "@kbn/shared-ux-prompt-not-found", ], "exclude": [ "target/**/*",