From 1d3d951e525cb0af2d2536700fad24ec4fd9ebc5 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 21 Aug 2023 13:06:29 -0700 Subject: [PATCH 1/8] remove failing functional test --- .../reporting_and_timeout.config.ts | 26 ----- .../fixtures/baseline/warnings_capture_b.png | Bin 95272 -> 0 bytes .../reporting_and_timeout/index.ts | 98 ------------------ 3 files changed, 124 deletions(-) delete mode 100644 x-pack/test/reporting_functional/reporting_and_timeout.config.ts delete mode 100644 x-pack/test/reporting_functional/reporting_and_timeout/fixtures/baseline/warnings_capture_b.png delete mode 100644 x-pack/test/reporting_functional/reporting_and_timeout/index.ts diff --git a/x-pack/test/reporting_functional/reporting_and_timeout.config.ts b/x-pack/test/reporting_functional/reporting_and_timeout.config.ts deleted file mode 100644 index 05e77ea70cd6f..0000000000000 --- a/x-pack/test/reporting_functional/reporting_and_timeout.config.ts +++ /dev/null @@ -1,26 +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 { FtrConfigProviderContext } from '@kbn/test'; -import { resolve } from 'path'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const functionalConfig = await readConfigFile(require.resolve('./reporting_and_security.config')); - - return { - ...functionalConfig.getAll(), - junit: { reportName: 'X-Pack Reporting Functional Tests: Reports and Timeout Handling' }, - testFiles: [resolve(__dirname, './reporting_and_timeout')], - kbnTestServer: { - ...functionalConfig.get('kbnTestServer'), - serverArgs: [ - ...functionalConfig.get('kbnTestServer.serverArgs'), - `--xpack.reporting.capture.timeouts.waitForElements=1s`, - ], - }, - }; -} diff --git a/x-pack/test/reporting_functional/reporting_and_timeout/fixtures/baseline/warnings_capture_b.png b/x-pack/test/reporting_functional/reporting_and_timeout/fixtures/baseline/warnings_capture_b.png deleted file mode 100644 index e86939f7ff741ef93185ebefa86052b49c908f85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 95272 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 diff --git a/x-pack/test/reporting_functional/reporting_and_timeout/index.ts b/x-pack/test/reporting_functional/reporting_and_timeout/index.ts deleted file mode 100644 index c420c76d77304..0000000000000 --- a/x-pack/test/reporting_functional/reporting_and_timeout/index.ts +++ /dev/null @@ -1,98 +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 expect from '@kbn/expect'; -import fs from 'fs'; -import path from 'path'; -import { PNG } from 'pngjs'; -import { FtrProviderContext } from '../ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function ({ getService, getPageObjects }: FtrProviderContext) { - const PageObjects = getPageObjects(['common', 'reporting', 'dashboard']); - const log = getService('log'); - const reportingAPI = getService('reportingAPI'); - const reportingFunctional = getService('reportingFunctional'); - const browser = getService('browser'); - const png = getService('png'); - const config = getService('config'); - const screenshotDir = config.get('screenshots.directory'); - - // FLAKY: https://github.com/elastic/kibana/issues/135309 - describe.skip('Reporting Functional Tests with forced timeout', function () { - const dashboardTitle = 'Ecom Dashboard Hidden Panel Titles'; - const sessionPngFullPage = 'warnings_capture_session_a'; - const sessionPngCropped = 'warnings_capture_session_b'; - const baselinePng = path.resolve(__dirname, 'fixtures/baseline/warnings_capture_b.png'); - - let url: string; - before(async () => { - await reportingAPI.logTaskManagerHealth(); - await reportingFunctional.initEcommerce(); - - await PageObjects.common.navigateToApp('dashboard'); - await PageObjects.dashboard.loadSavedDashboard(dashboardTitle); - await PageObjects.reporting.setTimepickerInEcommerceDataRange(); - await browser.setWindowSize(800, 850); - - await PageObjects.reporting.openPngReportingPanel(); - await PageObjects.reporting.clickGenerateReportButton(); - - url = await PageObjects.reporting.getReportURL(60000); - - const res = await PageObjects.reporting.getResponse(url); - expect(res.status).to.equal(200); - expect(res.get('content-type')).to.equal('image/png'); - }); - - after(async () => { - await reportingFunctional.teardownEcommerce(); - }); - - it('adds a visual warning in the report output', async () => { - const captureData = await PageObjects.reporting.getRawPdfReportData(url); - const sessionReport = await PageObjects.reporting.writeSessionReport( - sessionPngFullPage, - 'png', - captureData, - screenshotDir - ); - - const region = { height: 320, width: 1540, srcX: 20, srcY: 10 }; - const dstPath = path.resolve(screenshotDir, sessionPngCropped + '.png'); - const dst = new PNG({ width: region.width, height: region.height }); - - const pngSessionFilePath = await new Promise((resolve) => { - fs.createReadStream(sessionReport) - .pipe(new PNG()) - .on('parsed', function () { - log.info(`cropping report to the visual warning area`); - this.bitblt(dst, region.srcX, region.srcY, region.width, region.height, 0, 0); - dst.pack().pipe(fs.createWriteStream(dstPath)); - resolve(dstPath); - }); - }); - - log.info(`saved cropped file to ${dstPath}`); - - expect( - await png.checkIfPngsMatch(pngSessionFilePath, baselinePng, screenshotDir) - ).to.be.lessThan(0.09); - - /** - * This test may fail when styling differences affect the result. To update the snapshot: - * - * 1. Run the functional test, to generate new temporary files for screenshot comparison. - * 2. Save the screenshot as the new baseline file: - * cp \ - * x-pack/test/functional/screenshots/session/warnings_capture_session_b_actual.png \ - * x-pack/test/reporting_functional/reporting_and_timeout/fixtures/baseline/warnings_capture_b.png - * 3. Commit the changes to the .png file - */ - }); - }); -} From 368f2d57a60e497c6e6b2f661085bbcb43f1b9fe Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 21 Aug 2023 13:39:36 -0700 Subject: [PATCH 2/8] Rename screenshots.ts --- .../server/screenshots/index.ts | 201 +---------------- .../server/screenshots/screenshots.ts | 208 ++++++++++++++++++ 2 files changed, 212 insertions(+), 197 deletions(-) create mode 100644 x-pack/plugins/screenshotting/server/screenshots/screenshots.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/index.ts b/x-pack/plugins/screenshotting/server/screenshots/index.ts index f8dd839dfc55c..b1a0d98fe8a27 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/index.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/index.ts @@ -5,48 +5,18 @@ * 2.0. */ -import type { CloudSetup } from '@kbn/cloud-plugin/server'; -import type { HttpServiceSetup, KibanaRequest, Logger, PackageInfo } from '@kbn/core/server'; +import type { KibanaRequest } from '@kbn/core/server'; import type { ExpressionAstExpression } from '@kbn/expressions-plugin/common'; import type { Optional } from '@kbn/utility-types'; -import { Semaphore } from '@kbn/std'; -import ipaddr from 'ipaddr.js'; -import { defaultsDeep, sum } from 'lodash'; -import { from, Observable, of, throwError } from 'rxjs'; -import { - catchError, - concatMap, - first, - map, - mergeMap, - take, - takeUntil, - tap, - toArray, -} from 'rxjs/operators'; -import { - errors, - LayoutParams, - SCREENSHOTTING_APP_ID, - SCREENSHOTTING_EXPRESSION, - SCREENSHOTTING_EXPRESSION_INPUT, -} from '../../common'; -import { HeadlessChromiumDriverFactory, PerformanceMetrics } from '../browsers'; -import { systemHasInsufficientMemory } from '../cloud'; -import type { ConfigType } from '../config'; -import { durationToNumber } from '../config'; +import { LayoutParams } from '../../common'; +import { PerformanceMetrics } from '../browsers'; import { PdfScreenshotOptions, PdfScreenshotResult, PngScreenshotOptions, PngScreenshotResult, - toPdf, - toPng, } from '../formats'; -import { createLayout, Layout } from '../layouts'; -import { EventLogger, Transactions } from './event_logger'; import type { ScreenshotObservableOptions, ScreenshotObservableResult } from './observable'; -import { ScreenshotObservableHandler, UrlOrUrlWithContext } from './observable'; export type { ScreenshotObservableResult, UrlOrUrlWithContext } from './observable'; @@ -55,17 +25,14 @@ export interface CaptureOptions extends Optional { - const { browserTimezone } = options; - - return this.browserDriverFactory - .createPage( - { - browserTimezone, - openUrlTimeout: durationToNumber(this.config.capture.timeouts.openUrl), - defaultViewport: { width: layout.width, deviceScaleFactor: layout.getBrowserZoom() }, - }, - this.logger - ) - .pipe( - this.semaphore.acquire(), - mergeMap(({ driver, error$, close }) => { - const screen: ScreenshotObservableHandler = new ScreenshotObservableHandler( - driver, - this.config, - eventLogger, - layout, - options - ); - - return from(options.urls).pipe( - concatMap((url, index) => - screen.setupPage(index, url).pipe( - catchError((error) => { - screen.checkPageIsOpen(); // this fails the job if the browser has closed - - this.logger.error(error); - eventLogger.error(error, Transactions.SCREENSHOTTING); - return of({ ...DEFAULT_SETUP_RESULT, error }); // allow "as-is" screenshot with injected warning message - }), - takeUntil(error$), - screen.getScreenshots() - ) - ), - take(options.urls.length), - toArray(), - mergeMap((results) => - // At this point we no longer need the page, close it and send out the results - close().pipe(map(({ metrics }) => ({ metrics, results }))) - ) - ); - }), - first() - ); - } - - private getScreenshottingAppUrl() { - const info = this.http.getServerInfo(); - const { protocol, port } = info; - let { hostname } = info; - - if (ipaddr.isValid(hostname) && !sum(ipaddr.parse(hostname).toByteArray())) { - hostname = 'localhost'; - } - - return `${protocol}://${hostname}:${port}${this.http.basePath.serverBasePath}/app/${SCREENSHOTTING_APP_ID}`; - } - - private getCaptureOptions({ - expression, - input, - request, - ...options - }: ScreenshotOptions): ScreenshotObservableOptions { - const headers = { ...(request?.headers ?? {}), ...(options.headers ?? {}) }; - const urls = expression - ? [ - [ - this.getScreenshottingAppUrl(), - { - [SCREENSHOTTING_EXPRESSION]: expression, - [SCREENSHOTTING_EXPRESSION_INPUT]: input, - }, - ] as UrlOrUrlWithContext, - ] - : options.urls; - - return defaultsDeep( - { - ...options, - headers, - urls, - }, - { - timeouts: { - openUrl: 60000, - waitForElements: 60000, - renderComplete: 120000, - }, - urls: [], - } - ); - } - - systemHasInsufficientMemory(): boolean { - return systemHasInsufficientMemory(this.cloud, this.logger.get('cloud')); - } - - getScreenshots(options: PngScreenshotOptions): Observable; - getScreenshots(options: PdfScreenshotOptions): Observable; - getScreenshots(options: ScreenshotOptions): Observable; - getScreenshots(options: ScreenshotOptions): Observable { - if (this.systemHasInsufficientMemory()) { - return throwError(() => new errors.InsufficientMemoryAvailableOnCloudError()); - } - - const eventLogger = new EventLogger(this.logger, this.config); - const transactionEnd = eventLogger.startTransaction(Transactions.SCREENSHOTTING); - - const layout = createLayout(options.layout ?? {}); - const captureOptions = this.getCaptureOptions(options); - - return this.captureScreenshots(eventLogger, layout, captureOptions).pipe( - tap(({ results, metrics }) => { - transactionEnd({ - labels: { - cpu: metrics?.cpu, - memory: metrics?.memory, - memory_mb: metrics?.memoryInMegabytes, - ...eventLogger.getByteLengthFromCaptureResults(results), - }, - }); - }), - mergeMap((result) => { - switch (options.format) { - case 'pdf': - return toPdf(eventLogger, this.packageInfo, layout, options, result); - default: - return toPng(result); - } - }) - ); - } -} +export { Screenshots } from './screenshots'; diff --git a/x-pack/plugins/screenshotting/server/screenshots/screenshots.ts b/x-pack/plugins/screenshotting/server/screenshots/screenshots.ts new file mode 100644 index 0000000000000..64f6ecdb5264a --- /dev/null +++ b/x-pack/plugins/screenshotting/server/screenshots/screenshots.ts @@ -0,0 +1,208 @@ +/* + * 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 type { CloudSetup } from '@kbn/cloud-plugin/server'; +import type { HttpServiceSetup, Logger, PackageInfo } from '@kbn/core/server'; +import { Semaphore } from '@kbn/std'; +import ipaddr from 'ipaddr.js'; +import { defaultsDeep, sum } from 'lodash'; +import { from, Observable, of, throwError } from 'rxjs'; +import { + catchError, + concatMap, + first, + map, + mergeMap, + take, + takeUntil, + tap, + toArray, +} from 'rxjs/operators'; +import { CaptureResult, ScreenshotOptions, ScreenshotResult } from '.'; +import { + errors, + SCREENSHOTTING_APP_ID, + SCREENSHOTTING_EXPRESSION, + SCREENSHOTTING_EXPRESSION_INPUT, +} from '../../common'; +import { HeadlessChromiumDriverFactory } from '../browsers'; +import { systemHasInsufficientMemory } from '../cloud'; +import type { ConfigType } from '../config'; +import { durationToNumber } from '../config'; +import { + PdfScreenshotOptions, + PdfScreenshotResult, + PngScreenshotOptions, + PngScreenshotResult, + toPdf, + toPng, +} from '../formats'; +import { createLayout, Layout } from '../layouts'; +import { EventLogger, Transactions } from './event_logger'; +import type { ScreenshotObservableOptions } from './observable'; +import { ScreenshotObservableHandler, UrlOrUrlWithContext } from './observable'; + +const DEFAULT_SETUP_RESULT = { + elementsPositionAndAttributes: null, + timeRange: null, +}; + +export class Screenshots { + private semaphore: Semaphore; + + constructor( + private readonly browserDriverFactory: HeadlessChromiumDriverFactory, + private readonly logger: Logger, + private readonly packageInfo: PackageInfo, + private readonly http: HttpServiceSetup, + private readonly config: ConfigType, + private readonly cloud?: CloudSetup + ) { + this.semaphore = new Semaphore(config.poolSize); + } + + private captureScreenshots( + eventLogger: EventLogger, + layout: Layout, + options: ScreenshotObservableOptions + ): Observable { + const { browserTimezone } = options; + + return this.browserDriverFactory + .createPage( + { + browserTimezone, + openUrlTimeout: durationToNumber(this.config.capture.timeouts.openUrl), + defaultViewport: { width: layout.width, deviceScaleFactor: layout.getBrowserZoom() }, + }, + this.logger + ) + .pipe( + this.semaphore.acquire(), + mergeMap(({ driver, error$, close }) => { + const screen: ScreenshotObservableHandler = new ScreenshotObservableHandler( + driver, + this.config, + eventLogger, + layout, + options + ); + + return from(options.urls).pipe( + concatMap((url, index) => + screen.setupPage(index, url).pipe( + catchError((error) => { + screen.checkPageIsOpen(); // this fails the job if the browser has closed + + this.logger.error(error); + eventLogger.error(error, Transactions.SCREENSHOTTING); + return of({ ...DEFAULT_SETUP_RESULT, error }); // allow "as-is" screenshot with injected warning message + }), + takeUntil(error$), + screen.getScreenshots() + ) + ), + take(options.urls.length), + toArray(), + mergeMap((results) => + // At this point we no longer need the page, close it and send out the results + close().pipe(map(({ metrics }) => ({ metrics, results }))) + ) + ); + }), + first() + ); + } + + private getScreenshottingAppUrl() { + const info = this.http.getServerInfo(); + const { protocol, port } = info; + let { hostname } = info; + + if (ipaddr.isValid(hostname) && !sum(ipaddr.parse(hostname).toByteArray())) { + hostname = 'localhost'; + } + + return `${protocol}://${hostname}:${port}${this.http.basePath.serverBasePath}/app/${SCREENSHOTTING_APP_ID}`; + } + + private getCaptureOptions({ + expression, + input, + request, + ...options + }: ScreenshotOptions): ScreenshotObservableOptions { + const headers = { ...(request?.headers ?? {}), ...(options.headers ?? {}) }; + const urls = expression + ? [ + [ + this.getScreenshottingAppUrl(), + { + [SCREENSHOTTING_EXPRESSION]: expression, + [SCREENSHOTTING_EXPRESSION_INPUT]: input, + }, + ] as UrlOrUrlWithContext, + ] + : options.urls; + + return defaultsDeep( + { + ...options, + headers, + urls, + }, + { + timeouts: { + openUrl: 60000, + waitForElements: 60000, + renderComplete: 120000, + }, + urls: [], + } + ); + } + + systemHasInsufficientMemory(): boolean { + return systemHasInsufficientMemory(this.cloud, this.logger.get('cloud')); + } + + getScreenshots(options: PngScreenshotOptions): Observable; + getScreenshots(options: PdfScreenshotOptions): Observable; + getScreenshots(options: ScreenshotOptions): Observable; + getScreenshots(options: ScreenshotOptions): Observable { + if (this.systemHasInsufficientMemory()) { + return throwError(() => new errors.InsufficientMemoryAvailableOnCloudError()); + } + + const eventLogger = new EventLogger(this.logger, this.config); + const transactionEnd = eventLogger.startTransaction(Transactions.SCREENSHOTTING); + + const layout = createLayout(options.layout ?? {}); + const captureOptions = this.getCaptureOptions(options); + + return this.captureScreenshots(eventLogger, layout, captureOptions).pipe( + tap(({ results, metrics }) => { + transactionEnd({ + labels: { + cpu: metrics?.cpu, + memory: metrics?.memory, + memory_mb: metrics?.memoryInMegabytes, + ...eventLogger.getByteLengthFromCaptureResults(results), + }, + }); + }), + mergeMap((result) => { + switch (options.format) { + case 'pdf': + return toPdf(eventLogger, this.packageInfo, layout, options, result); + default: + return toPng(result); + } + }) + ); + } +} From 140a66947e013b3abd9eaeb1d0f2f17c880d6a51 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 21 Aug 2023 15:52:03 -0700 Subject: [PATCH 3/8] Add jest integration test and puppeteer mocks --- .../server/screenshots/screenshots.test.ts | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts diff --git a/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts b/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts new file mode 100644 index 0000000000000..11e919383243a --- /dev/null +++ b/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts @@ -0,0 +1,206 @@ +/* + * 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 { CloudSetup } from '@kbn/cloud-plugin/server'; +import { HttpServiceSetup } from '@kbn/core-http-server'; +import { PackageInfo } from '@kbn/core/server'; +import expect from '@kbn/expect'; +import type { Logger } from '@kbn/logging'; +import { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/server'; +import puppeteer from 'puppeteer'; +import * as Rx from 'rxjs'; +import { firstValueFrom } from 'rxjs'; +import { PngScreenshotOptions } from '..'; +import { HeadlessChromiumDriverFactory } from '../browsers'; +import { ConfigType } from '../config'; +import { Screenshots } from './screenshots'; + +jest.mock('puppeteer', () => { + const stubDevTools = { + send: jest.fn(), + }; + const stubTarget = { + createCDPSession: () => { + return stubDevTools; + }, + }; + const stubPage = { + target: () => { + return stubTarget; + }, + emulateTimezone: jest.fn(), + setDefaultTimeout: jest.fn(), + isClosed: jest.fn(), + setViewport: jest.fn(), + evaluate: jest.fn(), + screenshot: jest.fn().mockResolvedValue(`you won't believe this one weird screenshot`), + evaluateOnNewDocument: jest.fn(), + setRequestInterception: jest.fn(), + _client: jest.fn(() => ({ on: jest.fn() })), + on: jest.fn(), + goto: jest.fn(), + waitForSelector: jest.fn().mockResolvedValue(true), + waitForFunction: jest.fn(), + }; + const stubBrowser = { + newPage: () => { + return stubPage; + }, + }; + return { + launch: () => { + return stubBrowser; + }, + }; +}); + +describe('class Screenshots', () => { + let mockConfig: ConfigType; + let browserDriverFactory: HeadlessChromiumDriverFactory; + let mockPackageInfo: PackageInfo; + let mockHttpSetup: HttpServiceSetup; + let mockCloudSetup: CloudSetup; + let mockLogger: Logger; + let mockScreenshotModeSetup: ScreenshotModePluginSetup; + + const mockBinaryPath = '/kibana/x-pack/plugins/screenshotting/chromium/linux/headless_shell'; + const mockBasePath = '/kibanaTest1'; + + beforeEach(() => { + mockLogger = { debug: jest.fn(), error: jest.fn(), info: jest.fn() } as unknown as Logger; + mockLogger.get = () => mockLogger; + + mockConfig = { + networkPolicy: { + enabled: false, + rules: [], + }, + browser: { + autoDownload: false, + chromium: { proxy: { enabled: false } }, + }, + capture: { + timeouts: { + openUrl: 60000, + waitForElements: 60000, + renderComplete: 60000, + }, + zoom: 2, + }, + poolSize: 1, + }; + + mockScreenshotModeSetup = { + setScreenshotContext: jest.fn(), + setScreenshotModeEnabled: jest.fn(), + isScreenshotMode: jest.fn(), + }; + + browserDriverFactory = new HeadlessChromiumDriverFactory( + mockScreenshotModeSetup, + mockConfig, + mockLogger, + mockBinaryPath, + mockBasePath + ); + + mockCloudSetup = { isCloudEnabled: true, instanceSizeMb: 8000 } as unknown as CloudSetup; + }); + + const getScreenshotsInstance = () => + new Screenshots( + browserDriverFactory, + mockLogger, + mockPackageInfo, + mockHttpSetup, + mockConfig, + mockCloudSetup + ); + + it('detects sufficient memory from cloud plugin', () => { + const screenshotsInstance = getScreenshotsInstance(); + const hasInsufficient = screenshotsInstance.systemHasInsufficientMemory(); + expect(hasInsufficient).to.be(false); + }); + + it('detects insufficient memory from cloud plugin', () => { + mockCloudSetup = { isCloudEnabled: true, instanceSizeMb: 1000 } as unknown as CloudSetup; + const screenshotsInstance = getScreenshotsInstance(); + const hasInsufficient = screenshotsInstance.systemHasInsufficientMemory(); + expect(hasInsufficient).to.be(true); + }); + + it('ignores insufficient memory if cloud is not enabled', () => { + mockCloudSetup = { isCloudEnabled: false, instanceSizeMb: 1000 } as unknown as CloudSetup; + const screenshotsInstance = getScreenshotsInstance(); + const hasInsufficient = screenshotsInstance.systemHasInsufficientMemory(); + expect(hasInsufficient).to.be(false); + }); + + describe('getScreenshots', () => { + beforeAll(() => { + jest.mock('puppeteer'); // see __mocks__/puppeteer.ts + }); + + beforeEach(() => { + browserDriverFactory.getBrowserLogger = jest.fn(() => Rx.of()); + browserDriverFactory.getProcessLogger = jest.fn(() => Rx.of()); + browserDriverFactory.getPageExit = jest.fn(() => Rx.of()); + }); + + it('getScreenshots with PngScreenshotOptions', async () => { + const screenshotsInstance = getScreenshotsInstance(); + + const options: PngScreenshotOptions = { + format: 'png', + layout: { id: 'preserve_layout' }, + urls: ['/app/home/test'], + }; + + const observe = screenshotsInstance.getScreenshots(options); + await firstValueFrom(observe).then((captureResult) => { + expect(captureResult.results[0].screenshots[0].data).to.eql( + Buffer.from(`you won't believe this one weird screenshot`, 'base64') + ); + expect(captureResult.results[0].renderErrors).to.be(undefined); + expect(captureResult.results[0].error).to.be(undefined); + }); + }); + + it('adds warning to the screenshot in case of openUrl timeout', async () => { + // @ts-expect-error should not assign new value to read-only property + mockConfig.capture.timeouts.openUrl = 10; // must be a small amount of milliseconds + + // mock override + const browser = await puppeteer.launch(); + const page = await browser.newPage(); // should be stubPage + const pageGotoSpy = jest.spyOn(page, 'goto'); + pageGotoSpy.mockImplementation( + () => + new Promise((resolve) => { + setTimeout(resolve, 100); // must be larger than 10 + }) + ); + + const screenshotsInstance = getScreenshotsInstance(); + + const options: PngScreenshotOptions = { + format: 'png', + layout: { id: 'preserve_layout' }, + urls: ['/app/home/test'], + }; + + const observe = screenshotsInstance.getScreenshots(options); + await firstValueFrom(observe).then((captureResult) => { + expect(captureResult.results[0].error?.message).to.be( + `Screenshotting encountered a timeout error: "open URL" took longer than 0.01 seconds. You may need` + + ` to increase "xpack.screenshotting.capture.timeouts.openUrl" in kibana.yml.` + ); + }); + }); + }); +}); From ee9a2ad97c1c0615ce101aa20f49c34fbbeb18d3 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 21 Aug 2023 16:56:50 -0700 Subject: [PATCH 4/8] Unit test for chromium driver screenshot method --- .../server/browsers/chromium/driver.test.ts | 107 ++++++++++++++++++ .../server/browsers/chromium/driver.ts | 7 +- .../server/screenshots/screenshots.test.ts | 21 ++-- 3 files changed, 122 insertions(+), 13 deletions(-) create mode 100644 x-pack/plugins/screenshotting/server/browsers/chromium/driver.test.ts diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.test.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.test.ts new file mode 100644 index 0000000000000..fb07eac64ed17 --- /dev/null +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.test.ts @@ -0,0 +1,107 @@ +/* + * 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 type { Logger } from '@kbn/logging'; +import { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/server'; +import * as puppeteer from 'puppeteer'; +import { Size } from '../../../common/layout'; +import { ConfigType } from '../../config'; +import { PreserveLayout } from '../../layouts/preserve_layout'; +import { HeadlessChromiumDriver } from './driver'; + +describe('chromium driver', () => { + let mockConfig: ConfigType; + let mockLogger: Logger; + let mockScreenshotModeSetup: ScreenshotModePluginSetup; + let mockPage: puppeteer.Page; + + const mockBasePath = '/kibanaTest1'; + + beforeEach(() => { + mockLogger = { debug: jest.fn(), error: jest.fn(), info: jest.fn() } as unknown as Logger; + mockLogger.get = () => mockLogger; + + mockConfig = { + networkPolicy: { + enabled: false, + rules: [], + }, + browser: { + autoDownload: false, + chromium: { proxy: { enabled: false } }, + }, + capture: { + timeouts: { + openUrl: 60000, + waitForElements: 60000, + renderComplete: 60000, + }, + zoom: 2, + }, + poolSize: 1, + }; + + mockPage = { + screenshot: jest.fn().mockResolvedValue(`you won't believe this one weird screenshot`), + evaluate: jest.fn(), + } as unknown as puppeteer.Page; + + mockScreenshotModeSetup = { + setScreenshotContext: jest.fn(), + setScreenshotModeEnabled: jest.fn(), + isScreenshotMode: jest.fn(), + }; + }); + + it('return screenshot with preserve layout option', async () => { + const driver = new HeadlessChromiumDriver( + mockScreenshotModeSetup, + mockConfig, + mockBasePath, + mockPage + ); + + const result = await driver.screenshot({ + elementPosition: { + boundingClientRect: { top: 200, left: 10, height: 10, width: 100 }, + scroll: { x: 100, y: 300 }, + }, + layout: new PreserveLayout({ width: 16, height: 16 }), + }); + + expect(result).toEqual(Buffer.from(`you won't believe this one weird screenshot`, 'base64')); + }); + + it('add error to screenshot contents', async () => { + const driver = new HeadlessChromiumDriver( + mockScreenshotModeSetup, + mockConfig, + mockBasePath, + mockPage + ); + + // @ts-expect-error spy on non-public class method + const testSpy = jest.spyOn(driver, 'injectScreenshottingErrorHeader'); + + const result = await driver.screenshot({ + elementPosition: { + boundingClientRect: { top: 200, left: 10, height: 10, width: 100 }, + scroll: { x: 100, y: 300 }, + }, + layout: new PreserveLayout({} as Size), + error: new Error(`Here's the fake error!`), + }); + + expect(testSpy.mock.lastCall).toMatchInlineSnapshot(` + Array [ + [Error: Here's the fake error!], + "[data-shared-items-container]", + ] + `); + expect(result).toEqual(Buffer.from(`you won't believe this one weird screenshot`, 'base64')); + }); +}); diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts index 5727a854ac3ca..ccc54e2f1629f 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts @@ -96,9 +96,9 @@ export class HeadlessChromiumDriver { readonly screenshottingError$ = this.screenshottingErrorSubject.asObservable(); constructor( - private screenshotMode: ScreenshotModePluginSetup, - private config: ConfigType, - private basePath: string, + private readonly screenshotMode: ScreenshotModePluginSetup, + private readonly config: ConfigType, + private readonly basePath: string, private readonly page: Page ) {} @@ -199,6 +199,7 @@ export class HeadlessChromiumDriver { * to issues. Ideally, timeout errors wouldn't occur because ES would return pre-loaded results data * statically. */ + // FIXME test this private async injectScreenshottingErrorHeader(error: Error, containerSelector: string) { await this.page.evaluate( (selector: string, text: string) => { diff --git a/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts b/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts index 11e919383243a..86c33c0b5816a 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts @@ -8,7 +8,6 @@ import { CloudSetup } from '@kbn/cloud-plugin/server'; import { HttpServiceSetup } from '@kbn/core-http-server'; import { PackageInfo } from '@kbn/core/server'; -import expect from '@kbn/expect'; import type { Logger } from '@kbn/logging'; import { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/server'; import puppeteer from 'puppeteer'; @@ -124,21 +123,21 @@ describe('class Screenshots', () => { it('detects sufficient memory from cloud plugin', () => { const screenshotsInstance = getScreenshotsInstance(); const hasInsufficient = screenshotsInstance.systemHasInsufficientMemory(); - expect(hasInsufficient).to.be(false); + expect(hasInsufficient).toBe(false); }); it('detects insufficient memory from cloud plugin', () => { mockCloudSetup = { isCloudEnabled: true, instanceSizeMb: 1000 } as unknown as CloudSetup; const screenshotsInstance = getScreenshotsInstance(); const hasInsufficient = screenshotsInstance.systemHasInsufficientMemory(); - expect(hasInsufficient).to.be(true); + expect(hasInsufficient).toBe(true); }); it('ignores insufficient memory if cloud is not enabled', () => { mockCloudSetup = { isCloudEnabled: false, instanceSizeMb: 1000 } as unknown as CloudSetup; const screenshotsInstance = getScreenshotsInstance(); const hasInsufficient = screenshotsInstance.systemHasInsufficientMemory(); - expect(hasInsufficient).to.be(false); + expect(hasInsufficient).toBe(false); }); describe('getScreenshots', () => { @@ -163,11 +162,11 @@ describe('class Screenshots', () => { const observe = screenshotsInstance.getScreenshots(options); await firstValueFrom(observe).then((captureResult) => { - expect(captureResult.results[0].screenshots[0].data).to.eql( + expect(captureResult.results[0].screenshots[0].data).toEqual( Buffer.from(`you won't believe this one weird screenshot`, 'base64') ); - expect(captureResult.results[0].renderErrors).to.be(undefined); - expect(captureResult.results[0].error).to.be(undefined); + expect(captureResult.results[0].renderErrors).toBe(undefined); + expect(captureResult.results[0].error).toBe(undefined); }); }); @@ -196,9 +195,11 @@ describe('class Screenshots', () => { const observe = screenshotsInstance.getScreenshots(options); await firstValueFrom(observe).then((captureResult) => { - expect(captureResult.results[0].error?.message).to.be( - `Screenshotting encountered a timeout error: "open URL" took longer than 0.01 seconds. You may need` + - ` to increase "xpack.screenshotting.capture.timeouts.openUrl" in kibana.yml.` + expect(captureResult.results[0].error).toEqual( + new Error( + `Screenshotting encountered a timeout error: "open URL" took longer than 0.01 seconds.` + + ` You may need to increase "xpack.screenshotting.capture.timeouts.openUrl" in kibana.yml.` + ) ); }); }); From ef5dcf123723db5e42e51d1e42cb10218ace355a Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 21 Aug 2023 17:14:55 -0700 Subject: [PATCH 5/8] clean up tests --- .../server/__mocks__/puppeteer.ts | 47 +++++++++++++ .../chromium/driver_factory/index.test.ts | 36 +++++----- .../server/screenshots/screenshots.test.ts | 67 ++++--------------- 3 files changed, 75 insertions(+), 75 deletions(-) create mode 100644 x-pack/plugins/screenshotting/server/__mocks__/puppeteer.ts diff --git a/x-pack/plugins/screenshotting/server/__mocks__/puppeteer.ts b/x-pack/plugins/screenshotting/server/__mocks__/puppeteer.ts new file mode 100644 index 0000000000000..f295192723c2b --- /dev/null +++ b/x-pack/plugins/screenshotting/server/__mocks__/puppeteer.ts @@ -0,0 +1,47 @@ +/* + * 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. + */ + +const stubDevTools = { + send: jest.fn(), +}; +const stubTarget = { + createCDPSession: jest.fn(() => { + return stubDevTools; + }), +}; +const stubPage = { + target: jest.fn(() => { + return stubTarget; + }), + emulateTimezone: jest.fn(), + setDefaultTimeout: jest.fn(), + isClosed: jest.fn(), + setViewport: jest.fn(), + evaluate: jest.fn(), + screenshot: jest.fn().mockResolvedValue(`you won't believe this one weird screenshot`), + evaluateOnNewDocument: jest.fn(), + setRequestInterception: jest.fn(), + _client: jest.fn(() => ({ on: jest.fn() })), + on: jest.fn(), + goto: jest.fn(), + waitForSelector: jest.fn().mockResolvedValue(true), + waitForFunction: jest.fn(), +}; +const stubBrowser = { + newPage: jest.fn(() => { + return stubPage; + }), +}; + +const puppeteer = { + launch: jest.fn(() => { + return stubBrowser; + }), +}; + +// eslint-disable-next-line import/no-default-export +export default puppeteer; diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts index 325a734edd741..c3e6f75d6d511 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver_factory/index.test.ts @@ -6,6 +6,7 @@ */ import type { Logger } from '@kbn/core/server'; +import { loggerMock } from '@kbn/logging-mocks'; import type { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/server'; import * as puppeteer from 'puppeteer'; import * as Rx from 'rxjs'; @@ -24,22 +25,18 @@ describe('HeadlessChromiumDriverFactory', () => { }, }, } as ConfigType; - let logger: jest.Mocked; - let screenshotMode: jest.Mocked; + let logger: Logger; + let screenshotMode: ScreenshotModePluginSetup; let factory: HeadlessChromiumDriverFactory; - let mockBrowser: jest.Mocked; + let mockBrowser: puppeteer.Browser; beforeEach(async () => { - logger = { - debug: jest.fn(), - error: jest.fn(), - info: jest.fn(), - warn: jest.fn(), - get: jest.fn(() => logger), - } as unknown as typeof logger; - screenshotMode = {} as unknown as typeof screenshotMode; + logger = loggerMock.create(); + + screenshotMode = {} as unknown as ScreenshotModePluginSetup; let pageClosed = false; + mockBrowser = { newPage: jest.fn().mockResolvedValue({ target: jest.fn(() => ({ @@ -57,9 +54,8 @@ describe('HeadlessChromiumDriverFactory', () => { pageClosed = true; }), process: jest.fn(), - } as unknown as jest.Mocked; - - (puppeteer as jest.Mocked).launch.mockResolvedValue(mockBrowser); + } as unknown as puppeteer.Browser; + jest.spyOn(puppeteer, 'launch').mockResolvedValue(mockBrowser); factory = new HeadlessChromiumDriverFactory(screenshotMode, config, logger, path, ''); jest.spyOn(factory, 'getBrowserLogger').mockReturnValue(Rx.EMPTY); @@ -84,9 +80,8 @@ describe('HeadlessChromiumDriverFactory', () => { }); it('rejects if Puppeteer launch fails', async () => { - (puppeteer as jest.Mocked).launch.mockRejectedValue( - `Puppeteer Launch mock fail.` - ); + jest.spyOn(puppeteer, 'launch').mockRejectedValue(`Puppeteer Launch mock fail.`); + expect(() => factory .createPage({ openUrlTimeout: 0, defaultViewport: DEFAULT_VIEWPORT }) @@ -99,9 +94,8 @@ describe('HeadlessChromiumDriverFactory', () => { describe('close behaviour', () => { it('does not allow close to be called on the browse more than once', async () => { - await factory - .createPage({ openUrlTimeout: 0, defaultViewport: DEFAULT_VIEWPORT }) - .pipe( + await Rx.firstValueFrom( + factory.createPage({ openUrlTimeout: 0, defaultViewport: DEFAULT_VIEWPORT }).pipe( take(1), mergeMap(async ({ close }) => { expect(mockBrowser.close).not.toHaveBeenCalled(); @@ -110,7 +104,7 @@ describe('HeadlessChromiumDriverFactory', () => { expect(mockBrowser.close).toHaveBeenCalledTimes(1); }) ) - .toPromise(); + ); // Check again, after the observable completes expect(mockBrowser.close).toHaveBeenCalledTimes(1); }); diff --git a/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts b/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts index 86c33c0b5816a..d5e8757803c81 100644 --- a/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts +++ b/x-pack/plugins/screenshotting/server/screenshots/screenshots.test.ts @@ -5,57 +5,21 @@ * 2.0. */ -import { CloudSetup } from '@kbn/cloud-plugin/server'; -import { HttpServiceSetup } from '@kbn/core-http-server'; -import { PackageInfo } from '@kbn/core/server'; +import type { CloudSetup } from '@kbn/cloud-plugin/server'; +import type { HttpServiceSetup } from '@kbn/core-http-server'; +import type { PackageInfo } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; -import { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/server'; +import { loggerMock } from '@kbn/logging-mocks'; +import type { ScreenshotModePluginSetup } from '@kbn/screenshot-mode-plugin/server'; import puppeteer from 'puppeteer'; import * as Rx from 'rxjs'; import { firstValueFrom } from 'rxjs'; -import { PngScreenshotOptions } from '..'; +import type { PngScreenshotOptions } from '..'; import { HeadlessChromiumDriverFactory } from '../browsers'; -import { ConfigType } from '../config'; +import type { ConfigType } from '../config'; import { Screenshots } from './screenshots'; -jest.mock('puppeteer', () => { - const stubDevTools = { - send: jest.fn(), - }; - const stubTarget = { - createCDPSession: () => { - return stubDevTools; - }, - }; - const stubPage = { - target: () => { - return stubTarget; - }, - emulateTimezone: jest.fn(), - setDefaultTimeout: jest.fn(), - isClosed: jest.fn(), - setViewport: jest.fn(), - evaluate: jest.fn(), - screenshot: jest.fn().mockResolvedValue(`you won't believe this one weird screenshot`), - evaluateOnNewDocument: jest.fn(), - setRequestInterception: jest.fn(), - _client: jest.fn(() => ({ on: jest.fn() })), - on: jest.fn(), - goto: jest.fn(), - waitForSelector: jest.fn().mockResolvedValue(true), - waitForFunction: jest.fn(), - }; - const stubBrowser = { - newPage: () => { - return stubPage; - }, - }; - return { - launch: () => { - return stubBrowser; - }, - }; -}); +jest.mock('puppeteer'); describe('class Screenshots', () => { let mockConfig: ConfigType; @@ -70,8 +34,7 @@ describe('class Screenshots', () => { const mockBasePath = '/kibanaTest1'; beforeEach(() => { - mockLogger = { debug: jest.fn(), error: jest.fn(), info: jest.fn() } as unknown as Logger; - mockLogger.get = () => mockLogger; + mockLogger = loggerMock.create(); mockConfig = { networkPolicy: { @@ -93,11 +56,7 @@ describe('class Screenshots', () => { poolSize: 1, }; - mockScreenshotModeSetup = { - setScreenshotContext: jest.fn(), - setScreenshotModeEnabled: jest.fn(), - isScreenshotMode: jest.fn(), - }; + mockScreenshotModeSetup = {} as unknown as ScreenshotModePluginSetup; browserDriverFactory = new HeadlessChromiumDriverFactory( mockScreenshotModeSetup, @@ -146,9 +105,9 @@ describe('class Screenshots', () => { }); beforeEach(() => { - browserDriverFactory.getBrowserLogger = jest.fn(() => Rx.of()); - browserDriverFactory.getProcessLogger = jest.fn(() => Rx.of()); - browserDriverFactory.getPageExit = jest.fn(() => Rx.of()); + jest.spyOn(browserDriverFactory, 'getBrowserLogger').mockReturnValue(Rx.EMPTY); + jest.spyOn(browserDriverFactory, 'getProcessLogger').mockReturnValue(Rx.EMPTY); + jest.spyOn(browserDriverFactory, 'getPageExit').mockReturnValue(Rx.EMPTY); }); it('getScreenshots with PngScreenshotOptions', async () => { From 60ececc159b7cc6d958c430d9763144aef959dc8 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 21 Aug 2023 17:16:53 -0700 Subject: [PATCH 6/8] minimal diff --- .../screenshotting/server/browsers/chromium/driver.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts index ccc54e2f1629f..5727a854ac3ca 100644 --- a/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts +++ b/x-pack/plugins/screenshotting/server/browsers/chromium/driver.ts @@ -96,9 +96,9 @@ export class HeadlessChromiumDriver { readonly screenshottingError$ = this.screenshottingErrorSubject.asObservable(); constructor( - private readonly screenshotMode: ScreenshotModePluginSetup, - private readonly config: ConfigType, - private readonly basePath: string, + private screenshotMode: ScreenshotModePluginSetup, + private config: ConfigType, + private basePath: string, private readonly page: Page ) {} @@ -199,7 +199,6 @@ export class HeadlessChromiumDriver { * to issues. Ideally, timeout errors wouldn't occur because ES would return pre-loaded results data * statically. */ - // FIXME test this private async injectScreenshottingErrorHeader(error: Error, containerSelector: string) { await this.page.evaluate( (selector: string, text: string) => { From 0331bbec1a2af0de19c3e30cbb44b6550588528e Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 22 Aug 2023 00:22:16 +0000 Subject: [PATCH 7/8] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/screenshotting/tsconfig.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/screenshotting/tsconfig.json b/x-pack/plugins/screenshotting/tsconfig.json index 9110ea16661c2..3749a60fc4fe4 100644 --- a/x-pack/plugins/screenshotting/tsconfig.json +++ b/x-pack/plugins/screenshotting/tsconfig.json @@ -23,6 +23,8 @@ "@kbn/utils", "@kbn/safer-lodash-set", "@kbn/core-logging-server-mocks", + "@kbn/logging-mocks", + "@kbn/core-http-server", ], "exclude": [ "target/**/*", From ea9c5900a06a248e809b6d6eedc89359d3c4750f Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Mon, 21 Aug 2023 22:18:14 -0700 Subject: [PATCH 8/8] remove reporting_and_timeout.config from ftr list --- .buildkite/ftr_configs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 616f32285c99a..b918d82f846b1 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -347,7 +347,6 @@ enabled: - x-pack/test/reporting_functional/reporting_and_deprecated_security.config.ts - x-pack/test/reporting_functional/reporting_and_security.config.ts - x-pack/test/reporting_functional/reporting_without_security.config.ts - - x-pack/test/reporting_functional/reporting_and_timeout.config.ts - x-pack/test/rule_registry/security_and_spaces/config_basic.ts - x-pack/test/rule_registry/security_and_spaces/config_trial.ts - x-pack/test/rule_registry/spaces_only/config_basic.ts