From 20dcf5f7211c6e8ed166fc7bb9a61a733384a529 Mon Sep 17 00:00:00 2001 From: "Eyo O. Eyo" <7893459+eokoneyo@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:57:20 +0100 Subject: [PATCH] [Reporting] Improvements to reporting diagnostics (#191790) ## Summary Closes https://github.com/elastic/kibana/issues/186434 This PR adds a search within the output log whilst attempting to start the browser (i.e chromium) bundled with Kibana to ascertain if it includes a font config error log message as signal in reporting diagnostics for potential issues with generating a report. This work is informed from couple of support tickets with customers running the diagnostics tool, getting a confirmation that the reporting system is functionality whilst missing font issues was actually preventing reports from being generated on the linux platform. Also fixes an issue that prevented visual feedback for when an error is detected during diagnosis. #### Visuals ![ScreenRecording2024-09-26at15 04 19-ezgif com-video-to-gif-converter](https://github.com/user-attachments/assets/c0320f2a-6802-435b-bae7-535f878113b3) --------- Co-authored-by: Elastic Machine --- .../components/report_diagnostic.test.tsx | 118 ++++++++++++++++++ .../components/report_diagnostic.tsx | 14 ++- .../routes/internal/diagnostic/browser.ts | 8 ++ .../integration_tests/browser.test.ts | 24 ++++ 4 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/reporting/public/management/components/report_diagnostic.test.tsx diff --git a/x-pack/plugins/reporting/public/management/components/report_diagnostic.test.tsx b/x-pack/plugins/reporting/public/management/components/report_diagnostic.test.tsx new file mode 100644 index 0000000000000..95ede7f2d3444 --- /dev/null +++ b/x-pack/plugins/reporting/public/management/components/report_diagnostic.test.tsx @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { type ComponentProps } from 'react'; +import userEvent from '@testing-library/user-event'; +import { render, screen, waitFor } from '@testing-library/react'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { ReportDiagnostic } from './report_diagnostic'; + +const mockedApiClient: jest.Mocked< + Pick['apiClient'], 'verifyBrowser'> +> = { + verifyBrowser: jest.fn(), +}; + +const defaultProps: Pick, 'apiClient'> = { + // @ts-expect-error we don't need to provide the full apiClient for the test + apiClient: mockedApiClient, +}; + +const renderComponent = (props: Pick, 'clientConfig'>) => { + render( + + + + ); +}; + +describe('ReportDiagnostic', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it("does not render the component, if image exports aren't supported", () => { + renderComponent({ + clientConfig: { + export_types: { pdf: { enabled: false }, png: { enabled: false } }, + } as unknown as ComponentProps['clientConfig'], + }); + + expect(screen.queryByTestId('screenshotDiagnosticLink')).not.toBeInTheDocument(); + }); + + it('renders the component if image exports are supported', () => { + renderComponent({ + clientConfig: { + export_types: { + png: { enabled: true }, + pdf: { enabled: true }, + }, + } as unknown as ComponentProps['clientConfig'], + }); + + expect(screen.getByTestId('screenshotDiagnosticLink')).toBeInTheDocument(); + }); + + it('renders a callout with a warning if a problem is detected during diagnosis', async () => { + const user = userEvent.setup(); + + mockedApiClient.verifyBrowser.mockResolvedValue({ + success: false, + help: ['help'], + logs: 'logs', + }); + + renderComponent({ + clientConfig: { + export_types: { + png: { enabled: true }, + pdf: { enabled: true }, + }, + } as unknown as ComponentProps['clientConfig'], + }); + + await user.click(screen.getByTestId('screenshotDiagnosticLink')); + + await waitFor(() => expect(screen.getByTestId('reportDiagnosisFlyout')).toBeInTheDocument()); + + user.click(screen.getByTestId('reportingDiagnosticInitiationButton')); + + await waitFor(() => + expect(screen.getByTestId('reportingDiagnosticFailureCallout')).toBeInTheDocument() + ); + }); + + it('renders a success callout if no problem is detected during diagnosis', async () => { + const user = userEvent.setup(); + + mockedApiClient.verifyBrowser.mockResolvedValue({ + success: true, + help: [], + logs: 'logs', + }); + + renderComponent({ + clientConfig: { + export_types: { + png: { enabled: true }, + pdf: { enabled: true }, + }, + } as unknown as ComponentProps['clientConfig'], + }); + + await user.click(screen.getByTestId('screenshotDiagnosticLink')); + + await waitFor(() => expect(screen.getByTestId('reportDiagnosisFlyout')).toBeInTheDocument()); + + user.click(screen.getByTestId('reportingDiagnosticInitiationButton')); + + await waitFor(() => + expect(screen.getByTestId('reportingDiagnosticSuccessCallout')).toBeInTheDocument() + ); + }); +}); diff --git a/x-pack/plugins/reporting/public/management/components/report_diagnostic.tsx b/x-pack/plugins/reporting/public/management/components/report_diagnostic.tsx index bbaab324a3fd4..90139a56ead28 100644 --- a/x-pack/plugins/reporting/public/management/components/report_diagnostic.tsx +++ b/x-pack/plugins/reporting/public/management/components/report_diagnostic.tsx @@ -100,17 +100,17 @@ export const ReportDiagnostic = ({ apiClient, clientConfig }: Props) => { if (state.success && chromeStatus === 'complete') { outcomeCallout = ( ); - } else if (!state.success && chromeStatus === 'complete') { + } else if (!state.success && chromeStatus === 'danger') { outcomeCallout = ( { } flyout = ( - +

@@ -161,6 +166,7 @@ export const ReportDiagnostic = ({ apiClient, clientConfig }: Props) => { onClick={apiWrapper(() => apiClient.verifyBrowser(), statuses.chromeStatus)} isLoading={isBusy && chromeStatus === 'incomplete'} iconType={chromeStatus === 'complete' ? 'check' : undefined} + data-test-subj="reportingDiagnosticInitiationButton" > ({ defaultMessage: `Unable to use Chromium sandbox. This can be disabled at your own risk with 'xpack.screenshotting.browser.chromium.disableSandbox'. Please see {url}`, values: { url: docLinks.links.reporting.browserSandboxDependencies }, }), + + 'Fontconfig error: Cannot load default config file': i18n.translate( + 'xpack.reporting.diagnostic.fontconfigError', + { + defaultMessage: `The browser couldn't start properly due to missing system font dependencies. Please see {url}`, + values: { url: docLinks.links.reporting.browserSystemDependencies }, + } + ), }); const path = INTERNAL_ROUTES.DIAGNOSE.BROWSER; diff --git a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts index 3f1966d2e78d8..531b357e30bc0 100644 --- a/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/diagnostic/integration_tests/browser.test.ts @@ -120,6 +120,30 @@ describe(`GET ${INTERNAL_ROUTES.DIAGNOSE.BROWSER}`, () => { }); }); + it('returns a response including log received from the browser + helpful link on font config error', async () => { + const fontErrorLog = `Fontconfig error: Cannot load default config file: No such file: (null)`; + + registerDiagnoseBrowser(core, mockLogger); + + await server.start(); + screenshotting.diagnose.mockReturnValue(Rx.of(fontErrorLog)); + + return supertest(httpSetup.server.listener) + .get(INTERNAL_ROUTES.DIAGNOSE.BROWSER) + .expect(200) + .then(({ body }) => { + expect(body).toMatchInlineSnapshot(` + Object { + "help": Array [ + "The browser couldn't start properly due to missing system font dependencies. Please see https://www.elastic.co/guide/en/kibana/test-branch/secure-reporting.html#install-reporting-packages", + ], + "logs": "${fontErrorLog}", + "success": false, + } + `); + }); + }); + it('logs a message when the browser starts, but then has problems later', async () => { registerDiagnoseBrowser(core, mockLogger);