[Reporting] Fix TypeError with pending job info (#43924) (#46179)

* fix typeerror and typescript warnings

* add test for report listing

* get a relevant snapshot to test with

* fix debug diff

* remove unnecessary change
This commit is contained in:
Tim Sullivan 2019-09-19 14:49:22 -07:00 committed by GitHub
parent 2c0d3869f7
commit 9ee6f280f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 715 additions and 19 deletions

View file

@ -0,0 +1,609 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ReportListing Report job listing with some items 1`] = `
Array [
<EuiBasicTable
columns={
Array [
Object {
"field": "object_title",
"name": "Report",
"render": [Function],
},
Object {
"field": "created_at",
"name": "Created at",
"render": [Function],
},
Object {
"field": "status",
"name": "Status",
"render": [Function],
},
Object {
"actions": Array [
Object {
"render": [Function],
},
],
"name": "Actions",
},
]
}
data-test-subj="reportJobListing"
itemId="id"
items={Array []}
loading={true}
noItemsMessage="Loading reports"
onChange={[Function]}
pagination={
Object {
"hidePerPageOptions": true,
"pageIndex": 0,
"pageSize": 10,
"totalItemCount": 0,
}
}
responsive={true}
>
<div
className="euiBasicTable euiBasicTable-loading"
data-test-subj="reportJobListing"
>
<div>
<EuiTableHeaderMobile>
<div
className="euiTableHeaderMobile"
>
<EuiFlexGroup
alignItems="baseline"
justifyContent="spaceBetween"
responsive={false}
>
<div
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsBaseline euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow"
>
<EuiFlexItem
grow={false}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
/>
</EuiFlexItem>
<EuiFlexItem
grow={false}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
/>
</EuiFlexItem>
</div>
</EuiFlexGroup>
</div>
</EuiTableHeaderMobile>
<EuiTable
responsive={true}
>
<table
className="euiTable euiTable--responsive"
>
<EuiScreenReaderOnly>
<caption
aria-live="polite"
aria-relevant="text"
className="euiScreenReaderOnly"
role="status"
>
<EuiDelayRender
delay={500}
/>
</caption>
</EuiScreenReaderOnly>
<EuiTableHeader>
<thead>
<tr>
<EuiTableHeaderCell
align="left"
data-test-subj="tableHeaderCell_object_title_0"
key="_data_h_object_title_0"
mobileOptions={
Object {
"show": true,
}
}
scope="col"
>
<th
className="euiTableHeaderCell"
data-test-subj="tableHeaderCell_object_title_0"
role="columnheader"
scope="col"
>
<div
className="euiTableCellContent"
>
<span
className="euiTableCellContent__text"
>
Report
</span>
</div>
</th>
</EuiTableHeaderCell>
<EuiTableHeaderCell
align="left"
data-test-subj="tableHeaderCell_created_at_1"
key="_data_h_created_at_1"
mobileOptions={
Object {
"show": true,
}
}
scope="col"
>
<th
className="euiTableHeaderCell"
data-test-subj="tableHeaderCell_created_at_1"
role="columnheader"
scope="col"
>
<div
className="euiTableCellContent"
>
<span
className="euiTableCellContent__text"
>
Created at
</span>
</div>
</th>
</EuiTableHeaderCell>
<EuiTableHeaderCell
align="left"
data-test-subj="tableHeaderCell_status_2"
key="_data_h_status_2"
mobileOptions={
Object {
"show": true,
}
}
scope="col"
>
<th
className="euiTableHeaderCell"
data-test-subj="tableHeaderCell_status_2"
role="columnheader"
scope="col"
>
<div
className="euiTableCellContent"
>
<span
className="euiTableCellContent__text"
>
Status
</span>
</div>
</th>
</EuiTableHeaderCell>
<EuiTableHeaderCell
align="right"
key="_actions_h_3"
mobileOptions={
Object {
"show": true,
}
}
scope="col"
>
<th
className="euiTableHeaderCell"
role="columnheader"
scope="col"
>
<div
className="euiTableCellContent euiTableCellContent--alignRight"
>
<span
className="euiTableCellContent__text"
>
Actions
</span>
</div>
</th>
</EuiTableHeaderCell>
</tr>
</thead>
</EuiTableHeader>
<EuiTableBody>
<tbody>
<EuiTableRow>
<tr
className="euiTableRow"
>
<EuiTableRowCell
align="center"
colSpan={4}
isMobileFullWidth={true}
mobileOptions={
Object {
"show": true,
}
}
textOnly={true}
>
<td
className="euiTableRowCell euiTableRowCell--isMobileFullWidth"
colSpan={4}
>
<div
className="euiTableCellContent euiTableCellContent--alignCenter"
>
<span
className="euiTableCellContent__text"
>
Loading reports
</span>
</div>
</td>
</EuiTableRowCell>
</tr>
</EuiTableRow>
</tbody>
</EuiTableBody>
</table>
</EuiTable>
</div>
<PaginationBar
onPageChange={[Function]}
onPageSizeChange={[Function]}
pagination={
Object {
"hidePerPageOptions": true,
"pageIndex": 0,
"pageSize": 10,
"totalItemCount": 0,
}
}
>
<div>
<EuiSpacer
size="m"
>
<div
className="euiSpacer euiSpacer--m"
/>
</EuiSpacer>
<EuiTablePagination
activePage={0}
hidePerPageOptions={true}
itemsPerPage={10}
itemsPerPageOptions={
Array [
10,
25,
50,
]
}
onChangeItemsPerPage={[Function]}
onChangePage={[Function]}
pageCount={0}
>
<EuiFlexGroup
alignItems="center"
justifyContent="spaceBetween"
responsive={false}
>
<div
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow"
>
<EuiFlexItem
grow={false}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
/>
</EuiFlexItem>
<EuiFlexItem
grow={false}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<EuiPagination
activePage={0}
onPageClick={[Function]}
pageCount={0}
>
<span />
</EuiPagination>
</div>
</EuiFlexItem>
</div>
</EuiFlexGroup>
</EuiTablePagination>
</div>
</PaginationBar>
</div>
</EuiBasicTable>,
<div
className="euiBasicTable euiBasicTable-loading"
data-test-subj="reportJobListing"
>
<div>
<EuiTableHeaderMobile>
<div
className="euiTableHeaderMobile"
>
<EuiFlexGroup
alignItems="baseline"
justifyContent="spaceBetween"
responsive={false}
>
<div
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsBaseline euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow"
>
<EuiFlexItem
grow={false}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
/>
</EuiFlexItem>
<EuiFlexItem
grow={false}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
/>
</EuiFlexItem>
</div>
</EuiFlexGroup>
</div>
</EuiTableHeaderMobile>
<EuiTable
responsive={true}
>
<table
className="euiTable euiTable--responsive"
>
<EuiScreenReaderOnly>
<caption
aria-live="polite"
aria-relevant="text"
className="euiScreenReaderOnly"
role="status"
>
<EuiDelayRender
delay={500}
/>
</caption>
</EuiScreenReaderOnly>
<EuiTableHeader>
<thead>
<tr>
<EuiTableHeaderCell
align="left"
data-test-subj="tableHeaderCell_object_title_0"
key="_data_h_object_title_0"
mobileOptions={
Object {
"show": true,
}
}
scope="col"
>
<th
className="euiTableHeaderCell"
data-test-subj="tableHeaderCell_object_title_0"
role="columnheader"
scope="col"
>
<div
className="euiTableCellContent"
>
<span
className="euiTableCellContent__text"
>
Report
</span>
</div>
</th>
</EuiTableHeaderCell>
<EuiTableHeaderCell
align="left"
data-test-subj="tableHeaderCell_created_at_1"
key="_data_h_created_at_1"
mobileOptions={
Object {
"show": true,
}
}
scope="col"
>
<th
className="euiTableHeaderCell"
data-test-subj="tableHeaderCell_created_at_1"
role="columnheader"
scope="col"
>
<div
className="euiTableCellContent"
>
<span
className="euiTableCellContent__text"
>
Created at
</span>
</div>
</th>
</EuiTableHeaderCell>
<EuiTableHeaderCell
align="left"
data-test-subj="tableHeaderCell_status_2"
key="_data_h_status_2"
mobileOptions={
Object {
"show": true,
}
}
scope="col"
>
<th
className="euiTableHeaderCell"
data-test-subj="tableHeaderCell_status_2"
role="columnheader"
scope="col"
>
<div
className="euiTableCellContent"
>
<span
className="euiTableCellContent__text"
>
Status
</span>
</div>
</th>
</EuiTableHeaderCell>
<EuiTableHeaderCell
align="right"
key="_actions_h_3"
mobileOptions={
Object {
"show": true,
}
}
scope="col"
>
<th
className="euiTableHeaderCell"
role="columnheader"
scope="col"
>
<div
className="euiTableCellContent euiTableCellContent--alignRight"
>
<span
className="euiTableCellContent__text"
>
Actions
</span>
</div>
</th>
</EuiTableHeaderCell>
</tr>
</thead>
</EuiTableHeader>
<EuiTableBody>
<tbody>
<EuiTableRow>
<tr
className="euiTableRow"
>
<EuiTableRowCell
align="center"
colSpan={4}
isMobileFullWidth={true}
mobileOptions={
Object {
"show": true,
}
}
textOnly={true}
>
<td
className="euiTableRowCell euiTableRowCell--isMobileFullWidth"
colSpan={4}
>
<div
className="euiTableCellContent euiTableCellContent--alignCenter"
>
<span
className="euiTableCellContent__text"
>
Loading reports
</span>
</div>
</td>
</EuiTableRowCell>
</tr>
</EuiTableRow>
</tbody>
</EuiTableBody>
</table>
</EuiTable>
</div>
<PaginationBar
onPageChange={[Function]}
onPageSizeChange={[Function]}
pagination={
Object {
"hidePerPageOptions": true,
"pageIndex": 0,
"pageSize": 10,
"totalItemCount": 0,
}
}
>
<div>
<EuiSpacer
size="m"
>
<div
className="euiSpacer euiSpacer--m"
/>
</EuiSpacer>
<EuiTablePagination
activePage={0}
hidePerPageOptions={true}
itemsPerPage={10}
itemsPerPageOptions={
Array [
10,
25,
50,
]
}
onChangeItemsPerPage={[Function]}
onChangePage={[Function]}
pageCount={0}
>
<EuiFlexGroup
alignItems="center"
justifyContent="spaceBetween"
responsive={false}
>
<div
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentSpaceBetween euiFlexGroup--directionRow"
>
<EuiFlexItem
grow={false}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
/>
</EuiFlexItem>
<EuiFlexItem
grow={false}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<EuiPagination
activePage={0}
onPageClick={[Function]}
pageCount={0}
>
<span />
</EuiPagination>
</div>
</EuiFlexItem>
</div>
</EuiFlexGroup>
</EuiTablePagination>
</div>
</PaginationBar>
</div>,
]
`;

View file

@ -4,5 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
export const mockJobQueueClient = { getInfo: jest.fn() };
export const mockJobQueueClient = { list: jest.fn(), total: jest.fn(), getInfo: jest.fn() };
jest.mock('../lib/job_queue_client', () => ({ jobQueueClient: mockJobQueueClient }));

View file

@ -35,15 +35,11 @@ interface State {
const NA = 'n/a';
const UNKNOWN = 'unknown';
const getDimensions = (info: JobInfo) => {
const getDimensions = (info: JobInfo): string => {
const defaultDimensions = { width: null, height: null };
const { width, height } = get(info, 'payload.layout.dimensions', defaultDimensions);
if (width && height) {
return (
<Fragment>
Width: {width} x Height: {height}
</Fragment>
);
return `Width: ${width} x Height: ${height}`;
}
return NA;
};
@ -77,8 +73,21 @@ export class ReportInfoButton extends Component<Props, State> {
const jobType = info.jobtype || NA;
// TODO queue method (clicked UI, watcher, etc)
const jobInfoParts = {
interface JobInfo {
title: string;
description: string;
}
interface JobInfoMap {
[thing: string]: JobInfo[];
}
const attempts = info.attempts ? info.attempts.toString() : NA;
const maxAttempts = info.max_attempts ? info.max_attempts.toString() : NA;
const priority = info.priority ? info.priority.toString() : NA;
const timeout = info.timeout ? info.timeout.toString() : NA;
const jobInfoParts: JobInfoMap = {
datetimes: [
{
title: 'Created By',
@ -105,21 +114,21 @@ export class ReportInfoButton extends Component<Props, State> {
},
{
title: 'Browser Timezone',
description: info.payload.browserTimezone || NA,
description: get(info, 'payload.browserTimezone') || NA,
},
],
payload: [
{
title: 'Title',
description: info.payload.title || NA,
description: get(info, 'payload.title') || NA,
},
{
title: 'Type',
description: info.payload.type || NA,
description: get(info, 'payload.type') || NA,
},
{
title: 'Layout',
description: info.meta.layout || NA,
description: get(info, 'meta.layout') || NA,
},
{
title: 'Dimensions',
@ -131,29 +140,29 @@ export class ReportInfoButton extends Component<Props, State> {
},
{
title: 'Content Type',
description: info.output.content_type || NA,
description: get(info, 'output.content_type') || NA,
},
{
title: 'Size in Bytes',
description: info.output.size || NA,
description: get(info, 'output.size') || NA,
},
],
status: [
{
title: 'Attempts',
description: info.attempts || NA,
description: attempts,
},
{
title: 'Max Attempts',
description: info.max_attempts || NA,
description: maxAttempts,
},
{
title: 'Priority',
description: info.priority || NA,
description: priority,
},
{
title: 'Timeout',
description: info.timeout || NA,
description: timeout,
},
{
title: 'Status',

View file

@ -0,0 +1,77 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
interface JobData {
_index: string;
_id: string;
_source: {
browser_type: string;
created_at: string;
jobtype: string;
created_by: string;
payload: {
type: string;
title: string;
};
kibana_name?: string; // undefined if job is pending (not yet claimed by an instance)
kibana_id?: string; // undefined if job is pending (not yet claimed by an instance)
output?: { content_type: string; size: number }; // undefined if job is incomplete
completed_at?: string; // undefined if job is incomplete
};
}
jest.mock('ui/chrome', () => ({
getInjected() {
return {
jobsRefresh: {
interval: 10,
intervalErrorMultiplier: 2,
},
};
},
}));
jest.mock('ui/kfetch', () => ({
kfetch: ({ pathname }: { pathname: string }): Promise<JobData[] | number> => {
if (pathname === '/api/reporting/jobs/list') {
return Promise.resolve([
{ _index: '.reporting-2019.08.18', _id: 'jzoik8dh1q2i89fb5f19znm6', _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1635, height: 792 } }, type: 'dashboard', title: 'Names', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:24.869Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore
{ _index: '.reporting-2019.08.18', _type: '_doc', _id: 'jzoik7tn1q2i89fb5f60e5ve', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1635, height: 792 } }, type: 'dashboard', title: 'Names', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:24.155Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore
{ _index: '.reporting-2019.08.18', _type: '_doc', _id: 'jzoik5tb1q2i89fb5fckchny', _score: null, _source: { payload: { layout: { id: 'png', dimensions: { width: 1898, height: 876 } }, title: 'cool dashboard', type: 'dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:21.551Z', jobtype: 'PNG', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore
{ _index: '.reporting-2019.08.18', _type: '_doc', _id: 'jzoik5a11q2i89fb5f130t2m', _score: null, _source: { payload: { layout: { id: 'png', dimensions: { width: 1898, height: 876 } }, title: 'cool dashboard', type: 'dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:20.857Z', jobtype: 'PNG', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore
{ _index: '.reporting-2019.08.18', _type: '_doc', _id: 'jzoik3ka1q2i89fb5fdx93g7', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1898, height: 876 } }, type: 'dashboard', title: 'cool dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:18.634Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore
{ _index: '.reporting-2019.08.18', _type: '_doc', _id: 'jzoik2vt1q2i89fb5ffw723n', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1898, height: 876 } }, type: 'dashboard', title: 'cool dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:17.753Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore
{ _index: '.reporting-2019.08.18', _type: '_doc', _id: 'jzoik1851q2i89fb5fdge6e7', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1080, height: 720 } }, type: 'canvas workpad', title: 'My Canvas Workpad - Dark', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:15.605Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore
{ _index: '.reporting-2019.08.18', _type: '_doc', _id: 'jzoijyre1q2i89fb5fa7xzvi', _score: null, _source: { payload: { type: 'dashboard', title: 'tests-panels', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:12.410Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore
{ _index: '.reporting-2019.08.18', _type: '_doc', _id: 'jzoijv5h1q2i89fb5ffklnhx', _score: null, _source: { payload: { type: 'dashboard', title: 'tests-panels', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:07.733Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore
{ _index: '.reporting-2019.08.18', _type: '_doc', _id: 'jznhgk7r1bx789fb5f6hxok7', _score: null, _source: { kibana_name: 'spicy.local', browser_type: 'chromium', created_at: '2019-08-23T02:15:47.799Z', jobtype: 'printable_pdf', created_by: 'elastic', kibana_id: 'ca75e26c-2b7d-464f-aef0-babb67c735a0', output: { content_type: 'application/pdf', size: 877114 }, completed_at: '2019-08-23T02:15:57.707Z', payload: { type: 'dashboard (legacy)', title: 'tests-panels', }, max_attempts: 3, started_at: '2019-08-23T02:15:48.794Z', attempts: 1, status: 'completed', }, }, // prettier-ignore
]);
}
// query for jobs count
return Promise.resolve(18);
},
}));
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { ReportListing } from './report_listing';
describe('ReportListing', () => {
it('Report job listing with some items', () => {
const wrapper = mountWithIntl(
<ReportListing
badLicenseMessage=""
showLinks={false}
enableLinks={false}
redirect={jest.fn()}
/>
);
wrapper.update();
const input = wrapper.find('[data-test-subj="reportJobListing"]');
expect(input).toMatchSnapshot();
});
});

View file

@ -315,6 +315,7 @@ class ReportListingUi extends Component<Props, State> {
}
pagination={pagination}
onChange={this.onTableChange}
data-test-subj="reportJobListing"
/>
);
}