Skip to content

Commit

Permalink
[Reporting] Make usable default element positions (elastic#63191)
Browse files Browse the repository at this point in the history
* [Reporting] Make usable default element posistions

* revert unrelated changes

* fix ts

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
tsullivan and elasticmachine authored Apr 16, 2020
1 parent f4c81b4 commit 3d41ca6
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export abstract class Layout {

public abstract getPdfPageSize(pageSizeParams: PageSizeParams): string | Size;

public abstract getViewport(itemsCount: number): ViewZoomWidthHeight;
public abstract getViewport(itemsCount: number): ViewZoomWidthHeight | null;

public abstract getBrowserZoom(): number;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { LevelLogger } from '../../../../server/lib';
import { createMockBrowserDriverFactory, createMockLayoutInstance } from '../../../../test_helpers';
import { ConditionalHeaders, HeadlessChromiumDriver } from '../../../../types';
import { CaptureConfig } from '../../../../server/types';
import * as contexts from './constants';
import { screenshotsObservableFactory } from './observable';
import { ElementsPositionAndAttribute } from './types';

Expand Down Expand Up @@ -57,10 +58,30 @@ describe('Screenshot Observable Pipeline', () => {
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"elementsPositionAndAttributes": Array [
Object {
"attributes": Object {
"description": "Default ",
"title": "Default Mock Title",
},
"position": Object {
"boundingClientRect": Object {
"height": 600,
"left": 0,
"top": 0,
"width": 800,
},
"scroll": Object {
"x": 0,
"y": 0,
},
},
},
],
"error": undefined,
"screenshots": Array [
Object {
"base64EncodedData": "allyourBase64 of boundingClientRect,scroll",
"base64EncodedData": "allyourBase64",
"description": "Default ",
"title": "Default Mock Title",
},
Expand Down Expand Up @@ -95,6 +116,26 @@ describe('Screenshot Observable Pipeline', () => {
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"elementsPositionAndAttributes": Array [
Object {
"attributes": Object {
"description": "Default ",
"title": "Default Mock Title",
},
"position": Object {
"boundingClientRect": Object {
"height": 600,
"left": 0,
"top": 0,
"width": 800,
},
"scroll": Object {
"x": 0,
"y": 0,
},
},
},
],
"error": undefined,
"screenshots": Array [
Object {
Expand All @@ -106,6 +147,26 @@ describe('Screenshot Observable Pipeline', () => {
"timeRange": "Default GetTimeRange Result",
},
Object {
"elementsPositionAndAttributes": Array [
Object {
"attributes": Object {
"description": "Default ",
"title": "Default Mock Title",
},
"position": Object {
"boundingClientRect": Object {
"height": 600,
"left": 0,
"top": 0,
"width": 800,
},
"scroll": Object {
"x": 0,
"y": 0,
},
},
},
],
"error": undefined,
"screenshots": Array [
Object {
Expand Down Expand Up @@ -150,21 +211,55 @@ describe('Screenshot Observable Pipeline', () => {
await expect(getScreenshot()).resolves.toMatchInlineSnapshot(`
Array [
Object {
"elementsPositionAndAttributes": Array [
Object {
"attributes": Object {},
"position": Object {
"boundingClientRect": Object {
"height": 200,
"left": 0,
"top": 0,
"width": 200,
},
"scroll": Object {
"x": 0,
"y": 0,
},
},
},
],
"error": [Error: An error occurred when trying to read the page for visualization panel info. You may need to increase 'xpack.reporting.capture.timeouts.waitForElements'. Error: Mock error!],
"screenshots": Array [
Object {
"base64EncodedData": "allyourBase64 of boundingClientRect,scroll",
"base64EncodedData": "allyourBase64",
"description": undefined,
"title": undefined,
},
],
"timeRange": null,
},
Object {
"elementsPositionAndAttributes": Array [
Object {
"attributes": Object {},
"position": Object {
"boundingClientRect": Object {
"height": 200,
"left": 0,
"top": 0,
"width": 200,
},
"scroll": Object {
"x": 0,
"y": 0,
},
},
},
],
"error": [Error: An error occurred when trying to read the page for visualization panel info. You may need to increase 'xpack.reporting.capture.timeouts.waitForElements'. Error: Mock error!],
"screenshots": Array [
Object {
"base64EncodedData": "allyourBase64 of boundingClientRect,scroll",
"base64EncodedData": "allyourBase64",
"description": undefined,
"title": undefined,
},
Expand Down Expand Up @@ -208,10 +303,27 @@ describe('Screenshot Observable Pipeline', () => {
await expect(getScreenshot()).resolves.toMatchInlineSnapshot(`
Array [
Object {
"elementsPositionAndAttributes": Array [
Object {
"attributes": Object {},
"position": Object {
"boundingClientRect": Object {
"height": 200,
"left": 0,
"top": 0,
"width": 200,
},
"scroll": Object {
"x": 0,
"y": 0,
},
},
},
],
"error": "Instant timeout has fired!",
"screenshots": Array [
Object {
"base64EncodedData": "allyourBase64 of boundingClientRect,scroll",
"base64EncodedData": "allyourBase64",
"description": undefined,
"title": undefined,
},
Expand All @@ -221,5 +333,69 @@ describe('Screenshot Observable Pipeline', () => {
]
`);
});

it(`uses defaults for element positions and size when Kibana page is not ready`, async () => {
// mocks
const mockBrowserEvaluate = jest.fn();
mockBrowserEvaluate.mockImplementation(() => {
const lastCallIndex = mockBrowserEvaluate.mock.calls.length - 1;
const { context: mockCall } = mockBrowserEvaluate.mock.calls[lastCallIndex][1];

if (mockCall === contexts.CONTEXT_ELEMENTATTRIBUTES) {
return Promise.resolve(null);
} else {
return Promise.resolve();
}
});
mockBrowserDriverFactory = await createMockBrowserDriverFactory(logger, {
evaluate: mockBrowserEvaluate,
});
mockLayout.getViewport = () => null;

// test
const getScreenshots$ = screenshotsObservableFactory(mockConfig, mockBrowserDriverFactory);
const getScreenshot = async () => {
return await getScreenshots$({
logger,
urls: ['/welcome/home/start/index.php3?page=./home.php3'],
conditionalHeaders: {} as ConditionalHeaders,
layout: mockLayout,
browserTimezone: 'UTC',
}).toPromise();
};

await expect(getScreenshot()).resolves.toMatchInlineSnapshot(`
Array [
Object {
"elementsPositionAndAttributes": Array [
Object {
"attributes": Object {},
"position": Object {
"boundingClientRect": Object {
"height": 1200,
"left": 0,
"top": 0,
"width": 1800,
},
"scroll": Object {
"x": 0,
"y": 0,
},
},
},
],
"error": undefined,
"screenshots": Array [
Object {
"base64EncodedData": "allyourBase64",
"description": undefined,
"title": undefined,
},
],
"timeRange": undefined,
},
]
`);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import { ScreenSetupData, ScreenshotObservableOpts, ScreenshotResults } from './
import { waitForRenderComplete } from './wait_for_render';
import { waitForVisualizations } from './wait_for_visualizations';

const DEFAULT_SCREENSHOT_CLIP_HEIGHT = 1200;
const DEFAULT_SCREENSHOT_CLIP_WIDTH = 1800;

export function screenshotsObservableFactory(
captureConfig: CaptureConfig,
browserDriverFactory: HeadlessChromiumDriverFactory
Expand All @@ -42,7 +45,7 @@ export function screenshotsObservableFactory(
mergeMap(() => openUrl(captureConfig, driver, url, conditionalHeaders, logger)),
mergeMap(() => getNumberOfItems(captureConfig, driver, layout, logger)),
mergeMap(async itemsCount => {
const viewport = layout.getViewport(itemsCount);
const viewport = layout.getViewport(itemsCount) || getDefaultViewPort();
await Promise.all([
driver.setViewport(viewport, logger),
waitForVisualizations(captureConfig, driver, itemsCount, layout, logger),
Expand Down Expand Up @@ -83,7 +86,12 @@ export function screenshotsObservableFactory(
: getDefaultElementPosition(layout.getViewport(1));
const screenshots = await getScreenshots(driver, elements, logger);
const { timeRange, error: setupError } = data;
return { timeRange, screenshots, error: setupError };
return {
timeRange,
screenshots,
error: setupError,
elementsPositionAndAttributes: elements,
};
}
)
);
Expand All @@ -97,17 +105,30 @@ export function screenshotsObservableFactory(
};
}

/*
* If Kibana is showing a non-HTML error message, the viewport might not be
* provided by the browser.
*/
const getDefaultViewPort = () => ({
height: DEFAULT_SCREENSHOT_CLIP_HEIGHT,
width: DEFAULT_SCREENSHOT_CLIP_WIDTH,
zoom: 1,
});
/*
* If an error happens setting up the page, we don't know if there actually
* are any visualizations showing. These defaults should help capture the page
* enough for the user to see the error themselves
*/
const getDefaultElementPosition = ({ height, width }: { height: number; width: number }) => [
{
const getDefaultElementPosition = (dimensions: { height?: number; width?: number } | null) => {
const height = dimensions?.height || DEFAULT_SCREENSHOT_CLIP_HEIGHT;
const width = dimensions?.width || DEFAULT_SCREENSHOT_CLIP_WIDTH;

const defaultObject = {
position: {
boundingClientRect: { top: 0, left: 0, height, width },
scroll: { x: 0, y: 0 },
},
attributes: {},
},
];
};
return [defaultObject];
};
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,5 @@ export interface ScreenshotResults {
timeRange: TimeRange | null;
screenshots: Screenshot[];
error?: Error;
elementsPositionAndAttributes?: ElementsPositionAndAttribute[]; // NOTE: for testing
}
Original file line number Diff line number Diff line change
Expand Up @@ -190,19 +190,14 @@ export class HeadlessChromiumDriver {
}

public async screenshot(elementPosition: ElementPosition): Promise<string> {
let clip;
if (elementPosition) {
const { boundingClientRect, scroll = { x: 0, y: 0 } } = elementPosition;
clip = {
const { boundingClientRect, scroll } = elementPosition;
const screenshot = await this.page.screenshot({
clip: {
x: boundingClientRect.left + scroll.x,
y: boundingClientRect.top + scroll.y,
height: boundingClientRect.height,
width: boundingClientRect.width,
};
}

const screenshot = await this.page.screenshot({
clip,
},
});

return screenshot.toString('base64');
Expand Down
Loading

0 comments on commit 3d41ca6

Please sign in to comment.