Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [Reporting] Make usable default element positions (#63191) #63651

Merged
merged 1 commit into from
Apr 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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